百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

跨语言调用C#代码的新方式-DllExport

off999 2025-06-18 23:30 36 浏览 0 评论

简介

上一篇文章使用C#编写一个.NET分析器文章发布以后,很多小伙伴都对最新的NativeAOT函数导出比较感兴趣,今天故写一篇短文来介绍一下如何使用它。

在以前,如果有其他语言需要调用C#编写的库,那基本上只有通过各种RPC的方式(HTTP、GRPC)或者引入一层C++代理层的方式来调用。

自从微软开始积极开发和研究Native AOT以后,我们有了新的方式。那就是直接使用Native AOT函数导出的方式,其它语言(C++、Go、Java各种支持调用导出函数的语言)就可以直接调用C#导出的函数来使用C#库。

废话不多说,让我们开始尝试。

开始尝试

我们先来一个简单的尝试,就是使用C#编写一个用于对两个整数求和的Add方法,然后使用C语言调用它。

1.首先我们需要创建一个新的类库项目。这个大家都会了,可以直接使用命令行新建,也可以通过VS等IDE工具新建。

dotnet new classlib -o CSharpDllExport

2.为我们的项目加入Native AOT的支持,根据.NET的版本不同有不同的方式。

  • 如果你是.NET6则需要引入Microsoft.DotNet.ILCompiler这个Nuget包,需要指定为7.0.0-preview.7.22375.6,新版本的话只允许.NET7以上使用。更多详情请看hez2010的博客 https://www.cnblogs.com/hez2010/p/dotnet-with-native-aot.html
  • 如果是.NET7那么只需要在项目属性中加入<PublishAot>true</PublishAot>即可,笔者直接使用的.NET7,所以如下配置就行。

3.编写一个静态方法,并且为它打上UnmanagedCallersOnly特性,告诉编译器我们需要将它作为函数导出,指定名称为Add。

using System.Runtime.InteropServices;
namespace CSharpDllExport
{
    public class DoSomethings
    {
        [UnmanagedCallersOnly(EntryPoint = "Add")]
        public static int Add(int a, int b)
        {
            return a + b;
        }
    }
}

4.使用dotnet publish -p:NativeLib=Shared -r win-x64 -c Release命令发布共享库。共享库的扩展名在不同的操作系统上不一样,如.dll.dylib.so。当然我们也可以发布静态库,只需要修改为-p:NativeLib=Static即可。

5.使用DLL Export Viewer工具打开生成的.dll文件,查看函数导出是否成功,如下图所示,我们成功的把ADD方法导出了,另外那个是默认导出用于Debugger的方法,我们可以忽略。工具下载链接放在文末。

6.编写一个C语言项目来测试一下我们的ADD方法是否可用。

#define PathToLibrary "E:\\MyCode\\BlogCodes\\CSharp-Dll-Export\\CSharpDllExport\\CSharpDllExport\\bin\\Release\\net7.0\\win-x64\\publish\\CSharpDllExport.dll"
// 导入必要的头文件
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
int callAddFunc(char* path, char* funcName, int a, int b);
int main()
{
    // 检查文件是否存在
    if (access(PathToLibrary, 0) == -1)
    {
        puts("没有在指定的路径找到库文件");
        return 0;
    }
    // 计算两个值的和
    int sum = callAddFunc(PathToLibrary, "Add", 2, 8);
    printf("两个值的和是 %d \n", sum);
}
int callAddFunc(char* path, char* funcName, int firstInt, int secondInt)
{
    // 调用 C# 共享库的函数来计算两个数的和
    HINSTANCE handle = LoadLibraryA(path);
    typedef int(*myFunc)(int, int);
    myFunc MyImport = (myFunc)GetProcAddress(handle, funcName);
    int result = MyImport(firstInt, secondInt);
    return result;
}

7.跑起来看看

这样我们就完成了一个C#函数导出的项目,并且通过C语言调用了C#导出的dll。同样我们可以使用Go的syscall、Java的JNI、Python的ctypes来调用我们生成的dll,在这里就不再演示了。

限制

使用这种方法导出的函数同样有一些限制,以下是在决定导出哪种托管方法时要考虑的一些限制:

  • 导出的方法必须是静态方法。
  • 导出的方法只能接受或返回基元或值类型(即结构体,如果有引用类型,那必须像P/Invoke一样封送所有引用类型参数)。
  • 无法从常规托管C#代码调用导出的方法,必须走Native AOT,否则将引发异常。
  • 导出的方法不能使用常规的C#异常处理,它们应改为返回错误代码。

数据传递引用类型

如果是引用类型的话注意需要传递指针或者序列化以后的结构体数据,比如我们编写一个方法连接两个string,那么C#这边就应该这样写:

[UnmanagedCallersOnly(EntryPoint = "ConcatString")]
public static IntPtr ConcatString(IntPtr first, IntPtr second)
{
    // 从指针转换为string
    string my1String = Marshal.PtrToStringAnsi(first);
    string my2String = Marshal.PtrToStringAnsi(second);
    // 连接两个string 
    string concat = my1String + my2String;
    // 将申请非托管内存string转换为指针
    IntPtr concatPointer = Marshal.StringToHGlobalAnsi(concat);
    // 返回指针
    return concatPointer;
}

对应的C代码也应该传递指针,如下所示:

// 拼接两个字符串
char* result = callConcatStringFunc(PathToLibrary, "ConcatString", ".NET", " yyds");
printf("拼接符串的结果为 %s \n", result);
....
char* callConcatStringFunc(char* path, char* funcName, char* firstString, char* secondString)
{
    HINSTANCE handle = LoadLibraryA(path);
    typedef char* (*myFunc)(char*, char*);
    myFunc MyImport = (myFunc)GetProcAddress(handle, funcName);
    // 传递指针并且返回指针
    char* result = MyImport(firstString, secondString);
    return result;
}

运行一下,结果如下所示:

附录

  • 本文代码链接:https://github.com/InCerryGit/BlogCodes/tree/main/CSharp-Dll-Export
  • DLL Export Viewer下载链接:https://www.nirsoft.net/utils/dllexp-x64.zip
  • NativeAOT文档:https://github.com/dotnet/runtime/tree/main/src/coreclr/nativeaot/docs

相关推荐

愤怒的小鸟变形金刚破解版(愤怒的小鸟愤怒的小鸟变形金刚破解版)

怒鸟红擎天柱最强。变形金刚的首领,拥有大型卡车的变身形态,是新手登场的必备角色。主要用来过掉第一章的剧情任务,升级后加护甲和武器,所以不需要太多的培养,前期一级就足够了。属性:护甲1级、武器1级、车辆...

12360订票官网下载软件(下载12360订票软件新版本)

应该是12306app怎么订票?首先我们下载一个12306app,然后凭自己的身份证号码和手机号码注册,注册成功之后,然后再登录12306网站,进行人证核验,核验成功之后就可以订票了,首先打开1230...

lol官网入口(Sxr.lol官网入口)

英雄联盟官网如下:https://lol.qq.com。进入官网,你可以看到官网的详细信息,里面有游戏介绍,视频,资料,赛事,活动等一系列内容。官网具有非常准确的官方信息,大家需要了解相关游戏资讯,可...

24小时在线直播免费看(24小时播放视频免费观看)

1、首先,打开抖音之后,进入视频播放页面,点击右下角的“我”,进入抖音个人中心,再点击右上角的“三”按钮。2、进入抖音设置之后,选择其中的“反馈与帮助”。3、进入反馈与帮助之后,选择其中的“直播(权限...

汽车网(汽车网新车报价大全)

1.部分内容有真实性,部分内容可能存在虚假或夸大情况。2.因为汽车江湖网是一个汽车资讯平台,其发布的内容主要来源于汽车行业的新闻、评论、测评等,其中一些内容可能会被厂商或广告商进行操控或干扰,导致...

无货源一件代发平台(云仓一件代发)

【一】1688【阿里巴巴】国内知名批发和一件代发平台,类目也是很齐全的产品很丰富的一个平台,基本上我们需要的产品上面都有,很多店主一件代发也都是来自于这个平台。【二】一起做网店和搜款网,基本上覆盖广州...

快吧游戏盒子下载安装(快吧 游戏 盒子)

只是一个游戏盒子的下载,在这个应用中心里找到这样的游戏盒子就能够直接下来的。快吧游戏盒子可以通过以下几个步骤进行下载。1.打开你的手机应用商店,例如苹果应用商店或安卓应用商店。2.在应用商店中搜索...

午夜成人小电影(我想看免费午夜电影)

看你喜好是什么了,你喜欢的就是好看的,午夜还是建议看一下剧情类的,千万不要看恐怖片,除非你想失眠。对于男生深夜观看悲伤电影,以下是一些适合的选择:1."肖申克的救赎"(TheSha...

2025最火网名(2025最火网名最新版)

1、绝世好女人2、气≦贯长虹3、滿眼浮華4、好久没换,改什么好5、成功可望6、小西福7、突然,好懷念8、翡ル翠鸿图9、陈运锋10、桃花峪运维朱恒彬11、金safety12、指间轻纱°13、花≦好月圆1...

163免费邮箱登录入口(126免费邮箱登录入口)

是https://mail.163.com/。原因是163邮箱是中国最早的电子邮件服务提供商之一,拥有稳定的运营和使用群体。用户可以通过网页直接登录,进行收发邮件,具有便捷、快速的特点。另外,163...

中国联通腾讯(中国联通腾讯大王卡)
中国联通腾讯(中国联通腾讯大王卡)

当时,联通免除抖音的流量费用,是因为联通和字节跳动公司有合作。现在合作已经结束了,所以就不免抖音流量了。其实可以理解为字节跳动花钱为大伙买流量,大伙免费用的意思。但是现在使用抖音的人这么多,人口基数这么大,字节跳动再买这么大的流量也买不起。...

2026-02-01 19:43 off999

酷狗音乐下载歌曲(酷狗音乐下载歌曲在哪能找到)
酷狗音乐下载歌曲(酷狗音乐下载歌曲在哪能找到)

本地音乐就是已下载的音乐。1、打开音乐,点击我的。2、点击本地。3、然后就看到已下载和推荐下载的音乐了。4、点击向下的箭头就可以下载了。意思就是在手机里面下载的音乐,保存到手机上面的就是本地音乐,在手机自带的音乐里面就能下载首先我们进入到...

2026-02-01 19:15 off999

免费法律咨询24小时在线(青岛免费法律咨询24小时在线)

没有这样的律师二十四小时不休息。首先要说的是,提问者二十四小时不休息等着律师二十四小时免费为你解答法律咨询呀?提这样的问题过过大脑,律师是活生生的人,不是电脑和机器人,二十四小时不休息的,这样的人想要...

无需root权限的游戏修改器

游戏修改器都需要root。原因如下:root意思是获得超级用户权限,root后才能打开系统文件,游戏文件都在系统文件里,不root是打不开系统文件的,就更别提修改了。你好游戏修改器都是修改系统的内...

网络推广平台哪家公司最好(网上怎么赚钱一天500)

1,依附于搜索引擎,针对各个搜索引擎做的,叫sem,包括了竞价和优化seo,这是目前最广泛应用的平台。2,微博,目前微博营销已经很火热,用户量巨大,也能带动产品的销售,尤其做电子商务的,一定重视微博。...

取消回复欢迎 发表评论: