在C / C ++中编写DLL以实现.Net互操作性

在我的C#应用​​程序中,我想在C中编写一部分代码。我计划编写一个可与​​.Net互操作的DLL。 我怎样才能做到这一点?

基本上有三种正确的方法:

  • 使用C ++ / CLI。 如果此DLL仅由.NET使用,这是最佳方法。
  • 使用“ extern "C" ”兼容API,如Windows API本身。 这是最便携的,但对于调用者而言,使用类模型来表示对象并不方便。
    • 如果您真的打算用ANSI C(而不是C ++)编写,这是最好的选择。
    • 对于此路径,您将函数编写为extern "C" returntype __stdcall __declspec(dllexport) func(params) { ... }
    • 您还应该使用“caller-provide-the-buffer”内存模型,而不是返回库中分配的缓冲区。 在您需要为内部状态分配内存的情况下,调用者应将其视为不透明句柄,并且应为调用者提供访问器函数以提取数据。 在任何情况下都不应该要求调用者释放在库中分配的内存,但是调用者可以要求库进行重新分配。
  • 使用COM或类似COM的API。 在这里,您返回(通常通过out参数)指向接口的指针,该接口是具有纯虚函数,没有非虚函数且没有数据的类。
    • 实现是从这个抽象接口派生的具体类,它们可以有丰富的数据和辅助函数,因为这不会影响二进制接口。
    • 这在图书馆中工作量很大,但非常便携,便于消费者使用。

并且有一件事绝对不要做:

  • 在C ++类上使用__declspec(dllexport)

编辑:我还想解释选项#2的一些好的做法,它们将最大限度地提高可移植性,并使原生C / C ++部分也可以从非托管应用程序中使用。

您可以使用宏来更轻松,通常的做法是:

在头文件中,所有函数声明都是这样的

 MYPROJECTAPI(returntype) PublicFunc(params); 

在您的项目中,定义是

 #define MYPROJECTAPI(returntype) \ extern "C" returntype __stdcall __declspec(dllexport) 

在消费者项目中

 #define MYPROJECTAPI(returntype) \ extern "C" returntype __stdcall __declspec(dllimport) 

然后你可以为gcc等不使用__declspec其他编译器定义宏。

完整的解决方案看起来像(在公共头文件myproject.h ):

 #if _WIN32 # if BUILDMYPROJECT # define MYPROJECTAPI(returntype) \ extern "C" returntype __stdcall __declspec(dllexport) # else # define MYPROJECTAPI(returntype) \ extern "C" returntype __stdcall __declspec(dllimport) # endif #else # define MYPROJECTAPI(returntype) extern "C" returntype #endif 

然后你的Visual C ++项目将导致在构建myproject.dll时定义BUILDMYPROJECT

简而言之:

(1)创建一个新的C ++ / CLI库项目。

(2)编写代码。 对于需要从C#项目访问的类,请确保将它们创建为CLR类:

 public ref class R {/*...*/}; // CLR class public value class V {/*...*/}; // CLR struct public interface class I {/*...*/}; // CLR interface 

(3)编译项目并在C#项目中添加对它的引用。

下面是一个应用程序的示例,我必须这样做。 在我的例子中,我需要一个DLL来包装对仅在.lib中可用的函数的调用。 关键部分是声明中的extern "C" __declspec (dllexport) 。 这基本上就是你所需要的。 其余的只是在C#应用程序中使用dllimport并使编组正确。

 extern "C" __declspec (dllexport) LONG EstablishContext(DWORD dwScope, LPCVOID pvReserved1, LPCVOID pvReserved2, LPSCARDCONTEXT phContext) { return SCardEstablishContext(dwScope, pvReserved1, pvReserved2, phContext); }