14

我创建了一个包含类“myCppClass”的 C++ Dll 项目,并尝试使用以下代码将其导出为 Dll:http: //msdn.microsoft.com/en-us/library/a90k134d(v=vs. 80).aspx

class __declspec(dllexport) CExampleExport : //public CObject
{ ... class definition ... };

我省略了“公共 CObject”,因为它需要 afx.h 并暗示它是一个 MFC Dll。我不确定这是否是一件好事,但它与 DLL 项目的默认设置不同。

从上面的链接文档中,我相信所有“公共函数和成员变量”都可以导入。我如何在 C# 中实现这一点?可以简单地实例化类吗?

编辑:我刚刚意识到帖子的标题可能具有误导性。重点应该放在 C# 中的 DllImport-ing 上,并确保我在 C++ 中正确遵循了文档

4

5 回答 5

16

C# 不能直接导入 C++ 类(它们实际上是名称损坏的 C 接口)。

您的选择是通过 COM 公开类、使用 C++/CLI 创建托管包装器或公开 C 样式接口。我会推荐托管包装器,因为这是最简单的并且会提供最好的类型安全性。

C 风格的界面看起来像这样(警告:未经测试的代码):

extern "C" __declspec(dllexport)
void* CExampleExport_New(int param1, double param2)
{
    return new CExampleExport(param1, param2);
}

extern "C" __declspec(dllexport)
int CExampleExport_ReadValue(void* this, int param)
{
    return ((CExampleExport*)this)->ReadValue(param)
}

C++/CLI 风格的包装器看起来像这样(警告:未经测试的代码):

ref class ExampleExport
{
private:
    CExampleExport* impl;
public:
    ExampleExport(int param1, double param2)
    {
        impl = new CExampleExport(param1, param2);
    }

    int ReadValue(int param)
    {
        return impl->ReadValue(param);
    }

    ~ExampleExport()
    {
        delete impl;
    }
};
于 2011-01-19T21:55:18.287 回答
11

据我所知,C# 只能与 COM 接口互操作。幸运的是,它不需要是一个完整的带有注册表的 COM 对象,它可以是任何实现 IUnknown 的普通旧 C++ 类。

所以在 C++ 中做这样的事情:

#include <Windows.h>

// Generate with from VisualStudio Tools/Create Guid menu
static const GUID IID_MyInterface = 
{ 0xefbf7d84, 0x3efe, 0x41e0, { 0x95, 0x2e, 0x68, 0xa4, 0x4a, 0x3e, 0x72, 0xca } };

struct MyInterface: public IUnknown
{
    // add your own functions here
    // they should be virtual and __stdcall
    STDMETHOD_(double, GetValue)() = 0;
    STDMETHOD(ThrowError)() = 0;
};

class MyClass: public MyInterface
{
    volatile long refcount_;

public:
    MyClass(): refcount_(1) { }

    STDMETHODIMP QueryInterface(REFIID guid, void **pObj) {
        if(pObj == NULL) {
            return E_POINTER;
        } else if(guid == IID_IUnknown) {
            *pObj = this;
            AddRef();
            return S_OK;
        } else if(guid == IID_MyInterface) {
            *pObj = this;
            AddRef();
            return S_OK;
        } else {
            // always set [out] parameter
            *pObj = NULL;
            return E_NOINTERFACE;
        }
    }

    STDMETHODIMP_(ULONG) AddRef() {
        return InterlockedIncrement(&refcount_);
    }

    STDMETHODIMP_(ULONG) Release() {
        ULONG result = InterlockedDecrement(&refcount_);
        if(result == 0) delete this;
        return result;
    }

    STDMETHODIMP_(DOUBLE) GetValue() {
        return 42.0;
    }

    STDMETHODIMP ThrowError() {
        return E_FAIL;
    }
};

extern "C" __declspec(dllexport) LPUNKNOWN WINAPI CreateInstance()
{
    return new MyClass();
}

在 C# 方面,您可以执行以下操作:

[ComImport]
[Guid("EFBF7D84-3EFE-41E0-952E-68A44A3E72CA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface MyInterface
{
    [PreserveSig] double GetValue();
    void ThrowError();
}

class Program
{
    [DllImport("mylib.dll")]
    static extern MyInterface CreateInstance();

    static void Main(string[] args)
    {
        MyInterface iface = CreateInstance();
        Console.WriteLine(iface.GetValue());
        try { iface.ThrowError(); }
        catch(Exception ex) { Console.WriteLine(ex); }
        Console.ReadKey(true);
    }
}

只要 C++ 和 C# 之间的通信通过虚拟接口,您几乎可以通过这种方式做任何您想做的事情。

于 2011-01-19T22:23:57.220 回答
5

您不能通过 C# 中的 pinvoke 创建 C++ 类实例。这是一个麻烦的实现细节,只有 C++ 编译器知道需要分配多少内存以及何时以及如何正确调用构造函数和析构函数。对象大小是迄今为止最难破解的螺母,没有任何方法可以使其可靠。

如果您不能将 C++ 类扁平化为静态方法,那么您需要编写一个托管包装器。这是使用 C++/CLI 语言完成的,您将编写一个“引用类”,其中将非托管类对象存储为指针,在构造函数中创建并在析构函数和终结器中删除。

于 2011-01-19T21:55:30.647 回答
0

实际上,您可以使用DllImport属性的EntryPoint属性直接引用损坏的名称。有关更多详细信息,请参阅此答案

于 2016-03-11T00:17:42.320 回答
0

C# 和 C++ 不像 C++ 和 Delphi 那样与 ABI 兼容,因此您不能导出虚拟类成员(方法)并在调用端声明它们纯虚拟并调用它们,因为 C# 无法处理 C++ 对象的 vtbl。我建议你用 COM 包装你的 C++ 类,这样你就有另一个积极的副作用,其他 COM 兼容的语言也可以使用你的类。

于 2016-07-09T15:09:22.653 回答