在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#项目中添加对它的引用。
通过P / Invoke层。
下面是一个应用程序的示例,我必须这样做。 在我的例子中,我需要一个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); }