Skip to content

Dynamic PInvoke method generation by interface. This library is for dynamically specifying the path of DllImport.

License

Notifications You must be signed in to change notification settings

diontools/DllCaller

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 

Repository files navigation

NuGet version

DllCaller

インターフェースから、PInvokeメソッドが実装されたクラスを動的に生成します。

特徴

  • DllImportで指定するDLLのパスを実行時に解決できます
  • SetDllDirectoryのためにディレクトリを分ける必要がありません
  • 1つのインターフェースを書くだけで使用できます
    • 複数のDllImportやデリゲートを定義する必要はありません
  • Marshal.GetDelegateForFunctionPointer()のデリゲートをキャッシュする必要はありません
  • 比較的少ないオーバーヘッドでDLLを呼び出せます(約7%~30%:引数により変化)
  • 冗長なDllImport属性を一部まとめられます

使い方

インターフェースを定義

  1. DLL(例:Test_x86.dll / Test_x64.dll)の関数のインターフェース(例:ITestDll)を作成します
  2. インターフェースにNativeDllLocation属性を付けます
    • NativeDllLocation属性は、実行プロセスが32bit/64bitかどうかでリンクするDLLパスを選択します
  3. インターフェースのメソッドにNativeMethod属性を付けます
    • NativeMethod属性は、DllImport属性とほぼ同等です
  4. その他、メソッドやパラメーターに必要な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);
}

インスタンスの作成(PInvokeメソッドの動的生成)

NativeDll.CreateInstance()を呼び出してインスタンスを作成します。

このメソッドを呼び出すと、指定したインターフェースに定義されたメソッドと同等のPInvokeメソッド生成し、そのPInvokeメソッドを呼び出すようにインターフェースを実装したクラスの生成とインスタンス化を行います。

public static class TestDll
{
    public static readonly ITestDll Instance = NativeDll.CreateInstance<ITestDll>();
}

以上で、TestDll.InstanceのメソッドからDLLの関数が呼び出されます。

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属性は同時に使用できません。

NativeMethod属性の省略

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等でこのアセンブリを読み込むことにより、生成されたクラスを確認できます。

About

Dynamic PInvoke method generation by interface. This library is for dynamically specifying the path of DllImport.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published