C# 调用Native库的新方式
起因
在看.Net源码时候,发现调用Native库的新方式,这种方式是在.Net 5的时候加入的.下面我们一起看看新的调用方式.这种方式适合调用一次调用调用多个函数.新方式是在System.Runtime.InteropServices加入NativeLibrary这个静态工具类.主要有这几个函数可以调用.
- Load/TryLoad 加载动态库(dll/so文件)
- GetExport/TryGetExport 根据函数名获取函数地址/句柄
- Free 释放动态库
- SetDllImportResolver 设置回调函数
如何使用
//1. 加载动态库 Load有函数重载,可以指定动态库的路径,这里只是为了省事
IntPtr dllPtr = NativeLibrary.Load("E:/project/csharp/Dotnet6App/net6perf/bin/Release/b.dll");
//2. 根据函数名称获取动态库加载到内存中的函数地址
delegate* unmanaged[Cdecl]<int, int, int> addFuncPointer = (delegate* unmanaged[Cdecl]<int, int, int>)NativeLibrary.GetExport(dllPtr, "add");
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += addFuncPointer(1, 0);
}
Console.WriteLine(sum);
//3. 不使用的时候,对动态库进行释放
if (dllPtr != IntPtr.Zero)
{
NativeLibrary.Free(dllPtr);
}
在.Net 源码中是如何调用
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods;
namespace System.Net.Quic.Implementations.MsQuic.Internal
{
internal unsafe sealed class MsQuicApi
{
private static readonly Version MinWindowsVersion = new Version(10, 0, 20145, 1000);
public SafeMsQuicRegistrationHandle Registration { get; }
private MsQuicApi(NativeApi* vtable)
{
uint status;
SetParamDelegate =
Marshal.GetDelegateForFunctionPointer<SetParamDelegate>(
vtable->SetParam);
GetParamDelegate =
Marshal.GetDelegateForFunctionPointer<GetParamDelegate>(
vtable->GetParam);
//移除部分函数
var cfg = new RegistrationConfig
{
AppName = ".NET",
ExecutionProfile = QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY
};
status = RegistrationOpenDelegate(ref cfg, out SafeMsQuicRegistrationHandle handle);
QuicExceptionHelpers.ThrowIfFailed(status, "RegistrationOpen failed.");
Registration = handle;
}
internal static MsQuicApi Api { get; } = null!;
internal static bool IsQuicSupported { get; }
private const int MsQuicVersion = 1;
//使用静态构造函数
static MsQuicApi()
{
if (OperatingSystem.IsWindows() && !IsWindowsVersionSupported())
{
if (NetEventSource.Log.IsEnabled())
{
NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
}
return;
}
//在.Net 5开始支持Http3,是通过调用微软的另外一个开源msquic实现.
//加载msquic.dll,返回dll在内存中的句柄
if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle))
{
try
{
//获取MsQuicOpenVersion的在内存中的调用地址
if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
{
//将MsQuicOpenVersion函数的入口地址转为函数指针
delegate* unmanaged[Cdecl]<uint, out NativeApi*, uint> msQuicOpenVersion =
(delegate* unmanaged[Cdecl]<uint, out NativeApi*, uint>)msQuicOpenVersionAddress;
//传入版本号(目前是1),返回一个NatvieApi结构体类型的指针
uint status = msQuicOpenVersion(MsQuicVersion, out NativeApi* vtable);
if (MsQuicStatusHelper.SuccessfulStatusCode(status))
{
IsQuicSupported = true;
//将函数列表传入私有的构造函数
Api = new MsQuicApi(vtable);
}
}
}
finally
{
//如果不支持,将msquic.dll进行释放
if (!IsQuicSupported)
{
NativeLibrary.Free(msQuicHandle);
}
}
}
}
private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major,
MinWindowsVersion.Minor, MinWindowsVersion.Build, MinWindowsVersion.Revision);
//移除部分代码
internal SetParamDelegate SetParamDelegate { get; }
internal GetParamDelegate GetParamDelegate { get; }
}
}
秋风
2021-11-05