Loading

C#互操作(C# 调用动态库)

很多时候需要调用其它语言生成的动态库,那么 c# 如何调用动态库呢
下面简单介绍 C# 调用 c 语言生成的动态库

func.c

int add(int num1,int num2)
{
    return num1 + num2;
}

编译生成动态库文件

gcc -fPIC -shared func.c -o libfunc.so

c# 代码

using System;
using System.Runtime.InteropServices;

namespace Domain
{
    class Program
    {
        static void Main(string[] args)
        {
            var num = Func.Add(1, 2);
            Console.WriteLine("Hello World {0}!", num);
        }
    }

    public class Func
    {

        [DllImport("libfunc", EntryPoint = "add")]
        public static extern int Add(int num1, int num2);

    }
}

编译 dotnet 程序,将 dotnet 程序和 动态库放在同一目录下(确保动态库能被找到) 可以设置环境变量 LD_LIBRARY_PATH (基本知识需要自己补充)

输出

Hello World 3!

文档

高级用法

MsQuicApi.cs

// 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; }

        // This is workaround for a bug in ILTrimmer.
        // Without these DynamicDependency attributes, .ctor() will be removed from the safe handles.
        // Remove once fixed: https://github.com/mono/linker/issues/1660
        [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicRegistrationHandle))]
        [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConfigurationHandle))]
        [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicListenerHandle))]
        [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConnectionHandle))]
        [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicStreamHandle))]
        private MsQuicApi(NativeApi* vtable)
        {
            uint status;

            SetParamDelegate =
                Marshal.GetDelegateForFunctionPointer<SetParamDelegate>(
                    vtable->SetParam);

            GetParamDelegate =
                Marshal.GetDelegateForFunctionPointer<GetParamDelegate>(
                    vtable->GetParam);

            SetCallbackHandlerDelegate =
                Marshal.GetDelegateForFunctionPointer<SetCallbackHandlerDelegate>(
                    vtable->SetCallbackHandler);

            RegistrationOpenDelegate =
                Marshal.GetDelegateForFunctionPointer<RegistrationOpenDelegate>(
                    vtable->RegistrationOpen);
            RegistrationCloseDelegate =
                Marshal.GetDelegateForFunctionPointer<RegistrationCloseDelegate>(
                    vtable->RegistrationClose);

            ConfigurationOpenDelegate =
                Marshal.GetDelegateForFunctionPointer<ConfigurationOpenDelegate>(
                    vtable->ConfigurationOpen);
            ConfigurationCloseDelegate =
                Marshal.GetDelegateForFunctionPointer<ConfigurationCloseDelegate>(
                    vtable->ConfigurationClose);
            ConfigurationLoadCredentialDelegate =
                Marshal.GetDelegateForFunctionPointer<ConfigurationLoadCredentialDelegate>(
                    vtable->ConfigurationLoadCredential);

            ListenerOpenDelegate =
                Marshal.GetDelegateForFunctionPointer<ListenerOpenDelegate>(
                    vtable->ListenerOpen);
            ListenerCloseDelegate =
                Marshal.GetDelegateForFunctionPointer<ListenerCloseDelegate>(
                    vtable->ListenerClose);
            ListenerStartDelegate =
                Marshal.GetDelegateForFunctionPointer<ListenerStartDelegate>(
                    vtable->ListenerStart);
            ListenerStopDelegate =
                Marshal.GetDelegateForFunctionPointer<ListenerStopDelegate>(
                    vtable->ListenerStop);

            ConnectionOpenDelegate =
                Marshal.GetDelegateForFunctionPointer<ConnectionOpenDelegate>(
                    vtable->ConnectionOpen);
            ConnectionCloseDelegate =
                Marshal.GetDelegateForFunctionPointer<ConnectionCloseDelegate>(
                    vtable->ConnectionClose);
            ConnectionSetConfigurationDelegate =
                Marshal.GetDelegateForFunctionPointer<ConnectionSetConfigurationDelegate>(
                    vtable->ConnectionSetConfiguration);
            ConnectionShutdownDelegate =
                Marshal.GetDelegateForFunctionPointer<ConnectionShutdownDelegate>(
                    vtable->ConnectionShutdown);
            ConnectionStartDelegate =
                Marshal.GetDelegateForFunctionPointer<ConnectionStartDelegate>(
                    vtable->ConnectionStart);

            StreamOpenDelegate =
                Marshal.GetDelegateForFunctionPointer<StreamOpenDelegate>(
                    vtable->StreamOpen);
            StreamCloseDelegate =
                Marshal.GetDelegateForFunctionPointer<StreamCloseDelegate>(
                    vtable->StreamClose);
            StreamStartDelegate =
                Marshal.GetDelegateForFunctionPointer<StreamStartDelegate>(
                    vtable->StreamStart);
            StreamShutdownDelegate =
                Marshal.GetDelegateForFunctionPointer<StreamShutdownDelegate>(
                    vtable->StreamShutdown);
            StreamSendDelegate =
                Marshal.GetDelegateForFunctionPointer<StreamSendDelegate>(
                    vtable->StreamSend);
            StreamReceiveCompleteDelegate =
                Marshal.GetDelegateForFunctionPointer<StreamReceiveCompleteDelegate>(
                    vtable->StreamReceiveComplete);
            StreamReceiveSetEnabledDelegate =
                Marshal.GetDelegateForFunctionPointer<StreamReceiveSetEnabledDelegate>(
                    vtable->StreamReceiveSetEnabled);

            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())
            {
                IsQuicSupported = false;

                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
                }

                return;
            }
            

            // 重点这里 加载库 
            if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle))
            {
                try
                {
                   // 得到动态库函数指针
                    if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
                    {
                        delegate* unmanaged[Cdecl]<uint, out NativeApi*, uint> msQuicOpenVersion =
                            (delegate* unmanaged[Cdecl]<uint, out NativeApi*, uint>)msQuicOpenVersionAddress;
                        // 通过这个辅助函数返回所有 api 结构体指针
                        uint status = msQuicOpenVersion(MsQuicVersion, out NativeApi* vtable);
                        if (MsQuicStatusHelper.SuccessfulStatusCode(status))
                        {
                            IsQuicSupported = true;
                            // 结构体指针 还原到 包含c# 委托属性的类
                            Api = new MsQuicApi(vtable);
                        }
                    }
                }
                finally
                {
                    if (!IsQuicSupported)
                    {
                        NativeLibrary.Free(msQuicHandle);
                    }
                }
            }
        }

        private static bool IsWindowsVersionSupported() => OperatingSystem.IsWindowsVersionAtLeast(MinWindowsVersion.Major,
            MinWindowsVersion.Minor, MinWindowsVersion.Build, MinWindowsVersion.Revision);

        // TODO: Consider updating all of these delegates to instead use function pointers.
        internal RegistrationOpenDelegate RegistrationOpenDelegate { get; }
        internal RegistrationCloseDelegate RegistrationCloseDelegate { get; }

        internal ConfigurationOpenDelegate ConfigurationOpenDelegate { get; }
        internal ConfigurationCloseDelegate ConfigurationCloseDelegate { get; }
        internal ConfigurationLoadCredentialDelegate ConfigurationLoadCredentialDelegate { get; }

        internal ListenerOpenDelegate ListenerOpenDelegate { get; }
        internal ListenerCloseDelegate ListenerCloseDelegate { get; }
        internal ListenerStartDelegate ListenerStartDelegate { get; }
        internal ListenerStopDelegate ListenerStopDelegate { get; }

        // TODO: missing SendResumptionTicket
        internal ConnectionOpenDelegate ConnectionOpenDelegate { get; }
        internal ConnectionCloseDelegate ConnectionCloseDelegate { get; }
        internal ConnectionShutdownDelegate ConnectionShutdownDelegate { get; }
        internal ConnectionStartDelegate ConnectionStartDelegate { get; }
        internal ConnectionSetConfigurationDelegate ConnectionSetConfigurationDelegate { get; }

        internal StreamOpenDelegate StreamOpenDelegate { get; }
        internal StreamCloseDelegate StreamCloseDelegate { get; }
        internal StreamStartDelegate StreamStartDelegate { get; }
        internal StreamShutdownDelegate StreamShutdownDelegate { get; }
        internal StreamSendDelegate StreamSendDelegate { get; }
        internal StreamReceiveCompleteDelegate StreamReceiveCompleteDelegate { get; }
        internal StreamReceiveSetEnabledDelegate StreamReceiveSetEnabledDelegate { get; }

        internal SetCallbackHandlerDelegate SetCallbackHandlerDelegate { get; }

        internal SetParamDelegate SetParamDelegate { get; }
        internal GetParamDelegate GetParamDelegate { get; }
    }
}

    internal static unsafe class MsQuicNativeMethods
    {
        [StructLayout(LayoutKind.Sequential)]
        internal struct NativeApi
        {
            internal IntPtr SetContext;
            internal IntPtr GetContext;
            internal IntPtr SetCallbackHandler;

            internal IntPtr SetParam;
            internal IntPtr GetParam;

            internal IntPtr RegistrationOpen;
            internal IntPtr RegistrationClose;
            internal IntPtr RegistrationShutdown;

            internal IntPtr ConfigurationOpen;
            internal IntPtr ConfigurationClose;
            internal IntPtr ConfigurationLoadCredential;

            internal IntPtr ListenerOpen;
            internal IntPtr ListenerClose;
            internal IntPtr ListenerStart;
            internal IntPtr ListenerStop;

            internal IntPtr ConnectionOpen;
            internal IntPtr ConnectionClose;
            internal IntPtr ConnectionShutdown;
            internal IntPtr ConnectionStart;
            internal IntPtr ConnectionSetConfiguration;
            internal IntPtr ConnectionSendResumptionTicket;

            internal IntPtr StreamOpen;
            internal IntPtr StreamClose;
            internal IntPtr StreamStart;
            internal IntPtr StreamShutdown;
            internal IntPtr StreamSend;
            internal IntPtr StreamReceiveComplete;
            internal IntPtr StreamReceiveSetEnabled;

            internal IntPtr DatagramSend;
        }
posted @ 2021-06-30 16:46  microestc  阅读(2543)  评论(0编辑  收藏  举报