聊一聊 C# NativeAOT 多平台下的函数导出

360影视 动漫周边 2025-05-29 09:02 2

摘要:昨晚训练营里有一位朋友提到一个问题,说 C# AOT程序能否编译为一个dll,供其他语言调用,其实这个是完全没有问题的,也确实我的的文章体系中没有涉及到这块,那今天就补充完整吧。

1. 讲故事

昨晚训练营里有一位朋友提到一个问题,说 C# AOT程序能否编译为一个dll,供其他语言调用,其实这个是完全没有问题的,也确实我的的文章体系中没有涉及到这块,那今天就补充完整吧。

1. 简单的案例
extern "C"
{
_declspec(dllexport) void ComplexCalculation;
}

在 C# 中其实也差不多,用UnmanagedCallersOnly特性替代即可,接下来创建一个 C# 的类库,参考代码如下:
Internal classExportMethods
{
[UnmanagedCallersOnly(EntryPoint = "ComplexCalculation")]
public static int ComplexCalculation(int a, int b, IntPtr stringInput)
{
try
{
// 1. 处理字符串参数(从非托管代码传入的 char*)
string? inputStr = Marshal.PtrToStringAnsi(stringInput);

Console.WriteLine($"Input string: {a},{b}, {inputStr}");

// 2. 复杂计算逻辑
int result = a + b;

if (inputStr?.ToLower == "double")
{
result *=2;
}

return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return-1;
}
}
}

在 csproject 中追加最后二行,其中的Shared表示可以生成动态链接库,即 windows 上的 .dll 以及 linux 上的 .so 文件。
Sdk="Microsoft.NET.Sdk">


net8.0
disable
enable
AnyCPU;x86

true
Shared




配置好之后就可以用发布为 windows 的原生动态链接库,参考如下:
PS D:\skyfly\20.20250116\src\Example\Example_20_1_1> dotnet publish -r win-x64 -o D:\testdump
还原完成(0.3)
Example_20_1_1 已成功 (1.5) → D:\testdump\

在 2.1 中生成 已成功
PS D:\skyfly\20.20250116\src\Example\Example_20_1_1>

生成好 dll 之后可以用 PPEE 工具观察下 export 表,打开之后果然有了ComplexCalculation函数。

接下来大家可以把这个 dll 提供给 C 或者 C# 去调用,只要 PE头里有,怎么开心怎么玩。

Example_20_1_6的 C# 控制台程序,使用传统的 DllImport 导入外部方法,就像以前引入C的外部方法一样,参考代码如下:
internal classProgram
{
[DllImport("Example_20_1_1", CallingConvention = CallingConvention.StdCall)]
extern static int ComplexCalculation(int a, int b, IntPtr stringInput);

static void Main(string args)
{
Console.WriteLine("准备调用原生方法...");

// 1. 将托管字符串转换为非托管指针
IntPtr stringPtr = Marshal.StringToHGlobalAnsi("double");

try
{
// 2. 调用原生方法
int result = ComplexCalculation(1020, stringPtr);

Console.WriteLine($"调用完成,结果: {result}");
}
finally
{
// 3. 释放非托管内存
Marshal.FreeHGlobal(stringPtr);
}
}
}

将Example_20_1_1.dll拷贝到当前的bin目录下,运行程序之后,一切ok,截图如下:2. linux 上的投放刚才只是在 windows 平台上的演示,接下来试部署到ubuntu上,正常情况下你可能不会那么一帆风顺,不是缺少这个库就是那个库,比如下面的报错。
root@ubuntu2404:/data2/Example_20_1_1# dotnet publish -r linux-x64 -o ./dll
MSBuild version 17.8.22+bfbb05667 for .NET
Determining projects to restore...
Restored /data2/Example_20_1_1/Example_20_1_1.csproj (in 27.42 sec).
Example_20_1_1 -> /data2/Example_20_1_1/bin/Release/net8.0/linux-x64/Example_20_1_1.dll
Generating native code
/usr/bin/ld.bfd: cannot find -lz: No such file or directory

上面就是典型的缺少zlib包,安装一下即可,所以大家也是根据报错一个一个解决,最终肯定会走出迷雾。
root@ubuntu2404:/data2/Example_20_1_1# sudo apt update && sudo apt install zlib1g-dev

root@ubuntu2404:/data2/Example_20_1_1# dotnet publish -r linux-x64 -o ../
MSBuild version 17.8.22+bfbb05667 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
Example_20_1_1 -> /data2/Example_20_1_1/bin/Release/net8.0/linux-x64/Example_20_1_1.dll
Example_20_1_1 -> /data2/

root@ubuntu2404:/data2/Example_20_1_1# ls -lh ../
total 4.0M
drwxr-xr-x 5 root root 4.0K May 28 10:20 Example_20_1_1
-rw-r--r-- 1 root root 1.5M May 28 10:26 Example_20_1_1.so
-rwxr-xr-x 1 root root 2.5M May 28 10:26 Example_20_1_1.so.dbg
-rw-r--r-- 1 root root 0 May 28 10:18 app.c

有了这个 so 文件后,接下来我们新建一个 c文件,参考代码如下:


#include
#include // 动态加载库

int main
{
void *handle = dlopen("./Example_20_1_1.so", RTLD_LAZY);

if (!handle)
{
fprintf(stderr, "Error: %s\n", dlerror);
return1;
}

// 获取函数指针
int (*ComplexCalculation)(int, int, constchar *) =
(int (*)(int, int, constchar *))dlsym(handle, "ComplexCalculation");

if (!ComplexCalculation)
{
fprintf(stderr
dlclose(handle);
return1;
}

// 调用函数
int result = ComplexCalculation(1020, "double");
printf("Result: %d\n", result);

dlclose(handle);// 关闭句柄
return0;
}

使用 vscode 远程调试,哈哈,得到了我们想要的结果,截图如下:

这篇我们演示了 windows 上的C# 调用 C# AOT及 linux 上的C 调用 C# AOT,是不是挺有意思,也算是给训练营学员提供的一份资料参考。

来源:opendotnet

相关推荐