Mar*_*oth 11 c# dllimport .net-3.5 c#-3.0
我们有一个供应商,提供访问其硬件的库.不幸的是,如果您有多个设备,则需要多次导入其库,并使用不同的dll名称.因此,我们有一吨重复的代码,我担心它很快就会成为维护的噩梦.
我们现在所拥有的是:
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);
Run Code Online (Sandbox Code Playgroud)
...
[DllImport(DLL_NAME, EntryPoint = "_function99")]
public static extern int Function99(int param);
}
Run Code Online (Sandbox Code Playgroud)
....
public static class Device16 {
public const string DLL_NAME = @"Device16.dll";
[DllImport(DLL_NAME, EntryPoint = "_function1")]
public static extern int Function1(byte[] param);
Run Code Online (Sandbox Code Playgroud)
...
[DllImport(DLL_NAME, EntryPoint = "_function99")]
public static extern int Function99(int param);
}
}
Run Code Online (Sandbox Code Playgroud)
如果我使用的是C或C++,我只会在静态类中定义一个文件和#include它们多次,不是很好但是比替代方案好,但在C#中我没有那个选项.
如果有人对如何有效地定义工厂有任何聪明的想法,这将允许我们生成尽可能多的静态设备类,我会非常感兴趣.
谢谢,
编辑:函数原型是非常多变的,所以任何依赖它们的方法都是不合适的.感谢到目前为止的建议,我并没有如此迅速地阐述这么多想法.
Abe*_*bel 14
只是一些考虑:
编辑:这种方法需要改变编译方法,这很难,需要注入,装配修改或AOP-land中常用的其他方法.考虑下面的方法二,这更容易.
GetIlAsByteArray创建您的动态方法DllImport方法编辑:这种替代方法起初似乎有点涉及,但有人已经为你做了工作.查看这篇优秀的CodeProject文章,只需下载并使用其代码即可动态创建DllImport样式方法.基本上,它归结为:
LoadLibrary或LoadLibraryEx使用dllimport API函数执行"手动"DllImportMethodBuilder.编辑:进一步观察,有一个更简单的方法:只需使用DefinePInvokeMethod你所需要的一切.MSDN链接已经提供了一个很好的示例,但是在此CodeProject文章中提供了可以基于DLL和函数名创建任何本机DLL的完整包装器.
DefinePInvokeMethod以下是这种方法在代码中的外观,您可以根据需要重用返回的委托,每个方法只需要进行一次动态方法的昂贵构建.
编辑:更新代码示例以使用任何委托,并自动反映委托签名中的正确返回类型和参数类型.通过这种方式,我们将实现与签名完全分离,根据您当前的情况,我们可以做到最好.优点:您具有类型安全性和单点变化,这意味着:非常容易管理.
// 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());
Run Code Online (Sandbox Code Playgroud)
我想其他方法也是可能的(就像这个帖子中其他人提到的模板方法一样).
更新:添加了一个指向优秀代码项目文章的链接.
更新:增加了第三种更容易的方法.
更新:添加代码示例
更新:更新的代码示例与任何函数原型无缝协作
更新:修复可怕的错误:typeof(Function02)应该是typeof(T)当然的
使用T4(文本模板转换工具包)怎么样?创建一个包含以下内容的 .tt 文件:
<#@ template language="C#" #>
using System.Runtime.InteropServices;
namespace MyNamespace {
<# foreach(string deviceName in DeviceNames) { #>
public static class <#= deviceName #>
{
public const string DLL_NAME = @"<#= deviceName #>.dll";
<# foreach(string functionName in FunctionNames) { #>
[DllImport(DLL_NAME, EntryPoint = "<#= functionName #>")]
public static extern int <#= functionName.Substring(1) #>(byte[] param);
<# } #>
}
<# } #>
}
<#+
string[] DeviceNames = new string[] { "Device01", "Device02", "Device03" };
string[] FunctionNames = new string[] { "_function1", "_function2", "_function3" };
#>
Run Code Online (Sandbox Code Playgroud)
Visual Studio 然后会将其转换为:
using System.Runtime.InteropServices;
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 = "_function2")]
public static extern int function2(byte[] param);
[DllImport(DLL_NAME, EntryPoint = "_function3")]
public static extern int function3(byte[] param);
}
public static class Device02
{
public const string DLL_NAME = @"Device02.dll";
[DllImport(DLL_NAME, EntryPoint = "_function1")]
public static extern int function1(byte[] param);
[DllImport(DLL_NAME, EntryPoint = "_function2")]
public static extern int function2(byte[] param);
[DllImport(DLL_NAME, EntryPoint = "_function3")]
public static extern int function3(byte[] param);
}
public static class Device03
{
public const string DLL_NAME = @"Device03.dll";
[DllImport(DLL_NAME, EntryPoint = "_function1")]
public static extern int function1(byte[] param);
[DllImport(DLL_NAME, EntryPoint = "_function2")]
public static extern int function2(byte[] param);
[DllImport(DLL_NAME, EntryPoint = "_function3")]
public static extern int function3(byte[] param);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5562 次 |
| 最近记录: |