Just some considerations:
Alternative #one
EDIT: this approach requires changing compiled methods, which is hard and requires injection, assembly modification or other methods that are commonly used in AOP-land. Consider approach two below, which is easier.
- Remove all functions with the same signature, leave one of each
- Use
GetIlAsByteArray
to create a dynamic method of your DllImport
method
- Use the technique described here to manipulate the IL of the function, here you can change the DllImport attributes etc.
- Create a delegate of these functions and cache your calls
- Return the delegate
Alternative #two:
EDIT: This alternative approach seems a bit involved at first, but someone already did the work for you. Look up this excellent CodeProject article and simply download and use its code to dynamically create DllImport style methods. Basically, it comes down to:
- Remove all DllImport
- Create your own DllImport wrapper: takes a dll name and a function name (assuming all signatures are equal)
- The wrapper does a "manual" DllImport with
LoadLibrary
or LoadLibraryEx
using the dllimport API functions
- The wrapper creates a method for you with
MethodBuilder
.
- Returns a delegate to that method you can use as a function.
Alternative #three
EDIT: looking further, there's an easier approach: simply use DefinePInvokeMethod
which does all you need. The MSDN link already gives a good example, but a full wrapper that can create any Native DLL based on DLL and function name is provided at this CodeProject article.
- Remove all your DllImport style signatures
- Create a simple wrapper method around
DefinePInvokeMethod
- Make sure to add simple caching (dictionary?) to prevent building the whole method on each call
- Return a delegate from the wrapper.
Here's how this approach looks in code, you can reuse the returned delegate as much as you like, the costly building of the dynamic method should be done only once per method.
EDIT: updated the code sample to work with any delegate and to automatically reflect the correct return type and parameter types from the delegate signature. This way, we decoupled the implementation completely from the signature, which is, given your current situation, the best we can do. Advantages: you have type safety and single-point-of-change, which means: very easily manageable.
// 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());
Other approaches are possible, I guess (like the templating approach mentioned by someone else in this thread).
Update: added a link to excellent codeproject article.
Update: third and way easier approach added.
Update: added code sample
Update: updated code sample to work seamlessly with any function prototype
Update: fixed dreadful error: typeof(Function02)
should be typeof(T)
of course