简体   繁体   English

如何在C#应用程序中使用DllExport C ++类

[英]How do I DllExport a C++ Class for use in a C# Application

I have created a C++ Dll project which contains a class "myCppClass" and tried to Dll export it using the following code as described by: http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx 我创建了一个包含类“myCppClass”的C ++ Dll项目,并尝试使用以下代码将其导出:如下所述: http//msdn.microsoft.com/en-us/library/a90k134d( v = vs。 80)的.aspx

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

I have omitted the "public CObject" as that requires afx.h and implies it is an MFC Dll. 我省略了“public CObject”,因为它需要afx.h并暗示它是一个MFC Dll。 I am not sure if this is a good thing or not but it differed from the DLL project default settings. 我不确定这是否是好事但它与DLL项目默认设置不同。

From the above linked documentation I am led to believe that all "public functions and member variables" are available for import. 从上面链接的文档中我可以相信所有“公共函数和成员变量”都可以导入。 How do I accomplish this in C#? 我如何在C#中实现这一目标? Can simply instantiate the class? 可以简单地实例化该类吗?

Edit: I just realized that the Title of the post may be misleading. 编辑:我刚刚意识到帖子的标题可能会产生误导。 The emphasis should be on DllImport-ing from C# and ensuring that I followed the documentation properly in C++ 重点应放在C#的DllImporting上,并确保我在C ++中正确地遵循了文档

C# cannot directly import C++ classes (which are effectively name-mangled C interfaces). C#不能直接导入C ++类(它们实际上是名称错误的C接口)。

Your options are exposing the class via COM, creating a managed wrapper using C++/CLI or exposing a C-style interface. 您可以选择通过COM公开类,使用C ++ / CLI创建托管包装或公开C风格的界面。 I would recommend the managed wrapper, since this is easiest and will give the best type safety. 我建议使用托管包装器,因为这是最简单的并且可以提供最佳的类型安全性。

A C-style interface would look something like this (warning: untested code): 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)
}

A C++/CLI-style wrapper would look like this (warning: untested code): 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;
    }
};

As far as I know, C# can only interop with COM interfaces. 据我所知,C#只能与COM接口互操作。 Lucky enough it doesn't need to be a full blown COM object with registry, it can be any plain old C++ class implementing IUnknown. 幸运的是,它不需要是一个完整的COM对象与注册表,它可以是任何普通的旧C ++类实现IUnknown。

So do something like this in 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();
}

And on the C# side you do something like this: 在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);
    }
}

You can do pretty much anything you want this way, as long as the communication between C++ and C# goes through the virtual interface. 只要C ++和C#之间的通信通过虚拟接口,你就可以用这种方式做任何你想做的事情。

You cannot create a C++ class instance through pinvoke from C#. 您无法通过C#中的pinvoke创建C ++类实例。 This is a troublesome implementation detail, only the C++ compiler knows how much memory needs to be allocated and when and how to properly call the constructor and destructor. 这是一个麻烦的实现细节,只有C ++编译器知道需要分配多少内存以及何时以及如何正确调用构造函数和析构函数。 The object size is by far the hardest nut to crack, there is no way whatsoever to make that reliable. 目标尺寸是迄今为止最难破解的螺母,没有办法使其可靠。

If you cannot flatten the C++ class out into static methods then you need to write a managed wrapper. 如果你不能将C ++类压缩成静态方法,那么你需要编写一个托管包装器。 That's done with the C++/CLI language, you'd write a "ref class" that has the unmanaged class object stored as a pointer, created in the constructor and deleted in the destructor and finalizer. 这是用C ++ / CLI语言完成的,你要编写一个“ref class”,它将非托管类对象存储为指针,在构造函数中创建并在析构函数和终结器中删除。

Actually, you can refer to the mangled names directly, using the EntryPoint property of the DllImport attribute. 实际上,您可以使用DllImport属性的EntryPoint属性直接引用损坏的名称。 See this answer for more details. 有关详细信息,请参阅此答案

C# and C++ are NOT ABI compatible like C++ and Delphi, so you cannot export virtual class members (methods) and declare them purely virtual on the calling side an invoke them, because C# cannot handle vtbl's of C++ objects. C#和C ++不像C ++和Delphi那样兼容ABI,所以你不能导出虚拟类成员(方法)并在调用它们时将它们纯粹地声明为调用它们,因为C#无法处理vtbl的C ++对象。 I would suggest you to wrap your C++ classes by COM, so you have another positive side effect, that other COM compatible languages can use your classes too. 我建议你用COM包装你的C ++类,这样你就会产生另一个积极的副作用,其他COM兼容语言也可以使用你的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM