Parameterising的DllImport用于C#应用程序应用程序、Parameterising、DllImport

2023-09-07 02:22:04 作者:栀璃鸢年

我们有一个供应商谁提供了一个库,用于访问他们的硬件。不幸的是,如果你有多个设备,你需要输入他们的图书馆多次,用不同的DLL名称。因此,我们有一吨的重复code,我很担心它很快就会成为维护成为一场噩梦。

We have a supplier who provides a library for access to their hardware. Unfortunately, if you have multiple devices, you need to import their library multiple times, with different dll names. As a consequence, we have a metric ton of duplicated code, and I'm worried that it will soon become be a maintenance nightmare.

我们目前所面对的是财产以后这样的:

What we have at the moment is somthing like:

namespace MyNamespace {
    public static class Device01 {
        public const string DLL_NAME = @"Device01.dll";

        [DllImport(DLL_NAME, EntryPoint = "_function1")]
        public static extern int Function1(byte[] param);

...

        [DllImport(DLL_NAME, EntryPoint = "_function99")]
        public static extern int Function99(int param);
    }

...

    public static class Device16 {
        public const string DLL_NAME = @"Device16.dll";

        [DllImport(DLL_NAME, EntryPoint = "_function1")]
        public static extern int Function1(byte[] param);

...

        [DllImport(DLL_NAME, EntryPoint = "_function99")]
        public static extern int Function99(int param);
    }
}

如果我是用C或C ++,我只想定义一个函数文件,#包括他们多次在静态类,而不是pretty的,但比其他更好的,但在C#我没有这方面的选项​​。

If I were using C or C++, I would just define the functions one file and #include them multiple times in the static classes, not pretty but better than the alternative, but in C# I don't have that option.

如果任何人有关于如何有效地定义一个工厂,将使我们,因为我们所需要的,我会非常有兴趣,产生许多静电设备类的任何巧妙的构思

If anyone has any clever ideas about how to effectively define a factory which would allow us to generate as many static device classes as we need, I would be very interested.

谢谢

编辑:函数原型都相当videly变化,所以它依赖于它们是相同的任何方法将不适合。感谢您的建议,到目前为止,我没有expacting这么多的想法这么快。

The function prototypes are quite videly varied, so any method which relies on them being the same wouldn't be suitable. Thanks for the suggestions so far, I wasn't expacting so many ideas quite so quickly.

推荐答案

只是一些注意事项:

编辑:这种方法需要改变编译的方法,这是很难的,需要注入,装配或修改被在AOP-土地常用的其他方法。考虑下面的方法二,哪一个更容易。

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.

删除所有功能具有相同签名,互留之一 使用 GetIlAsByteArray 来创建的DllImport 方法的动态方法 使用the这里描述的技术的操控功能的白细胞介素,在这里你可以改变的DllImport属性等。 创建这些功能的委托和缓存您的通话 返回委托 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

编辑:此替代方法似乎有点涉及在第一,但已经有人做的工作适合你。查找这个优秀的$ C $的CProject文章,并简单地下载和使用它的code动态创建的DllImport风格的方法。基本上,它归结为:

Alternative #two:

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:

删除所有的DllImport 创建你自己的DllImport包装:采用一个DLL名称和函数名(假设所有的签名都是平等的) 的包装做了手册的DllImport与 调用LoadLibrary 或LoadLibraryEx使用dllimport的API函数 在该包装与 MethodBuilder 为您创建一个方法。 返回一个委托,你可以作为一个函数使用该方法。 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.

编辑:进一步看,有一个更简单的方法:直接使用 DefinePInvokeMethod 它做了所有你需要的。在MSDN链接已经提供了一个很好的例子,但一个完整的包装,可以创建任何原生的DLL基于DLL和函数的名称在的这$ C $的CProject文章。

Alternative #three

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.

删除所有的DllImport风格的签名 创建围绕 DefinePInvokeMethod 一个简单的包装方法 确保以简单的缓存(字典)添加至prevent构建整个方法在每次调用 从包装返回一个代表。 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.

这就是这个方法看起来在code,可以重复使用返回的委托,只要你喜欢一样多,动态方法的昂贵的建筑应该每个方法只需进行一次。

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.

编辑:更新了code样品与任何代表工作,并自动反映从委托签名正确的返回类型和参数类型。通过这种方式,我们完全脱钩的实施从签名,这就是,鉴于你目前的情况,我们可以做的最好的。优点:你有类型安全的变单点和,这意味着:非常容易管理

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).

更新:添加了一个链接,优秀$ C $的CProject文章 更新:第三和方式更简单的方法添加。 更新:添加code样品 更新:更新code样品与任何函数原型 更新:固定可怕的错误:的typeof(Function02)的的typeof(T)当然

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