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