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

问题描述 投票:12回答:4

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

c# interop native
4个回答
20
投票

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

  • 使用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


4
投票

简而言之:

(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#项目中添加对它的引用。



1
投票

下面是一个应用程序的示例,我必须这样做。在我的例子中,我需要一个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);
}
© www.soinside.com 2019 - 2024. All rights reserved.