考虑C#中与自动化兼容的COM库,如下所示。它遵循一个常见的COM模式,即有一个可见的工厂coclass FooFactory实现ICreateFoos,它创建一个IFoo类型的对象。 FooFactory是类型库中仅的 coclass。 (工厂模式对COM特别有用,因为它不允许参数化构造函数。)
在下面的代码中,我发现我无法从 jscript 访问返回的IFoo接口,除非我将FooImpl类设为ComVisible (通过取消注释注释行;这个使它在类型库中显示为coclass)。 从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中出现这个问题。 / p>
如果我取消注释下面代码中的注释行,使得FooImpl作为coclass可见,问题就消失了 - 但我特别不希望这样做,因为我不想暴露实现细节。一个解决方法似乎是使FooImpl ComVisible,但标记其构造函数内部,这阻止客户端能够CoCreate它,但这不是很优雅。
我正在使用Visual Studio 2005,.net 2在WinXP SP3上运行,并且能够在VirtualBox上完全全新安装TinyXP(都使用Windows Script Host 5.7)以及Windows上重现问题7终极使用.net SDKs 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");
}
}
}
您可以使用
编译和注册(您可能需要以管理员身份运行到regasm)csc /target:library jstest.cs
regasm /codebase jstest.dll
答案 0 :(得分:6)
当针对IDispatch GUID从IFoo
返回的CreateFoo
对象调用QueryInterface时,它返回E_NOINTERFACE
,除非为实际的实现类设置了 ComVisible。
当 jscript 准备调用Foo
方法时,它会多次调用QueryInterface,包括使用此特定GUID,并且由于返回错误,因此不会尝试使用{{1 }}
当 vbscript 准备调用Invoke
方法时,不检查界面是否支持Foo
。使用IDispatchEx的GUID调用QueryInterface一次,但它似乎只是假设支持IDispatch。