代码之家  ›  专栏  ›  技术社区  ›  Jeff Dege

为什么不通过interop类从.NET访问COM对象有时会起作用?

  •  11
  • Jeff Dege  · 技术社区  · 16 年前

    当您从.NET代码接口一个COM对象时,vs使用interop类创建一个interop dll。

    例子:

    您有一个foo.dll来实现一个com库foo,其中包括一个com接口“ibar”的实现。向.NET项目添加对foo.dll的引用。在/bin中,您将看到interop.foolib.dll。在对象浏览器中,您将看到interop.foolib,在它下面,您将看到foolib,在它下面,您将看到barclass,在它下面,您将看到基本类型,在那个条和ibar下面。

    在.NET代码中,当声明变量时,可以键入foolib,intellisense将为您提供bar或barclass()选项。

    根据我的理解,在变量声明中使用哪一个并不重要,但在其构造函数中使用哪一个却非常重要。

    也就是说,这两种方法都应该有效:

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

    但这不应该奏效:

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

    这就是问题所在。我们刚刚跟踪到一个奇怪的bug,其中一些客户的代码在开发和测试环境中工作,但没有在一个客户站点上工作,结果证明是使用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用户界面将其称为反汇编——如果它输出一个HLL,那么它就是一个反编译文件。

    3 回复  |  直到 16 年前
        1
  •  2
  •   Josh    16 年前

    好问题。很多人都很惊讶这一点,因为bar是一个接口,当然您不应该创建一个接口的新实例!但是,尽管我似乎找不到实现的任何细节,但我记得我在亚当·内森的《COM互操作手册》中读到,C对标记有coclassattribute的COM接口做了一个特殊的异常,并将调用转换为coclass的实例化。

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

        2
  •  0
  •   Scott P    16 年前

    原则上,bar()构造函数应该显式返回接口,而不是类对象。我不太明白.NET是如何支持接口的构造的!?

    在任何情况下,都可以单击bar()构造函数并单击shift-f12。这将显示代码中使用该构造函数的其他地方。我想不出一种方法来防止用户无意中调用这个构造函数。

        3
  •  0
  •   Community Mohan Dere    9 年前

    你把讨论读了吗 this question ?我认为它正是在讨论这个问题。