为什么这个 C# COM 类可以在 VBScript 中使用,但不能在 JScript 中使用?

发布于 2024-09-19 00:13:07 字数 2089 浏览 6 评论 0原文

考虑 C# 中的自动化兼容 COM 库,如下所示。它遵循常见的 COM 模式,即具有可见工厂 coclass FooFactory 实现 ICreateFoos,它创建 IFoo 类型的对象。 FooFactory 是类型库中唯一的组件类。 (工厂模式对于 COM 特别有用,因为它不允许参数化构造函数)。

在下面的代码中,我发现我无法从jscript访问返回的IFoo接口除非我使FooImpl类ComVisible(通过取消注释注释行;这会导致它在类型库中显示为组件类)。 从 VBscript 访问它不存在这样的问题

也就是说,我可以运行这个 VBScript:

set ff = CreateObject("jstest.FooFactory")
set foo = ff.CreateFoo(0)
foo.Foo

但是这个功能相同的 JScript 失败,并出现错误“C:\temp\jstest\jstest.js(4, 1) Microsoft JScript 运行时错误:'foo'为 null 或不是对象”:

var ff = new ActiveXObject("jstest.FooFactory");
var foo = ff.CreateFoo(0)
//WScript.Stdout.WriteLine(null==foo)
foo.Foo();

如果我取消注释该行,我可以看到 null==foo 为 false。

为什么会发生这种情况?这是一个错误吗?请注意,我认为这是 JScript 和 C#/.net 特定实现(可能是 IDispatch)的组合的问题,因为我有其他类似的 COM 服务器(用 C++ 实现),它们在 JScript 中不会出现此问题。

如果我取消注释下面代码中的注释行,使 FooImpl 作为组件类可见,那么问题就会消失 - 但我特别不想这样做,因为我不想公开实现细节。一种解决方法似乎是使 FooImpl ComVisible,但将其构造函数标记为内部,这会阻止客户端能够 CoCreate 它,但这并不优雅。

我在 WinXP SP3 上运行 Visual Studio 2005、.net 2,并且能够在 VirtualBox 上全新安装的 TinyXP(均使用 Windows Script Host 5.7)以及 Windows 7 Ultimate 上重现该问题.net SDK 2.0、3.0、3.5 和 4.0 (WSH 5.8)。所有操作系统均为 32 位。

库代码:

using System;
using System.Runtime.InteropServices;

[assembly: ComVisible(false)]

namespace jstest
{
    [ComVisible(true)]
    public interface ICreateFoos
    {
        IFoo CreateFoo(int importantNumber);
    }

    [ComVisible(true)]
    public interface IFoo
    {
        void Foo();
    }

    [ComVisible(true)]
    public class FooFactory : ICreateFoos
    {
        public IFoo CreateFoo(int importantNumber)
        {   // in *this* version, we don't use importantNumber yet
            return new FooImpl();
        }
    }

    //[ComVisible(true)]
    public class FooImpl : IFoo
    {
        public void Foo()
        {
            Console.WriteLine("Foo");
        }
    }
}

您可以编译并注册(您可能必须以管理员身份运行才能重装)

csc /target:library jstest.cs
regasm /codebase jstest.dll

Consider the automation-compatible COM library in C#, given below. It follows a common COM pattern of having a visible factory coclass FooFactory implementing ICreateFoos which creates an object of type IFoo. FooFactory is the only coclass in the type library. (The factory pattern is particularly useful with COM, as it does not allow for parameterized constructors).

In the code below, I'm finding that I cannot access the returned IFoo interface from jscript unless I make the FooImpl class ComVisible (by uncommenting commented lines; this causes it to appear as a coclass in the type library). There is no such problem accessing this from VBscript.

That is, I can run this VBScript:

set ff = CreateObject("jstest.FooFactory")
set foo = ff.CreateFoo(0)
foo.Foo

But this functionally identical JScript fails, with the error "C:\temp\jstest\jstest.js(4, 1) Microsoft JScript runtime error: 'foo' is null or not an object":

var ff = new ActiveXObject("jstest.FooFactory");
var foo = ff.CreateFoo(0)
//WScript.Stdout.WriteLine(null==foo)
foo.Foo();

If I uncomment the line, I can see that null==foo is false.

Why does this happen? Is it a bug? Note that I think this is a problem is a combination of JScript and the C#/.net-specific implementation (possibly of IDispatch), because I have other similar COM servers - implemented in C++ - that do not exhibit this problem from JScript.

The problem goes away if I uncomment the commented lines in the code below, making FooImpl visible as a coclass - but I specifically do not want to do this as I don't want to expose implementation details. A workaround seems to be to make FooImpl ComVisible, but mark its constructor internal, which prevents clients from being able to CoCreate it, but that's hardly elegant.

I'm running on WinXP SP3 with Visual Studio 2005, .net 2, and have been able to reproduce the issue on a completely fresh install of TinyXP on a VirtualBox (both with Windows Script Host 5.7), and also on Windows 7 Ultimate using .net SDKs 2.0, 3.0, 3.5 and 4.0 (WSH 5.8). All OSes were 32-bit.

The library code:

using System;
using System.Runtime.InteropServices;

[assembly: ComVisible(false)]

namespace jstest
{
    [ComVisible(true)]
    public interface ICreateFoos
    {
        IFoo CreateFoo(int importantNumber);
    }

    [ComVisible(true)]
    public interface IFoo
    {
        void Foo();
    }

    [ComVisible(true)]
    public class FooFactory : ICreateFoos
    {
        public IFoo CreateFoo(int importantNumber)
        {   // in *this* version, we don't use importantNumber yet
            return new FooImpl();
        }
    }

    //[ComVisible(true)]
    public class FooImpl : IFoo
    {
        public void Foo()
        {
            Console.WriteLine("Foo");
        }
    }
}

You can compile and register (you may have to run as admin to regasm) this with

csc /target:library jstest.cs
regasm /codebase jstest.dll

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冷…雨湿花 2024-09-26 00:13:07

当针对从 CreateFoo 返回的 IDispatch GUID 的 IFoo 对象调用 QueryInterface 时,它​​返回 E_NOINTERFACE 除非设置了 ComVisible对于实际的实现类。

jscript准备调用Foo方法时,它会多次调用QueryInterface,包括使用这个特定的GUID,并且由于返回错误,因此它不会尝试使用调用

vbscript准备调用Foo方法时,它检查接口是否支持IDispatch。 QueryInterface 使用 IDispatchEx 的 GUID 调用一次,但似乎只是假设支持 IDispatch。

When QueryInterface is called against the IFoo object returned from CreateFoo for the IDispatch GUID it returns E_NOINTERFACE unless ComVisible is set for the actual implementing class.

When jscript prepares to call the Foo method it calls QueryInterface several times, including with this specific GUID, and since an error is returns it doesn't try to use Invoke.

When vbscript prepares to call the Foo method it does not check the interface supports IDispatch. QueryInterface is called, once, with the GUID for IDispatchEx but it seems to simply assume IDispatch will be supported.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文