Ответ 1
Просто некоторые соображения:
Альтернатива #one
EDIT: этот подход требует изменения скомпилированных методов, которые являются трудными и требуют инъекции, модификации сборки или других методов, которые обычно используются в AOP-land. Рассмотрим два подхода ниже, что проще.
- Удалите все функции с одной и той же сигнатурой, оставьте одну из них
- Используйте
GetIlAsByteArray
для создания динамического метода вашего методаDllImport
- Используйте описанную здесь методику, чтобы манипулировать IL-функцией функции, здесь вы можете изменить атрибуты DllImport и т.д.
- Создайте делегат из этих функций и кешируйте свои звонки
- Возвращает делегат
Альтернатива #two:
EDIT: этот альтернативный подход, по-видимому, немного связан с первым, но кто-то уже сделал эту работу для вас. Посмотрите эту превосходную статью CodeProject и просто загрузите и используйте ее код для динамического создания методов стиля DllImport. В основном это сводится к:
- Удалить все DllImport
- Создайте собственную оболочку DllImport: принимает имя dll и имя функции (при условии, что все подписи равны)
- Обертка выполняет "ручной" DllImport с
LoadLibrary
илиLoadLibraryEx
с использованием функций API dllimport - Оболочка создает метод для вас с
MethodBuilder
. - Возвращает делегат к тому методу, который вы можете использовать как функцию.
Альтернативный # три
РЕДАКТИРОВАТЬ: смотря далее, есть более простой подход: просто используйте DefinePInvokeMethod
, который делает все, что вам нужно. Ссылка MSDN уже дает хороший пример, но полная оболочка, которая может создавать любую Native DLL на основе DLL и имени функции, предоставляется в этой статье CodeProject.
- Удалите все ваши подписи стиля DllImport
- Создайте простой метод оболочки вокруг
DefinePInvokeMethod
- Обязательно добавьте простое кеширование (словарь?), чтобы предотвратить создание всего метода при каждом вызове
- Возвращает делегат из оболочки.
Здесь, как этот подход выглядит в коде, вы можете повторно использовать возвращаемый делегат столько, сколько хотите, дорогостоящее построение динамического метода должно выполняться только один раз для каждого метода.
EDIT: обновить образец кода для работы с любым делегатом и автоматически отобразить правильный тип возвращаемого значения и типы параметров из подписи делегата. Таким образом, мы полностью отделили реализацию от подписи, которая, учитывая вашу текущую ситуацию, наилучшим образом. Преимущества: у вас есть безопасность типа и одноточечная замена, что означает: очень легко управляемый.
// 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());
Возможно, возможны другие подходы (например, подход шаблонов, упомянутый кем-то другим в этом потоке).
Обновление: добавлена ссылка на отличную статью о кодепроекте.
Обновление: добавлен третий и более простой подход.
Обновление: добавленный образец кода
Обновить: обновленный образец кода для беспрепятственной работы с любым прототипом функции
Обновление: исправлена ужасная ошибка: typeof(Function02)
должен быть typeof(T)
, конечно