为什么从.NET访问COM对象而不通过Interop类,有时可以工作?

时间:2010-06-17 15:14:55

标签: .net com interop

当您从.NET代码接口COM对象时,VS会创建一个带互操作类的互操作DLL。

示例:

你有一个foo.dll实现了一个COM库Foo,它包含了一个COM接口“IBar”的实现。您将foo.dll的引用添加到.NET项目。在/ bin中,您将看到一个Interop.FooLib.dll。在对象浏览器中,您将看到Interop.FooLib,在其下您将看到FooLib,您将看到BarClass,您将在该Bar和IBar下看到Base Types。

在.NET代码中,声明变量时,可以键入FooLib,intellisense将为您提供Bar或BarClass()的选项。

根据我的理解,你在变量声明中使用它并不重要,但它对你的构造函数使用的非常重要。

也就是说,这两个都应该有效:

FooLib.BarClass theBar = new FooLib.BarClass();
FooLib.Bar theBar = new FooLib.BarClass();

但这不应该奏效:

FooLib.Bar theBar = new FooLib.Bar();

这是问题所在。我们刚刚查找了一个奇怪的错误,其中代码适用于某些客户,并且在我们的开发和测试环境中工作,但是在一个客户站点上工作,结果是使用Bar()构造函数的程序员。

那么,任何人都能解释两个构造函数Bar()和BarClass()之间的区别吗?

有人可以解释为什么Bar()构造函数似乎有效吗?

任何人都可以提供一种方法来确保没有人错误地调用错误的构造函数而不读取每行代码吗?

- 已添加 -

有人提出问题出在我们的COM实施中。这就是我们正在做的事情:

IDL:

[
    object,
    uuid(...),
    dual,
    helpstring("IBar Interface"),
    pointer_default(unique),
    nonextensible
]
interface IBar : IDispatch
{
    [id(1), helpstring("method barify")] 
        HRESULT barify([out, retval] VARIANT_BOOL *rVal);
    // ...
};
// ...
[
    uuid(...),
    version(1.0),
    helpstring("Foo 1.0 Type Library")
]
library FooLib
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");
    // ...
    [
        uuid(...),
        helpstring("Bar Class")
    ]
    coclass Bar
    {
        [default] interface IBar;
    };
    // ...
};

实施:

class ATL_NO_VTABLE CBar : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CBar, &CLSID_Bar>,
    public IDispatchImpl<IBar, &IID_IBar, &LIBID_FooLib>,
    public ISupportErrorInfoImpl <&IID_IBar>
{
public:
    CBar();

    DECLARE_REGISTRY_RESOURCEID(IDR_BAR)

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    BEGIN_COM_MAP(CBar)
        COM_INTERFACE_ENTRY(IBar)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(ISupportErrorInfo)
    END_COM_MAP()

    // ...
};

- 稍后添加 -

通过.NET Reflector反编译*:

[ComImport, CoClass(typeof(BarClass)), Guid("...")]
public interface Bar : IBar
{
}

*我不关心Reflector UI将其称为反汇编 - 如果它正在输出HLL,那么它就是反编译。

3 个答案:

答案 0 :(得分:2)

好问题。很多人都对此工作感到惊讶,因为Bar是一个界面,当然你不应该能够创建一个新的界面实例!但是,虽然我似乎无法找到实现的任何细节,但我记得在Adam Nathan的COM互操作性书中读到C#对标记有CoClassAttribute的COM接口进行特殊异常,并将调用转换为coclass的实例化。 / p>

但我不知道为什么它有时会起作用,有时也不起作用。

答案 1 :(得分:0)

原则上,Bar()构造函数应该显式返回接口而不是类对象。我无法弄清楚.NET如何支持接口的构建!?

在任何情况下,您都可以单击Bar()构造函数并按Shift-F12。这将显示代码中使用该构造函数的任何其他位置。我想不出有办法阻止用户无意中调用这个构造函数。

答案 2 :(得分:0)

您是否阅读了this question中的讨论?它确实讨论了我认为的这个问题。