如何导出 C++用于 C# 应用程序的类

发布于 2024-10-13 01:30:03 字数 567 浏览 10 评论 0原文

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

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

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

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

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

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

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. I am not sure if this is a good thing or not but it differed from the DLL project default settings.

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#? 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++

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

め可乐爱微笑 2024-10-20 01:30:03

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;
    }
};

C# cannot directly import C++ classes (which are effectively name-mangled C interfaces).

Your options are exposing the class via COM, creating a managed wrapper using C++/CLI or exposing a C-style interface. 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):

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):

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;
    }
};
孤蝉 2024-10-20 01:30:03

据我所知,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# 之间的通信通过虚拟接口,您就可以用这种方式执行几乎任何您想要的操作。

As far as I know, C# can only interop with COM interfaces. 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.

So do something like this in 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:

[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.

愿与i 2024-10-20 01:30:03

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

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

You cannot create a C++ class instance through pinvoke from 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. 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. 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.

白鸥掠海 2024-10-20 01:30:03

实际上,您可以使用 DllImport 属性。有关更多详细信息,请参阅此答案

Actually, you can refer to the mangled names directly, using the EntryPoint property of the DllImport attribute. See this answer for more details.

还给你自由 2024-10-20 01:30:03

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

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.
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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文