聊一聊 C# NativeAOT 多平台下的函数导出
一:背景
1. 讲故事
昨晚训练营里有一位朋友提到一个问题,说 C# AOT程序能否编译为一个dll,供其他语言调用,其实这个是完全没有问题的,也确实我的的文章体系中没有涉及到这块,那今天就补充完整吧。
二:NativeAOT 函数导出
1. 简单的案例
在 C 中我相信很多人都知道用 dllexport
进行函数导出,如下所示:
extern "C"
{
_declspec(dllexport) void ComplexCalculation();
}
在 C# 中其实也差不多,用 UnmanagedCallersOnly
特性替代即可,接下来创建一个 C# 的类库,参考代码如下:
internal class ExportMethods
{
[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 中追加最后二行,其中的 <NativeLib>Shared</NativeLib>
表示可以生成动态链接库,即 windows 上的 .dll 以及 linux 上的 .so 文件。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x86</Platforms>
<!--追加的 -->
<PublishAot>true</PublishAot>
<NativeLib>Shared</NativeLib>
</PropertyGroup>
</Project>
配置好之后就可以用 dotnet publish
发布为 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 class Program
{
[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(10, 20, 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 <stdio.h>
#include <dlfcn.h> // 动态加载库
int main()
{
void *handle = dlopen("./Example_20_1_1.so", RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "Error: %s\n", dlerror());
return 1;
}
// 获取函数指针
int (*ComplexCalculation)(int, int, const char *) =
(int (*)(int, int, const char *))dlsym(handle, "ComplexCalculation");
if (!ComplexCalculation)
{
fprintf(stderr, "Error: %s\n", dlerror());
dlclose(handle);
return 1;
}
// 调用函数
int result = ComplexCalculation(10, 20, "double");
printf("Result: %d\n", result);
dlclose(handle); // 关闭句柄
return 0;
}
使用 vscode 远程调试,哈哈,得到了我们想要的结果,截图如下:
三:总结
这篇我们演示了 windows 上的 C# 调用 C# AOT
及 linux 上的 C 调用 C# AOT
,是不是挺有意思,也算是给训练营学员提供的一份资料参考。
聊一聊 C# NativeAOT 多平台下的函数导出的更多相关文章
- Windows及Linux平台下的计时函数总结
本文对Windows及Linux平台下常用的计时函数进行总结,包括精度为秒.毫秒.微秒三种精度的各种函数.比如Window平台下特有的Windows API函数GetTickCount().timeG ...
- .NET平台下开源框架
一.AOP框架Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式.这种部署方面(asp ...
- Windows平台下的读写锁
Windows平台下的读写锁简单介绍Windows平台下的读写锁以及实现.背景介绍Windows在Vista 和 Server2008以后才开始提供读写锁API,即SRW系列函数(Initialize ...
- cocos2d-x实战 C++卷 学习笔记--第4章 win32平台下中文乱码问题
前言: 将GBK编码的字符串转为UTF-8编码.(通俗点说就是解决中文乱码问题) 简要介绍: 在Win32平台下通过 log 输出中文字符时,会出现中文乱码问题.同样的代码在 ios 和 Androi ...
- .NET平台下几种SOCKET模型的简要性能供参考
转载自:http://www.cnblogs.com/asilas/archive/2006/01/05/311309.html .NET平台下几种SOCKET模型的简要性能供参考 这个内容在cnbl ...
- Ninject是一款.Net平台下的开源依赖注入框架
Ninject是一款.Net平台下的开源依赖注入框架.按照官方说法,它快如闪电.超级轻量,且充分利用了.Net的最新语法,使用Lambda表达式代替Xml文件完成类型绑定.Ninject结构精巧,功能 ...
- Windows平台下的内存泄漏检测
在C/C++中内存泄漏是一个不可避免的问题,很多新手甚至有许多老手也会犯这样的错误,下面说明一下在windows平台下如何检测内存泄漏. 在windows平台下内存泄漏检测的原理大致如下. 1. 在分 ...
- 不同WINDOWS平台下磁盘逻辑扇区的直接读写
不同WINDOWS平台下磁盘逻辑扇区的直接读写 关键字:VWIN32.中断.DeviceIoControl 一.概述 在DOS操作系统下,通过BIOS的INT13.DOS的INT25(绝对读).INT ...
- linux下sprintf_s函数的替代
error code: ]; sprintf_s(buf, , "predicted position:(%3d, %3d)", predict_pt.x, predict_pt. ...
- 在 Linux 平台下使用 JNI
引言 Java 的出现给大家开发带来的极大的方便.但是,如果我们有大量原有的经过广泛测试的非 Java 代码,将它们全部用 Java 来重写,恐怕会带来巨大的工作量和长期的测试:如果我们的应用中需要访 ...
随机推荐
- 【答题系统可参考】php 禁止api被跨域调用
在 PHP 中,防止 API 被跨域调用可以通过设置适当的 HTTP 响应头来实现.跨域资源共享(CORS,Cross-Origin Resource Sharing)机制允许或拒绝来自不同源的请求. ...
- 关于vue,npm,webpack,nod的一点心得
玩前端,换不到一颗米,纯粹是基于兴趣(我也希望能换到米,但真到那一天,说不定兴趣就没了,^_^),感觉玩这个能带来快乐,仅此而已. 最近,又来了点兴趣,读了读<vue实战>,自然免不了再次 ...
- linux下配置ip为动态获取
点击查看代码 在Linux系统中配置网络接口以动态获取IP地址,通常需要使用DHCP(Dynamic Host Configuration Protocol).大多数现代Linux发行版都默认支持这个 ...
- 强!10.8K star!推荐一款用于威胁预防、安全检测的开源监控平台,功能非常强大!!
在当今数字化时代,网络安全威胁日益严峻,企业和组织对于高效.强大的安全解决方案的需求也愈发迫切. 今天给大家分享一个专注于安全监控的开源项目:Wazuh,正是应对这一挑战的有力武器,专门用于帮助个人和 ...
- 多版本Java 配置记录
来自 https://blog.csdn.net/zdl177/article/details/105246997 起因是为了启动MC 目录结构 Java总目录下放置多个jdk目录(jdk16.0.2 ...
- 当你在浏览器中输入 google.com 后按下回车发生了什么?
按下"g"键 接下来的内容介绍了物理键盘和系统中断的工作原理,但是有一部分内容却没有涉及.当你按下"g"键,浏览器接收到这个消息之后,会触发自动完成机制.浏览器 ...
- BUUCTF---basic RSA
题目 给出一个RSA加密的密文,阐述了RSA,主要就是代码实现解密 代码 点击查看代码 import gmpy2 from Crypto.Util.number import * from binas ...
- 客户端“自废武功”背后的深层秘密——CORS跨域是怎么回事?
客户端"自废武功"背后的深层秘密--CORS跨域是怎么回事? 嘿,对于刚入门的开发新手,你是不是曾经遇到过这样的情况:你正在愉快地开发一个 Web 应用,代码写得热火朝天,前后端配 ...
- 代码块--java进阶day03
1.代码块 1.局部代码块 定义在方法中的一对大括号,可以提早释放内存,走完{}里的逻辑后就会被释放,在之后的编程中无法使用 2.构造代码块 位置在类中,方法外的{},在构造方法执行的时候,构造代码块 ...
- 主存的扩展及其CPU的连接——位扩展
其初始状态 进行读操作: 输入对应地址,将MREQ端设置为低电平,此时片选端有效,r/w端为高电平,所以写使能端无效,然后通过数据线和数据总线,CPU读取数据. 进行写操作: 输入对应地址,将R/W设 ...