Skip to content

UseDLLInCSharp

HexagramNM edited this page Feb 22, 2025 · 2 revisions

C#でdllの関数を呼び出す

NM_WindowCaptureで扱うWinRTのWindowsGraphicsCapture APIやDirectXはC++用のライブラリであるため、C++で処理を実装しています。このC++で実装した処理をDLLとして作っておくことで、C#で実装するWPFから処理を呼び出すことができます。

C++でdllを作成する

C++で実装した関数をDLLから呼び出せるように公開する場合、関数宣言の際に関数の前にextern "C" __declspec(dllexport)をつけます。DLLの関数呼び出しはC言語ベースです。C++のクラスについては、以下のような対応をする必要があります。

  • クラスのインスタンスを生成し、そのポインタを返す関数を用意する。
  • クラスのメソッドについては、クラスのインスタンスへのポインタを引数に取る関数として用意する。
  • クラスのインスタンスへのポインタを引数に取り、そのインスタンスを破棄し、メモリ解放する関数を用意する。

C++の処理でインスタンスの生成のために確保されたメモリは、C#上ではアンマネージドメモリとして扱われ、ガベージコレクションの対象外になります。そのため、 生成したインスタンスは使用しなくなったタイミングで明示的に破棄、メモリ解放する必要があります。

コードの詳細はNM_WindowCapture/NM_WindowCaptureAPI.hNM_WindowCapture/NM_WindowCaptureAPI.cppをご覧ください。

// クラスのインスタンスを生成し、そのポインタを返す関数の例
extern "C" __declspec(dllexport) NM_WindowCapture *CreateWindowCaptureObject(HWND baseHwnd);

NM_WindowCapture* CreateWindowCaptureObject(HWND baseHwnd)
{
    NM_WindowCapture* result = new NM_WindowCapture(baseHwnd);

    return result;
}

// クラスのメソッドを呼び出す関数の例。nullptrかどうかの確認をしたほうが安全
extern "C" __declspec(dllexport) void OpenWindowPicker(NM_WindowCapture* windowCaptureObj);

void OpenWindowPicker(NM_WindowCapture* windowCaptureObj)
{
    if (windowCaptureObj != nullptr)
    {
        windowCaptureObj->OpenWindowPicker();
    }
}

// クラスのインスタンスを破棄し、メモリ解放をする関数の例。nullptrかどうかの確認をしたほうが安全
extern "C" __declspec(dllexport) void DeleteWindowCaptureObject(NM_WindowCapture *windowCaptureObj);

void DeleteWindowCaptureObject(NM_WindowCapture* windowCaptureObj)
{
    if (windowCaptureObj != nullptr)
    {
        delete windowCaptureObj;
    }
}

C#で処理を呼び出す

C#でdll内の処理を呼び出すためにはstaticクラス(NM_WindowCaptureVirtualCameraではNM_WindowCaptureクラス)を用意する必要があります。さらにそのクラスの中にNativeMethodsというstaticクラスを用意し、そこでDllImportでdll内の関数を読み込む必要があります。

NM_WindowCaptureクラスでは他のC#コードでdllの関数を呼び出せるよう、NativeMethodsクラスにある対応するメソッドを呼び出すメソッドを用意する必要があります。少し面倒な構成ですが、NativeMethodsを用意しないとVisual Studio側のコード分析で警告が出てしまいます。

コードの詳細はNM_WindowCaptureVirtualCamera/NM_WindowCapture.csをご覧ください。以下の例はわかりやすくするために、一部のコードを省略しています。

namespace NM_WindowCaptureVirtualCamera
{
    public static class NM_WindowCapture
    {
        private static class NativeMethods
        {
            [DllImport("NM_WindowCapture.dll")]
            internal static extern IntPtr CreateWindowCaptureObject(IntPtr baseHwnd);

            [DllImport("NM_WindowCapture.dll")]
            internal static extern void OpenWindowPicker(IntPtr windowCaptureObj);

            [DllImport("NM_WindowCapture.dll")]
            internal static extern void DeleteWindowCaptureObject(IntPtr windowCaptureObj);
        }

        public static IntPtr CreateWindowCaptureObject(IntPtr baseHwnd)
        {
            return NativeMethods.CreateWindowCaptureObject(baseHwnd);
        }

        public static void OpenWindowPicker(IntPtr windowCaptureObj)
        {
            if (windowCaptureObj == IntPtr.Zero)
            {
                return;
            }
            NativeMethods.OpenWindowPicker(windowCaptureObj);
        }

        public static void DeleteWindowCaptureObject(IntPtr windowCaptureObj)
        {
            if (windowCaptureObj == IntPtr.Zero)
            {
                return;
            }
            NativeMethods.DeleteWindowCaptureObject(windowCaptureObj);
        }
    }
}

関数の引数や返り値に使う型ですが、以下の標準の型はC++とC#で同じものが使用できます。

  • void
  • int
  • char
  • bool
  • float
  • double

C++のクラスのポインタ(例:NM_WindowCapture*)やウィンドウハンドルHWNDは、C#ではIntPtrで扱います。C++のnullptrはC#ではIntPtr.Zeroとなります。WPFのウィンドウに対するウィンドウハンドルは、WPFのメインウィンドウのクラスで以下を呼び出すことで、helper.Handleとして取得することができます。helper.Handlenint型でIntPtrと互換性があります。

var helper = new System.Windows.Interop.WindowInteropHelper(this);

戻る

Clone this wiki locally