优秀的编程知识分享平台

网站首页 > 技术文章 正文

C# 调用Native库的新方式

nanyue 2025-01-04 22:15:03 技术文章 4 ℃

起因

在看.Net源码时候,发现调用Native库的新方式,这种方式是在.Net 5的时候加入的.下面我们一起看看新的调用方式.这种方式适合调用一次调用调用多个函数.

新方式是在System.Runtime.InteropServices加入NativeLibrary这个静态工具类.主要有这几个函数可以调用.

  1. Load/TryLoad 加载动态库(dll/so文件)
  2. GetExport/TryGetExport 根据函数名获取函数地址/句柄
  3. Free 释放动态库
  4. SetDllImportResolver 设置回调函数

如何使用

//1. 加载动态库 Load有函数重载,可以指定动态库的路径,这里只是为了省事
IntPtr dllPtr = NativeLibrary.Load("E:/project/csharp/Dotnet6App/net6perf/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 源码中是如何使用NativeLibrary

 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
{
    //使用unsafe和sealed(密封类)标记
    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, #34;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; }
    }
}

如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流

Tags:

最近发表
标签列表