アンマネージ関数の動的呼び出し

C#でアンマネージな関数を使いたければ、DllImportを使うのが常道です。しかし、定義の際に必ずstaticをつけることからもわかるように、静的な定義であり、プログラムの実行中に動的に関数名を決めてロード、といったことはできません。

C/C++ではWin32APIのLoadLibraryとGetProcAddressを使って得た関数ポインタで関数を呼び出すことができます。そこでC#でも同じ方法を使ってみましょう。

// デリゲートの定義
delegate IntPtr LoadImageDelegate(string filename, int flags);
delegate int SaveImageDelegate(string filename, IntPtr img);

[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll")]
static extern bool FreeLibrary(IntPtr hLibModule);
    
static void Main(string[] args)
{
    // 関数ポインタを取得
    IntPtr ptrLib = LoadLibrary("highgui110.dll");
    IntPtr ptrLoad = GetProcAddress(ptrLib, "cvLoadImage");
    IntPtr ptrSave = GetProcAddress(ptrLib, "cvSaveImage");
    // 関数ポインタをデリゲートに変換する 
    LoadImageDelegate loadDelegate 
        = (LoadImageDelegate)Marshal.GetDelegateForFunctionPointer(ptrLoad, typeof(LoadImageDelegate));
    SaveImageDelegate saveDelegate
        = (SaveImageDelegate)Marshal.GetDelegateForFunctionPointer(ptrSave, typeof(SaveImageDelegate));
    // デリゲートを呼び出す
    IntPtr img = loadDelegate("hoge.jpg", 1);
    saveDelegate("fuga.png", img);
    // ライブラリを解放
    FreeLibrary(ptrLib);
}

こんな感じでMarshal.GetDelegateForFunctionPointerを使うことで関数ポインタをデリゲートに変換して呼び出すことができます。せっかくなのでこの機能をOpenCvSharpに組み込んでみました。ラップしてないのを使いたいという場合の急場しのぎに使える・・・かも。

delegate int SaveImageDelegate(String filename, IntPtr img);

static void Main(string[] args)
{
    IplImage img = new IplImage("lenna.jpg");

    using (var invoker = new DynamicInvoker<SaveImageDelegate>("highgui110.dll", "cvSaveImage"))
    {
        invoker.Call("lenna.bmp", img.CvPtr);
    }
}

C#とOpenCVの融合プログラミング―ライブラリのラッパーDLLを利用する

C#とOpenCVの融合プログラミング―ライブラリのラッパーDLLを利用する