【问题标题】:C++ COM DLL for use in C#用于 C# 的 C++ COM DLL
【发布时间】:2015-03-06 03:27:10
【问题描述】:

在我为我的公司工作的一个项目中,出现了跨语言 DLL 通信问题。库提供者用 C++ 制作 DLL,我的公司将在 C# 中使用这个 DLL。

我在网上看到,如果 C++ DLL 是在 COM(组件对象模型)中创建的,Visual Studio 可以将此 DLL 作为引用添加到 C# 项目中并在代码中使用。

因此,我制作了非常简单的 DLL,并尝试按照 MSDN 文章“从 CPP 到 COM”来使这个 DLL 与 COM 兼容,这样我就可以将它与 C# 项目一起使用。但是,即使我可以编译 DLL 并且在构建时没有错误,我也无法设法让 C# 项目引用它,并且当我想将此 DLL 作为引用添加时,Visual Studio 会给出“没有有效的 COM 组件”的错误。

我为此制作了两个文件。一个“头”文件:

#include "stdafx.h"
// 467B2143-85CC-404E-9050-4A754EF1DF01
static const GUID IID_Test = { 0x467B2143, 0x85CC, 0x404E , { 0x90, 0x50, 0x4A,0x75,0x4E,0xF1,0xDF,0x01} };
// 5D4EEEE9-9BE4-4DE9-B36D-86B96400F0F1
static const GUID CLSID_Test = { 0x5D4EEEE9, 0x9BE4, 0x4DE9, {0xB3, 0x6D, 0x86, 0xB9, 0x64, 0x00, 0xF0, 0xF1} };

#define DEF_EXPORT _declspec(dllexport) 

interface ITest: public IUnknown
{
    public: 

    virtual HRESULT __stdcall MultiplyNumbers( double n1, double n2, double& result) = 0;

};

还有一个实现接口的文件(header-impl.cpp)

#include "stdafx.h" 
#include "Header.h"

ULONG g_dwRefCount = 0;

class Test : public ITest
{
protected:
    ULONG m_dwRefCount;

public:
    Test(): m_dwRefCount(0) {   }

    virtual HRESULT __stdcall MultiplyNumbers( double n1, double n2, double& result)
    {
        result = n1 * n2;
        return NO_ERROR;
    }

    virtual HRESULT __stdcall QueryInterface(  REFIID riid, void  **ppvObject)
    {
        if (riid==IID_IUnknown || riid==IID_Test) {
            *ppvObject= this;
        } else {
            return E_NOINTERFACE;
        }
        AddRef();
        return NO_ERROR;
    }

    virtual ULONG __stdcall AddRef( void)
    {
        g_dwRefCount++; 
        m_dwRefCount++; 
        return m_dwRefCount;
    }

    virtual ULONG __stdcall Release( void)
    {
        g_dwRefCount--; 
        m_dwRefCount--; 
        if (m_dwRefCount==0) {
            delete this; 
            return 0; 
        } 
        return m_dwRefCount;
    }

};

class TestFactory : public IClassFactory {
protected:
    ULONG m_dwRefCount;

public:

    TestFactory(): m_dwRefCount(0){ }

    HRESULT __stdcall CreateInstance(IUnknown *pUnkOuter, REFIID riid, void** ppObject)  
    {
        if (pUnkOuter!=NULL) {
            return CLASS_E_NOAGGREGATION;
        }

        Test *pobj = new Test();

        if (FAILED(pobj->QueryInterface(riid, ppObject))) {
            delete pobj;
            *ppObject=NULL;
            return E_NOINTERFACE;
        }
        return NO_ERROR;
    }

    HRESULT   __stdcall LockServer(BOOL fLock)
    {
        if (fLock) {
            g_dwRefCount++;
        } else {
            g_dwRefCount--;
        }

        return NO_ERROR;
    }

    virtual HRESULT __stdcall QueryInterface(  REFIID riid, void  **ppvObject)
    {
        if (riid==IID_IUnknown || riid==IID_IClassFactory) {
            *ppvObject=  this;
        } else {
            return E_NOINTERFACE;
        }
        AddRef();
        return NO_ERROR;
    }

    virtual ULONG __stdcall AddRef( void)
    {
        g_dwRefCount++;
        m_dwRefCount++;
        return m_dwRefCount;
    }


    virtual ULONG  __stdcall Release()
    {
        g_dwRefCount--;
        m_dwRefCount--;
        if (m_dwRefCount==0) {
            delete this;
            return 0;
        }
        return m_dwRefCount;


    }
};

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID * ppObject)
{
    if (rclsid!= CLSID_Test) { // Is this the number of our class?
        return CLASS_E_CLASSNOTAVAILABLE;
    }

    TestFactory *pFactory= new TestFactory;

    if (FAILED(pFactory->QueryInterface(riid, ppObject))) {
        delete pFactory;
        *ppObject=NULL;
        return E_INVALIDARG;
    }
    return NO_ERROR;

}

现在,我知道我的要求有点过分,但谁能指出这里的问题?我还有一个“Source.def”文件,里面有一些内容:

LIBRARY DLLTEST
EXPORTS
    DllGetClassObject

这使得编译器说“这个函数声明必须是私有的”。

我在这里走错路了吗?还是只是我,在写DLL时犯了一个错误。

谢谢。

PS:我没有调用 regsvr32.exe 之类的。我已经读到 Windows XP 和更高版本的操作系统不再严格要求了,我认为尝试在 Visual Studio 中添加作为参考可以为我解决这个问题。

【问题讨论】:

  • 您是否使用regsvr32注册了COM dll?
  • 我没有,我只是尝试使用 Visual Studio 添加 -> 项目(右键单击)-> 添加参考页面并尝试使用“浏览”按钮添加参考。当我尝试将 regsrv32.exe 与 DLL 一起使用时,它显示“已加载 DLL,但未找到 DllRegisterServer”。
  • 据我所知,regsvr32 必须成功才能在 .NET 中使用 DLL。我可能是错的。
  • 你用Visual C++ ATL项目模板试过了吗?
  • 不,但你确定我需要它吗? ATL 对我来说似乎是一个完全不同的故事,而且我以前从未使用过它。我现在需要做的就是让“库提供商公司”确保编写 C++ COM DLL 可以用于 C# 客户端代码。

标签: c# c++ .net dll com


【解决方案1】:

SO 上的某个人曾经将编写“旧式”COM 比作汇编编程。我认为这样说是公平的。人们正在逃离 COM,而不是相反。要将 COM 组件正确添加到 VS,必须在注册表中正确注册包含 COM 服务器的 DLL(假设您需要 inproc server)。这是使用 regsvr32.exe 程序完成的。但是regsvr32。 exe 需要一个入口点 - 即 DllRegisterServer() - http://msdn.microsoft.com/en-us/library/windows/desktop/ms682162(v=vs.85).aspx 。 (还有 DllUnregisterServer()) 我建议使用活动模板库来创建 COM 服务器,因为它启用了一些有用的抽象(IDL 和许多宏)。

对于“普通”C++ DLL - 您可以使用 C# 中可用的 P/Invoke 功能,并且无需使用任何 COM 即可访问 DLL 内容。

【讨论】:

  • 是的,显然 COM 是一个全新的世界。我很想使用 P/Invoke,但问题是:DLL 正在发布一些接口,这些接口应该由客户端为“事件通知”填充 - 这使得 P/Invoke 不适合我的需求 AFAIK。现在我正在检查本地 DLL 周围的 C++/CLI 包装器是否可以解决我的需求,但这也是很多工作。我简直不知所措..
  • 那个DLL本身就是一个com服务器吗?因为 COM 功能也可以通过 C# 直接访问。
  • 我其实不知道。我认为编写一些从 IUnkown 派生的类并实现它们的功能会使这个 DLL 成为 COM 服务器,我在这里弄错了吗?
猜你喜欢
  • 2011-09-26
  • 2012-03-09
  • 1970-01-01
  • 1970-01-01
  • 2015-02-22
  • 2013-06-21
  • 1970-01-01
  • 1970-01-01
  • 2011-05-05
相关资源
最近更新 更多