c++ - 如何将 C++ 类包装在基于 C 的 dll 或基于 CLI 的 dll 中?

标签 c++ c dll c++-cli

有人告诉我将 C++ 中的 writen 类导入 dll,然后在 C# 应用程序中使用该 dll。正在关注this guide我创建了 dll,但我不能简单地在 C# 应用程序中使用它,因为它存在一些问题:

  1. 我应该为工厂函数的返回类型放置什么?

  2. const wchar_t* 相当于什么?我的构造函数参数类型是什么?

  3. 如何检索和使用类型为 vector< wstring> 的函数返回类型?

这些是阻止我在 C# 应用程序中使用 C++ DLL 的问题。我被告知我需要使用 C++/CLI 创建一个包装器,然后在我的 C# 中使用它。但遗憾的是我对此一无所知,我不知道 C++.net。

目前唯一让我觉得有点耸人听闻的事情是让它以某种方式与 C 兼容,然后创建一个 C DLL 并在我的 C# 应用程序中使用它。我读到在 C 中,类对象指针可以通过 HANDLE 访问。 s,所以我认为在不进行大量更改的情况下让事情顺利进行是个好主意。

所以问题是我如何使用 Handles 访问我在 C 中的类对象并使用它们?我怎样才能转换 vector<wstring>到它的 C 对应物? 如果我想使用 CLI 为我的 C++ DLL 创建一个包装器(DLL?),以便在其他 dotnet 应用程序中使用,我应该怎么做?

最佳答案

为了为 C++ 类制作一个 C 包装器,以便在例如 C# 应用程序中使用,您可以执行以下操作。

在 Visual Studio 中选择 Win32 Console Application 并输入名称,然后单击下一步并在下一个 Pane 中选择 DLL 并单击完成。完成后,您将看到一个包含 3 个文件的 DLL 项目。

testdll.h 
testdll.cpp
dllmain

删除testdll.htestdll.cpp 文件中存在的所有内容,并将以下内容分别复制到每个文件中。将这些行添加到您的 testdll.h

// Our C wrapper for creating a dll to be used in C# apps

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the TESTDLL_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// TESTDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef  TESTDLL_EXPORTS
#define  TESTDLL_API __declspec(dllexport)
#else
#define  TESTDLL_API __declspec(dllimport)
#endif

extern "C"
{
     TESTDLL_API int OurTestFunction(int x, int y);                         
}

在这个 extern "C" block 中,您可以在其中定义接口(interface)和访问类成员函数的函数。请注意函数原型(prototype)之前的 TESTDLL。您的所有功能都必须由此进行。

将这些添加到您的 testdll.cpp 文件中:

#include "testdll.h"
#include "ourClass.h"

#define DLL_EXPORT

extern "C"
{
    OurClass ourObject;
    TESTDLL_API int OurTestFunction(int x, int y)
    {
        return ourObject.Add(x,y);
    }
}

你编译它并得到一个基于 C 的 dll,它可以在 C# 应用程序中使用。
不过有几点需要注意,比较重要的是:

  1. 您需要了解您用作代理的代码——我的意思是 testdll.h 中的函数定义,只能使用 C 兼容类型,毕竟是C,不是C++。
  2. 是您希望能够分配您的新对象 类而不是仅仅使用一个全局对象来访问所有方法。

为此,如果您需要在成员函数之间传递您的类对象,您需要先将其转换为 C 可以理解的 void* 然后传递它并使用它来访问您的成员任何功能。

例如,为了让用户能够间接管理对象,我会在我的 testdll.h 中包含这样的内容:

#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif

extern "C"
{
    TESTDLL_API int OurTestFunction(int x, int y);                      

    TESTDLL_API void*  CreateHandle();
    TESTDLL_API void*  GetCurrentHandle();
    TESTDLL_API void   DisposeCurrentHandle();
    TESTDLL_API void   SetCurrentHandle(void* handle);
    TESTDLL_API void*  GetHandle();
    TESTDLL_API void   DisposeHandle(void*);
    TESTDLL_API void   DisposeArrayBuffers(void);
}

在我的 testdll.cpp 中,我将它们定义为:

#include "testdll.h"
#include "ourClass.h"

#define DLL_EXPORT

extern "C"
{
    OurClass *ourObject;

    TESTDLL_API int OurTestFunction(int x, int y)
    {
        //return ourObject.Add(x,y); -- not any more !!
        ourObject = reinterpret_cast<OurClass *>(GetHandle());
    }

    //Handle operations
    TESTDLL_API void* CreateHandle()
    {
        if (ourObject == nullptr)
        {
            ourObject = new OurClass ;
        }
        else
        {
            delete ourObject ;
            ourObject = new OurClass ;
        }
        return reinterpret_cast<void*>(ourObject);
    }

    TESTDLL_API void* GetCurrentHandle()
    {
        return reinterpret_cast<void*>(ourObject );
    }

    TESTDLL_API void  DisposeCurrentHandle()
    {
        delete ourObject ;
        ourObject = nullptr;
    }

    TESTDLL_API void  SetCurrentHandle(void* handle)
    {
        if (handle != nullptr)
        {
            ourObject = reinterpret_cast<OurClass *>(handle);
        }
        else
        {
            ourObject = new OurClass ;
        }

    }

    //factory utility function
    TESTDLL_API void* GetHandle()
    {
        void* handle = GetCurrentHandle();
        if (handle != nullptr)
        {
            return handle;
        }
        else
        {
            ourObject = new OurClass ;
            handle = reinterpret_cast <void*>(ourObject );
        }
        return handle;
    }

    CDLL_API void  DisposeHandle(void* handle)
    {
        OurClass * tmp = reinterpret_cast<OurClass *>(handle);
        delete tmp;
    }

    TESTDLL_API void DisposeArrayBuffers(void)
    {
        ourObject = reinterpret_cast<OurClass *>(GetHandle());
        return ourObject ->DisposeBuffers();//This is a member function defined solely for this purpose of being used inside this wrapper to delete any allocated resources by our class object.
    }
}

当我们编译这个 Dll 时,我们可以很容易地在我们的 C# 应用程序中使用它。在能够使用我们在此 dll 中定义的函数之前,我们需要使用适当的 [ImportDll()]。所以对于我们的 TestDll,我们会写:

[DllImport(@"TestDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int OurTestFunction(int firstNumber,int secondNumber); 

最后像这样使用它:

private void btnReadBigram_Click(object sender, EventArgs e)
{
    int x = OurTestFunction(10,50);
    MessageBox.Show(x.ToString());
}

这就是我为使我的 C++ 类成员函数在 C# 应用程序中可访问而没有任何麻烦所做的一切。

注意:
编译您的 C# 应用程序时,请确保您选择了 x86 平台来编译您的项目,而不是 AnyCpu。您可以通过属性更改您的平台。

注2:
要了解如何为您的 native C++ 类创建 C++/CLI 包装器,请阅读:C++/CLI wrapper for your native C++ class .

关于c++ - 如何将 C++ 类包装在基于 C 的 dll 或基于 CLI 的 dll 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17912107/

相关文章:

c# - 如何在 C# 中 DllImport 枚举

c++ - 如何更改线程的名称

c - 如何并行运行该函数?

c - "Gtk-WARNING **: Locale not supported by C library. Using the fallback ' C OS X 上的 ' locale."

c - 如何将单个字符数字(如 '5')转换为整数

c# - 在 C# 中捕获从 Delphi DLL 抛出的异常

C++ 从 istream 读取直到换行(但不是空格)

c++ - 用于容器/类型名转发的递归模板类型

c++ - 编译错误 : "‘function’ in namespace ‘std’ does not name a template type"

dll - 当我尝试将文件/文件夹发送到压缩(压缩)文件夹时,没有任何反应