インターフェースから、PInvokeメソッドが実装されたクラスを動的に生成します。
- DllImportで指定するDLLのパスを実行時に解決できます
- SetDllDirectoryのためにディレクトリを分ける必要がありません
- 1つのインターフェースを書くだけで使用できます
- 複数のDllImportやデリゲートを定義する必要はありません
- Marshal.GetDelegateForFunctionPointer()のデリゲートをキャッシュする必要はありません
- 比較的少ないオーバーヘッドでDLLを呼び出せます(約7%~30%:引数により変化)
- 冗長なDllImport属性を一部まとめられます
- DLL(例:Test_x86.dll / Test_x64.dll)の関数のインターフェース(例:ITestDll)を作成します
- インターフェースにNativeDllLocation属性を付けます
- NativeDllLocation属性は、実行プロセスが32bit/64bitかどうかでリンクするDLLパスを選択します
- インターフェースのメソッドにNativeMethod属性を付けます
- NativeMethod属性は、DllImport属性とほぼ同等です
- その他、メソッドやパラメーターに必要なMarshalAs属性等を付けます
using DllCaller;
[NativeDllLocation(@"Test_x86.dll", @"Test_x64.dll")]
public interface ITestDll
{
[NativeMethod(EntryPoint = "func_void", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
void FuncVoid();
[NativeMethod(EntryPoint = "func_void_int", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
void FuncVoidInt(int i);
[NativeMethod(EntryPoint = "func_bool_int", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
bool FuncBoolInt(int i);
[NativeMethod(EntryPoint = "func_int_string", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
int FuncIntString([MarshalAs(UnmanagedType.LPWStr)] string str);
}
NativeDll.CreateInstance()を呼び出してインスタンスを作成します。
このメソッドを呼び出すと、指定したインターフェースに定義されたメソッドと同等のPInvokeメソッド生成し、そのPInvokeメソッドを呼び出すようにインターフェースを実装したクラスの生成とインスタンス化を行います。
public static class TestDll
{
public static readonly ITestDll Instance = NativeDll.CreateInstance<ITestDll>();
}
以上で、TestDll.InstanceのメソッドからDLLの関数が呼び出されます。
NativeDllLocator属性で、コードでDLLパスを指定できます。
INativeDllLocatorインターフェースを実装するクラス(例:TestDllLocator)を作成します。 DLLパスを返すLocateメソッドを実装します。
public class TestDllLocator : INativeDllLocator
{
public string Locate(Type type)
{
return (Environment.Is64BitProcess ? "x64" : "x86") + "\\TestDll.dll";
}
}
ITestDllインターフェースにNativeDllLocator属性を付け、TestDllLocatorクラスを指定します。
[NativeDllLocator(typeof(TestDllLocator))]
public interface ITestDll
注意: NativeDllLocation属性とNativeDllLocator属性は同時に使用できません。
GlobalNativeMethod属性で、各メソッドのNativeMethod属性の指定を省略できます。
[NativeDllLocation(@"Test_x86.dll", @"Test_x64.dll")]
[GlobalNativeMethod(CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public interface ITestDll
{
void func_void();
void func_void_int(int i);
[return: MarshalAs(UnmanagedType.Bool)]
bool func_bool_int(int i);
int func_int_string([MarshalAs(UnmanagedType.LPWStr)] string str);
}
GlobalNativeMethod属性を付けた状態で、メソッドにNativeMethod属性を付けた場合は、NativeMethod属性が優先されます。
NativeDll.CreateInstance(bool assemblyOutput)メソッドにtrueを指定すると、動的に生成される型を含んだアセンブリが出力されます。 出力されるアセンブリの名前は、'指定されたインターフェース名Impl.dll'となります。
ildasmやILSpy等でこのアセンブリを読み込むことにより、生成されたクラスを確認できます。