LoadLibraryを使ってC#からC++DLLを動的ロードして、DLL内の関数を呼び出す方法

C/C++

C#からC++のDLLを使うには2通りあり、DllImport属性を使った動的リンクと、LoadLibrary関数を使った動的ロードがあります。

予めDLLのパスがわかっている場合は動的リンクで事足りますが、今回はプログラム起動後にDLLパスを指定するためにLoadLibraryを使った動的ロードを行います。

DLLの動的ロード処理

C++のDLLを用意

C++側に簡単な足し算処理のソースを作成しました。

extern "C" __declspec(dllexport) int Sum(int a, int b) {
	return a + b;
}

ビルドして出力先ディレクトリの.dllファイルができあがるのを確認しておきます。

C#側でロード処理の用意

先ずDLLをロードする際に使用する基底クラスを作成します。

using System.Runtime.InteropServices;

public abstract class BaseDllClass : IDisposable
{
    [DllImport("kernel32")]
    private static extern IntPtr LoadLibrary(string dllFilePath);

    [DllImport("kernel32")]
    private static extern IntPtr GetProcAddress(IntPtr module, string functionName);

    [DllImport("kernel32")]
    private static extern bool FreeLibrary(IntPtr module);

    private IntPtr _dllPtr;

    public BaseDllClass(string dllFilePath)
    {
        if (!File.Exists(dllFilePath))
            throw new Exception("Not found dll.");
        // DLLのロード
        _dllPtr = LoadLibrary(dllFilePath);
        if (_dllPtr == IntPtr.Zero)
            throw new Exception("Failed load dll.");
    }

    protected T LoadFunction<T>(string functionName)
    {
        // 関数ポインタを取得
        IntPtr functionPtr = GetProcAddress(_dllPtr, functionName);
     if (functionPtr == IntPtr.Zero)
            throw new Exception("function not found.");

        // 関数ポインタをdelegateに変換する
        return (T)(object)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(T));
    }
    
    public void Dispose()
    {
        FreeLibrary(_dllPtr);
    }
}

次にBaseDllClassを継承した、DLLのラッパークラスを作成します。

using System.Runtime.InteropServices;

public class MyMath : BaseDllClass
{
    // ここで定義したdelegateにあとで関数ポインタをマッピングする
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate int Sum_Internal(int a, int b);

    private Sum_Internal _sum;

    public MyMath(string dllFilePath): base(dllFilePath) 
    {
         // コンストラクタで関数をロードしておく
         _sum = LoadFunction<Sum_Internal>("Sum");
    }

    // ラッパー関数
    public int Sum(int a, int b)
    {
        return _sum(a, b);
    }
}

C++DLLをC#側の実行ディレクトリコピー

C#側で参照できるようにdllファイルを移動しておいてください。

ここまでの手順で以下のような構成になったかと思います。

いざ呼び出し

using var myMath = new MyMath("myMath.dll");
Console.WriteLine(myMath.Sum(1, 1));

無事に足し算の結果が表示されました。

ここまででDLLの動的ロードと、関数の呼び出し方は把握できたかと思います。
ただ、実際にはこんな単純な関数ではなく、もっと複雑なデータ構造のやり取りがあると思うので、次回の記事では構造体、配列、コールバックなどもDLLとやり取りしてみようと思います。

次回記事

タイトルとURLをコピーしました