いくつかの考慮事項:
代替#one
編集:このアプローチでは、コンパイルされたメソッドを変更する必要があります。これは困難であり、注入、アセンブリの変更、またはAOPランドで一般的に使用されるその他のメソッドが必要です。以下のアプローチ2を検討してください。これは簡単です。
- 同じ署名を持つすべての関数を削除し、それぞれ1つを残します
GetIlAsByteArray
メソッドの動的メソッドを作成するためにDllImport
使用します
- ここで説明する手法を使用して、関数のILを操作します。ここで、DllImport属性などを変更できます。
- これらの関数のデリゲートを作成し、通話をキャッシュします
- 代理人を返す
代替案#2:
編集:この代替アプローチは最初は少し複雑に見えますが、誰かがすでにあなたのために仕事をしてくれました。この優れたCodeProjectの記事を検索し、そのコードをダウンロードして使用するだけで、DllImportスタイルのメソッドを動的に作成できます。基本的に、それは次のようになります。
- すべてのDllImportを削除します
- 独自のDllImportラッパーを作成します:dll名と関数名を取ります(すべての署名が等しいと仮定します)
- ラッパーは、dllimportAPI関数を使用して
LoadLibrary
または使用して「手動」DllImportを実行しますLoadLibraryEx
- ラッパーは、を使用してメソッドを作成します
MethodBuilder
。
- 関数として使用できるそのメソッドへのデリゲートを返します。
代替案#3
編集:さらに見てみると、より簡単なアプローチがDefinePInvokeMethod
あります:必要なすべてを実行するだけです。MSDNリンクはすでに良い例を示していますが、DLLと関数名に基づいてネイティブDLLを作成できる完全なラッパーは、このCodeProjectの記事で提供されています。
- すべてのDllImportスタイルの署名を削除します
- 簡単なラッパーメソッドを作成する
DefinePInvokeMethod
- 呼び出しごとにメソッド全体が構築されないように、必ず単純なキャッシュ(辞書?)を追加してください
- ラッパーからデリゲートを返します。
このアプローチがコードでどのように見えるかを次に示します。返されたデリゲートを好きなだけ再利用できます。動的メソッドのコストのかかる構築は、メソッドごとに1回だけ実行する必要があります。
編集:コードサンプルを更新して、任意のデリゲートで機能し、デリゲート署名からの正しいリターンタイプとパラメータータイプを自動的に反映するようにしました。このようにして、実装を署名から完全に切り離しました。これは、現在の状況を考慮して、私たちができる最善のことです。利点:型安全性と単一の変更点があります。つまり、非常に簡単に管理できます。
// expand this list to contain all your variants
// this is basically all you need to adjust (!!!)
public delegate int Function01(byte[] b);
public delegate int Function02();
public delegate void Function03();
public delegate double Function04(int p, byte b, short s);
// TODO: add some typical error handling
public T CreateDynamicDllInvoke<T>(string functionName, string library)
{
// create in-memory assembly, module and type
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("DynamicDllInvoke"),
AssemblyBuilderAccess.Run);
ModuleBuilder modBuilder = assemblyBuilder.DefineDynamicModule("DynamicDllModule");
// note: without TypeBuilder, you can create global functions
// on the module level, but you cannot create delegates to them
TypeBuilder typeBuilder = modBuilder.DefineType(
"DynamicDllInvokeType",
TypeAttributes.Public | TypeAttributes.UnicodeClass);
// get params from delegate dynamically (!), trick from Eric Lippert
MethodInfo delegateMI = typeof(T).GetMethod("Invoke");
Type[] delegateParams = (from param in delegateMI.GetParameters()
select param.ParameterType).ToArray();
// automatically create the correct signagure for PInvoke
MethodBuilder methodBuilder = typeBuilder.DefinePInvokeMethod(
functionName,
library,
MethodAttributes.Public |
MethodAttributes.Static |
MethodAttributes.PinvokeImpl,
CallingConventions.Standard,
delegateMI.ReturnType, /* the return type */
delegateParams, /* array of parameters from delegate T */
CallingConvention.Winapi,
CharSet.Ansi);
// needed according to MSDN
methodBuilder.SetImplementationFlags(
methodBuilder.GetMethodImplementationFlags() |
MethodImplAttributes.PreserveSig);
Type dynamicType = typeBuilder.CreateType();
MethodInfo methodInfo = dynamicType.GetMethod(functionName);
// create the delegate of type T, double casting is necessary
return (T) (object) Delegate.CreateDelegate(
typeof(T),
methodInfo, true);
}
// call it as follows, simply use the appropriate delegate and the
// the rest "just works":
Function02 getTickCount = CreateDynamicDllInvoke<Function02>
("GetTickCount", "kernel32.dll");
Debug.WriteLine(getTickCount());
他のアプローチも可能だと思います(このスレッドで他の誰かが言及したテンプレートアプローチのように)。
更新:優れたcodeproject記事へのリンクを追加しました。
更新: 3番目のはるかに簡単なアプローチが追加されました。
更新:コードサンプルを追加更新
:関数プロトタイプとシームレスに連携するようにコードサンプルを
更新更新:恐ろしいエラーを修正:もちろんtypeof(Function02)
必要ですtypeof(T)