用AOT导出本地dll
记得之前在做医保接口时,经常要调一些C++或delphi写的本地dll(这些dll通常是用来读医保卡之类),这里简单介绍一下用C#生成本地可以直接调用dll,具体做法是通过UnmanagedCallersOnlyAttribute来完成。
dll项目文件如下
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <LangVersion>12</LangVersion> <PublishAot>true</PublishAot> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="8.0.0" /> </ItemGroup> </Project>
定义了几个简单的方法,如下:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace classlib
{
//发布命令 dotnet publish /p:NativeLib=Shared --use-current-runtime
public static class DllTest
{
[UnmanagedCallersOnly(EntryPoint = "int_add")]
public static int Add(int a, int b)
{
return a + b;
}
[UnmanagedCallersOnly(EntryPoint = "string_add")]
public static IntPtr StringTest(IntPtr ptr1, IntPtr ptr22)
{
var s = Marshal.PtrToStringUTF8(ptr1) + Marshal.PtrToStringUTF8(ptr22);
return Marshal.StringToCoTaskMemUTF8(s);
}
[UnmanagedCallersOnly(EntryPoint = "get_doubles")]
public static IntPtr StringTest()
{
return Marshal.UnsafeAddrOfPinnedArrayElement(new double[] { 1.1, 1.2 }, 0);
}
[UnmanagedCallersOnly(EntryPoint = "set_doubles", CallConvs = new[] { typeof(CallConvCdecl) })]
public static double SetAll(IntPtr InItems, int InItemsLength)
{
var sum = 0d;
for (int i = 0; i < InItemsLength; i++)
{
sum += Marshal.PtrToStructure<double>(InItems + i * Marshal.SizeOf<double>());
}
return sum;
}
[UnmanagedCallersOnly(EntryPoint = "get_order")]
public static IntPtr get_order()
{
var order = new Order { ID = 1, Name = "订单1" };
IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(order));
Marshal.StructureToPtr(order, structPtr, false);
return structPtr;
}
}
public struct Order
{
public int ID { get; set; }
public string Name { get; set; }
}
}
然后在命令行中发布应用:
dotnet publish /p:NativeLib=Shared --use-current-runtime
现在创建测试的.NET项目,使用.NET中的P/Invocke来调用。把发布出来的dll,复制到当前项目,并设置文件“复制到输出目录”属性为始终复制。下面的代码就是分别调用上面方法的方式和用到的参数和类型 。
项目中分别测试了,int,double[],string,自定义结构等几种类型的进出。
// See https://aka.ms/new-console-template for more information
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
Console.WriteLine(MyClass.int_add(1, 2));
Console.WriteLine(MyClass.string_add("abcd", "efg"));
var resultPtr = MyClass.get_doubles();
var Items = new double[2];
for (int j = 0; j < 2; j++)
{
Items[j] = Marshal.PtrToStructure<double>(resultPtr + j * Marshal.SizeOf<double>());
Console.WriteLine(Items[j]);
}
var sum = MyClass.set_doubles(new double[] { 5.6, 6.7 }, 2);
Console.WriteLine(sum);
var orderP = MyClass.get_order();
var order = Marshal.PtrToStructure<Order>(orderP);
Console.WriteLine(order);
Console.ReadLine();
public class MyClass
{
// 定义非托管函数的签名
[DllImport("classlib.dll")] // 指定DLL文件名
public static extern int int_add(int a, int b);
[DllImport("classlib.dll")] // 指定DLL文件名
public static extern string string_add(string a, string b);
[DllImport("classlib.dll")] // 指定DLL文件名
public static extern IntPtr get_doubles();
[DllImport("classlib.dll")] // 指定DLL文件名
public static extern double set_doubles(double[] arr, int length);
[DllImport("classlib.dll")] // 指定DLL文件名
public static extern IntPtr get_order();
}
public struct Order
{
public int ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return $"Order ID:{ID},Name:{Name}";
}
}
文章来源微信公众号
想要更快更方便的了解相关知识,可以关注微信公众号
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
2022-02-21 .net LTS3.1升5.0和LTS6.0隐蔽的坑
2022-02-21 应用内moniter
2017-02-21 数组,一维数组,二维数组,交错数组