C# 互操作(一) 編寫一個C++ COM組件


    C# 如何與C++ 編寫的COM 組件進行交互,首先編寫一個C++ ATL COM 組件,打開vs,新建一個ATL項目,將項目命名為COMServer,在Application Setting中,選擇Dynamic Link Library(動態鏈接庫)。創建項目后,右鍵點擊項目,添加-類,選擇ATL標簽,選擇ATL Simple Object (ATL 簡單對象),在對話框中簡稱填寫COMDemo,接口填寫IWelcome,ProgId填寫COMServer.COMDemo

image

然后在類視圖下,選擇接口IWelcome,並使用下面的參數添加方法Greeting

HRESULT Greeting( [in] BSTR name, [out,retval] BSTR* message);

image

IDL 文件COMServer.idl 定義了COM的接口,向導在COMServer.idl文件中生成的代碼應該與下面類似,唯一標識符(uuid)會不同。IWelcome接口定義了Greeting方法,關鍵字interface之前的中括號定義了該接口的一些特性。uuid定義額了接口ID,dual標記了接口類型。

[
    object,
    uuid(4704A15D-CDE1-4BF1-A28E-E1477E7C74B8),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface IWelcome : IDispatch
{
    [id(1)] HRESULT Greeting([in] BSTR name, [out, retval] BSTR* message);
};

IDL文件也定義了類型庫的內容,它是實現了IWelcome接口的COM對象(coclass)

[
    uuid(FBB80A75-06E1-452B-88BA-F7B00ED151A9),
    version(1.0),
]
library COMServerLib
{
    [
        uuid(85567ACE-7031-4246-9E81-CA5AA470F212),
    ]
    coclass COMDemo
    {
        [default] interface IWelcome;
    };
};

在IWelcome接口的頭部,使用上述標識符和名稱Learning.Interop.Server.IWelcome添加custom特性。在coclass COMDemo中使用對應的名稱天界類似的custom特性:

[
    object,
    uuid(4704A15D-CDE1-4BF1-A28E-E1477E7C74B8),
    dual,
    nonextensible,
    pointer_default(unique),
    custom(67412B27-B0FB-425A-A7D2-DAB66BD6768B, "Learning.Interop.Server.IWelcome")
]
interface IWelcome : IDispatch
{
    [id(1)] HRESULT Greeting([in] BSTR name, [out, retval] BSTR* message);
};

[
    uuid(FBB80A75-06E1-452B-88BA-F7B00ED151A9),
    version(1.0),
]
library COMServerLib
{
    [
        uuid(85567ACE-7031-4246-9E81-CA5AA470F212),
        helpstring("COMDemo Class"),
        custom(67412B27-B0FB-425A-A7D2-DAB66BD6768B, "Learning.Interop.Server.COMDemo")
    ]
    coclass COMDemo
    {
        [default] interface IWelcome;
    };
};

現在可以在COMServer.idl文件中再添加一個接口,可以把IWelcome的接口頭部直接復制到新建的IMath接口的頭部,但要記得修改uuid關鍵字定義的唯一標識符。

IMath接口提供兩個方法Add,Sub:

//IMath
[
    object,
    uuid(2158751B-B96E-461d-9012-EF1680BE0628),
    dual,
    nonextensible,
    pointer_default(unique),
    custom(67412B27-B0FB-425A-A7D2-DAB66BD6768B, "Learning.Interop.Server.IMath")
]
interface IMath : IDispatch
{
    
    [id(1)] HRESULT Add([in] LONG val1, [in] LONG val2, [out, retval] LONG* result);
    [id(2)] HRESULT Sub([in] LONG val1, [in] LONG val2, [out, retval] LONG* result);
};

還必須修改coclass COMDemo,使其同時實現接口IWelcome和IMath。IWelcome接口設為默認接口:

[
    uuid(85567ACE-7031-4246-9E81-CA5AA470F212),
    helpstring("COMDemo Class"),
    custom(67412B27-B0FB-425A-A7D2-DAB66BD6768B, "Learning.Interop.Server.COMDemo")
]
coclass COMDemo
{
    [default] interface IWelcome;
    interface IMath;
};

現在可以將注意力從IDL文件轉移到C++代碼。COMDemo.h文件包含了COM對象的定義。類COMDemo使用多繼承派生自模板類CComObjectRootEx,CComCoClass 和 IDispatchImpl.CComObjectRootEx 類實現了IUnknow接口的功能,例如AddRef和Release方法。CComCoClass類創建了一個工廠實例化模板參數對象,在這里是CComDemo。IDispatchImple實現了IDispathc接口的方法。

包含在BEGIN_COM_MAP 和 END_COM_MAP中的宏創建了一個映射,用於定義COM類實現的所有COM接口。QuerInterface方法的實現會使用此映射

class ATL_NO_VTABLE CCOMDemo :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CCOMDemo, &CLSID_COMDemo>,
    public IDispatchImpl<IWelcome, &IID_IWelcome, &LIBID_COMServerLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
{
public:
    CCOMDemo()
    {
    }

DECLARE_REGISTRY_RESOURCEID(IDR_COMDEMO1)


BEGIN_COM_MAP(CCOMDemo)
    COM_INTERFACE_ENTRY(IWelcome)
    COM_INTERFACE_ENTRY2(IDispatch,IWelcome)
END_COM_MAP()



    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

public:



    STDMETHOD(Greeting)(BSTR name, BSTR* message);
};

OBJECT_ENTRY_AUTO(__uuidof(COMDemo), CCOMDemo)

在這個類定義中,還需要添加第二接口IMath,以及IMath接口定義的方法:

class ATL_NO_VTABLE CCOMDemo :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CCOMDemo, &CLSID_COMDemo>,
public IDispatchImpl<IWelcome, &IID_IWelcome, &LIBID_COMServerLib, /*wMajor =*/ 1, /*wMinor =*/ 0>, public IDispatchImpl<IMath, &IID_IMath, &LIBID_COMServerLib, 1, 0>,
{
public:
    CCOMDemo()
    {
    }

DECLARE_REGISTRY_RESOURCEID(IDR_COMDEMO1)


BEGIN_COM_MAP(CCOMDemo)
    COM_INTERFACE_ENTRY(IWelcome)
    COM_INTERFACE_ENTRY(IMath)
    COM_INTERFACE_ENTRY2(IDispatch,IWelcome)
END_COM_MAP()



    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

public:



    STDMETHOD(Greeting)(BSTR name, BSTR* message);
    STDMETHOD(Add)(LONG val1, LONG val2, LONG* result); STDMETHOD(Sub)(LONG val1, LONG val2, LONG* result);
};

OBJECT_ENTRY_AUTO(__uuidof(COMDemo), CCOMDemo)

現在可以關注一下COMDemo.cpp了,在cpp文件中實現頭文件中聲明的方法:

STDMETHODIMP CCOMDemo::Greeting(BSTR name, BSTR* message)
{
    // TODO:  在此添加實現代碼
    CComBSTR tmp("Welcome, ");
    tmp.Append(name);
    *message = tmp;
    return S_OK;
}

STDMETHODIMP CCOMDemo::Add(LONG val1, LONG val2, LONG* result)
{
    *result = val1 + val1;
    Fire_Completed();
    return S_OK;
}

STDMETHODIMP CCOMDemo::Sub(LONG val1, LONG val2, LONG* result)
{
    *result = val1 - val2;
    Fire_Completed();
    return S_OK;
}

現在就可以生成組件了。生成過程會在注冊表中配置組件,需要管理員身份,所以請務必以管理員身份運行VS。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM