【C#|C++】C#调用C++导出的dll之非托管的方式

发布于:2025-07-30 ⋅ 阅读:(35) ⋅ 点赞:(0)


1、宏定义导出关键字

#ifndef dll_import
#define dll_export __declspec(dllexport)
#else
#define dll_export __declspec(dllimport)
#endif

__declspec 是 Microsoft C++ 编译器特有的关键字,用来为变量、函数、类等添加 存储类属性 或 编译器指令,从而控制它们的行为或链接方式。我们上述的操作就是给它一个更显示的指令名称,方便我们去理解和使用。


2、定义导出的方法

我们在需要被导出的函数的声明前面加上dll_export:

'''demo.hpp'''
extern "C" dll_export int __stdcall Add(int a, int b);

'''demo.cpp'''
dll_export int __stdcall Add(int a, int b)
{
	...
}

这里的__stdcall是一种调用的约定方式,这里我们指定了__stdcall的方式,因此在C#调用时,需要指定好__stdcall的方式:

[DllImport("demo.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int Add(int a, int b);

这里需要注意的是,如果使用的是P/Invoke的方式来对接,那么我们不能导出类,只能导出方法,如果我们写的是类,那么需要再封装一下,变成一个独立的方法给c#调用。(clr托管的方式,即可通过导出类的方式来给c#调用。)


3、关于对接中的一些变量问题

C#中的图像可以使用byte*,byte[] 来传,c++则用unsigned char* 来接。其中需要注意的是,接收byte*的数据时,我们需要在C++中手动进行对齐,我们可以计算step,计算方式如下:

int step = width * 3;
if(step % 4 != 0)
{
	step = ((width * 3) / 4 ) + 1) * 4;
}
cv::Mat src(height, width, CV_8UC3, ImgPtr, step);

这样构建出来的mat才是正常的mat。其中这里的 × 3指的是三通道,如果接收的单通道的图,那么 × 1即可。

c++给c#传字符串时,可以使用const char *,c#那边接收可以用IntPtr来接收,然后使用Marshal.PtrToStringAnsi 将其转成string。

4、关于一些细节问题

c++的接口如果要返回字符串的话,尽可能的返回char* 类型,而不是string类型,因为在返回string的时候,有可能会导致c#与c++的入参不一致而出现乱码入参(虽然不知道为什么会这样)。

尽可能的指定__stdcall或者常见的还有__cdecl,然后我们就可以在C#中声明调用的方法时指定好同样的调用约定。

如果我们在方法中用了new char创建的变量,一定要记得写一个接口单独释放char* 指针,这个方法将会在c#中获得string后调用。

5、设置visual studio

将.exe全部设置成.dll


网站公告

今日签到

点亮在社区的每一天
去签到