C#による動的ライブラリの呼び出し方法

C#から他の言語で作成された動的ライブラリを呼び出す必要があるケースは多いです。以下では、C#がC言語で作成された動的ライブラリを呼び出す方法を紹介します。

math.c

int calculateSum(int a, int b)
{
    return a + b;
}

動的ライブラリの作成

gcc -fPIC -shared math.c -o libmath.so

C#コード

using System;
using System.Runtime.InteropServices;

namespace Utility
{
    class Program
    {
        static void Main(string[] args)
        {
            var result = MathHelper.Sum(1, 2);
            Console.WriteLine("Result: {0}", result);
        }
    }

    public class MathHelper
    {
        [DllImport("libmath", EntryPoint = "calculateSum")]
        public static extern int Sum(int a, int b);
    }
}

.NETアプリケーションの実行時に動的ライブラリが配置されているディレクトリを確認するか、LD_LIBRARY_PATH環境変数を設定してください。

出力結果

Result: 3

高度な例

QuicHandler.cs

// .NET Foundationライセンスに基づくコード
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using static System.Net.Quic.Implementations.QuicHandler.NativeMethods;

namespace System.Net.Quic.Implementations.QuicHandler.Internal
{
    internal unsafe sealed class QuicHandler
    {
        private static readonly Version MinWindowsVersion = new Version(10, 0, 20145, 1000);

        public SafeQuicRegistrationHandle Registration { get; }

        [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeQuicRegistrationHandle))]
        [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeQuicConfigurationHandle))]
        private QuicHandler(NativeApi* vtable)
        {
            uint status;

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

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

            var cfg = new RegistrationConfig
            {
                AppName = ".NET",
                ExecutionProfile = QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY
            };

            status = RegistrationOpenDelegate(ref cfg, out SafeQuicRegistrationHandle handle);
            QuicExceptionHelpers.ThrowIfFailed(status, "RegistrationOpen failed.");

            Registration = handle;
        }

        internal static QuicHandler Api { get; } = null!;

        internal static bool IsQuicSupported { get; }

        private const int QuicVersion = 1;

        static QuicHandler()
        {
            if (OperatingSystem.IsWindows() && !IsWindowsVersionSupported())
            {
                IsQuicSupported = false;
                return;
            }

            if (NativeLibrary.TryLoad(Interop.Libraries.Quic, typeof(QuicHandler).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr quicHandle))
            {
                try
                {
                    if (NativeLibrary.TryGetExport(quicHandle, "QuicOpenVersion", out IntPtr quicOpenVersionAddress))
                    {
                        delegate* unmanaged[Cdecl]<uint, out NativeApi*, uint> quicOpenVersion =
                            (delegate* unmanaged[Cdecl]<uint, out NativeApi*, uint>)quicOpenVersionAddress;
                        uint status = quicOpenVersion(QuicVersion, out NativeApi* vtable);
                        if (QuicStatusHelper.SuccessfulStatusCode(status))
                        {
                            IsQuicSupported = true;
                            Api = new QuicHandler(vtable);
                        }
                    }
                }
                finally
                {
                    if (!IsQuicSupported)
                    {
                        NativeLibrary.Free(quicHandle);
                    }
                }
            }
        }

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

        internal RegistrationOpenDelegate RegistrationOpenDelegate { get; }
        internal RegistrationCloseDelegate RegistrationCloseDelegate { get; }

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

        internal IntPtr SetParam;
        internal IntPtr RegistrationOpen;
        internal IntPtr RegistrationClose;

        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 StreamOpen;
        internal IntPtr StreamClose;
        internal IntPtr StreamStart;
        internal IntPtr StreamShutdown;
        internal IntPtr StreamSend;
        internal IntPtr StreamReceiveComplete;
        internal IntPtr StreamReceiveSetEnabled;
    }
}

タグ: C# DllImport ナイティブライブラリ 互換性

8月9日 18:28 投稿