使用AoT让.NetFramework4.7.2程序调用.Net8编写的库
1、创建.Net8的库,双击解决方案中的项目,修改如下,启用AoT:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<PublishAot>true</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
<CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
在Class1.cs文件中加入如下代码:
using System.Runtime.InteropServices;
namespace AoT001
{
public class Export
{
[UnmanagedCallersOnly(EntryPoint = "Add")]
public static int Add(int a, int b)
{
return a + b;
}
[UnmanagedCallersOnly(EntryPoint = "Combine")]
public static IntPtr Combine(IntPtr str, int num)
{
var name = nameof(Export);
string? inputStr = Marshal.PtrToStringAnsi(str);
string? result = $"{inputStr} -- {num} -- {name}";
return Marshal.StringToHGlobalAnsi(result);
}
[UnmanagedCallersOnly(EntryPoint = "Free")]
public static void Free(IntPtr ptr)
{
Marshal.FreeHGlobal(ptr);
}
}
}
右键,AoT库项目, 发布,弹出如下图,按步骤配置:
注意:AoT仅支持win-x64
点击发布后,启动编译过程,结果如下图:
如图,简单的dll编译后size是1.3M,因为AoT将所有的依赖库都打包到了DLL内,没有C#运行环境也可以执行。使用Dependence查看如下图:
可见AoT编译的dll已经和C或C++编译的dll兼容。在Framework4.7.2项目中可按普通C编译的dll使用,如下:
namespace Winform472
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
var sum = AoTHelp.Add(1111, 1112);
var str1 = "Hello World 0001";
var ptr1 = Marshal.StringToHGlobalAnsi(str1);
var ptr2 = AoTHelp.Combine(ptr1, 500);
var str2 = Marshal.PtrToStringAnsi(ptr2);
AoTHelp.Free(ptr2);
MessageBox.Show($"Hello, World! {sum} {str2}");
}
}
public static class AoTHelp
{
[DllImport("AoT001.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern int Add(int a, int b);
[DllImport("AoT001.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern IntPtr Combine(IntPtr str, int sum);
[DllImport("AoT001.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern void Free(IntPtr ptr);
}
}
点击按钮界面如下:
在AoT库中引入MathNet.Numerics库,测试使用三方库后AoT创建dll的情况,新增函数:
[UnmanagedCallersOnly(EntryPoint = "UseThirdLibTest")]
public static bool UseThirdLibTest(int a, int b)
{
var D = Matrix<double>.Build.Dense(2, 9, 1);
Console.WriteLine(D);
return MathNet.Numerics.Control.Equals(a, b);
}
重新发布后dll拷贝到调用项目的debug子命令,dll大下从1.3M变为1.8M。调用代码如下:
[DllImport("AoT001.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool UseThirdLibTest(int a, int b);
private void button2_Click(object sender, EventArgs e)
{
var res = AoTHelp.UseThirdLibTest(100, 100);
MessageBox.Show(res.ToString());
}
AoT库中传递结构体,AoT定义及调用分别如下:
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct ParamStruct
{
public Int32 ResFlag;
public UInt32 a;
public UInt32 b;
public UInt32 c;
}
[UnmanagedCallersOnly(EntryPoint = "GetValueFromAoT")]
public static ParamStruct GetValueFromAoT(int initValue, ParamStruct param)
{
var result = new ParamStruct();
if (initValue < 0)
{
result.ResFlag = -1;
return result;
}
result.ResFlag = 0;
result.a = param.a + (UInt32)initValue;
result.b = param.b + (UInt32)initValue * 2;
result.c = param.c + (UInt32)initValue * 3;
return result;
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct ParamStruct
{
public Int32 ResFlag;
public UInt32 a;
public UInt32 b;
public UInt32 c;
}
public static class AoTHelp
{
[DllImport("AoT001.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern ParamStruct GetValueFromAoT(int initValue, ParamStruct param);
}
var param = new ParamStruct { a = 10, b = 20, c = 30};
var param2 = AoTHelp.GetValueFromAoT(100, param);
MessageBox.Show(param2.ToString());
注:结构体定义内如含bool运行时报错、含数组编译报错,简单的数值类型是ok的。如需要传递字符串,可使用IntPtr来实现。