参数化 DllImport 以在 C# 应用程序中使用

2023-11-24

我们有一家供应商提供了一个库来访问他们的硬件。不幸的是,如果您有多个设备,则需要使用不同的 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);

...

        [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++,我只需在一个文件中定义函数,然后在静态类中多次 #include 它们,虽然不漂亮,但比替代方案更好,但在 C# 中我没有这个选项。

如果有人对如何有效地定义一个工厂有任何聪明的想法,这将使我们能够根据需要生成尽可能多的静态设备类,我会非常感兴趣。

Thanks,

编辑:函数原型差异很大,因此任何依赖它们相同的方法都是不合适的。感谢到目前为止的建议,我并没有那么快地提出这么多想法。


只是一些考虑因素:

替代方案#one

编辑:这种方法需要更改编译方法,这很困难,并且需要注入、程序集修改或 AOP 领域常用的其他方法。考虑下面的方法二,这是更容易的。

  1. 删除所有具有相同签名的函数,保留每个函数一个
  2. Use GetIlAsByteArray创建您的动态方法DllImport method
  3. Use 这里描述的技术要操作函数的 IL,您可以在此处更改 DllImport 属性等。
  4. 创建这些函数的委托并缓存您的调用
  5. 返回委托

替代方案#二:

编辑:这种替代方法一开始似乎有点复杂,但有人已经为你完成了这项工作。抬头这篇优秀的 CodeProject 文章只需下载并使用其代码即可动态创建 DllImport 风格的方法。基本上,它可以归结为:

  1. 删除所有 DllImport
  2. 创建您自己的 DllImport 包装器:采用 dll 名称和函数名称(假设所有签名都相同)
  3. 包装器执行“手动”DllImportLoadLibrary or LoadLibraryEx使用 dllimport API 函数
  4. 包装器为您创建一个方法MethodBuilder.
  5. 返回该方法的委托,您可以将其用作函数。

替代方案#三

编辑:进一步看,有一个更简单的方法:只需使用DefinePInvokeMethod这满足了你所需要的一切。 MSDN 链接已经给出了一个很好的示例,但提供了一个完整的包装器,可以根据 DLL 和函数名称创建任何本机 DLL这篇 CodeProject 文章.

  1. 删除所有 DllImport 样式签名
  2. 创建一个简单的包装方法DefinePInvokeMethod
  3. 确保添加简单的缓存(字典?)以防止在每次调用时构建整个方法
  4. 从包装器返回一个委托。

下面是这种方法在代码中的样子,您可以根据需要重用返回的委托,每个方法只应完成一次昂贵的动态方法构建。

编辑:更新了代码示例以与任何委托一起使用,并自动反映委托签名中的正确返回类型和参数类型。通过这种方式,我们将实现与签名完全解耦,考虑到您当前的情况,这是我们能做的最好的事情。优点:具有类型安全性和单点更改,这意味着:非常容易管理。

// 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());

我想其他方法也是可能的(就像本线程中其他人提到的模板方法)。

Update:添加了一个链接优秀的代码项目文章.
Update:添加了第三种更简单的方法。
Update:添加了代码示例
Update:更新的代码示例可与任何函数原型无缝协作
Update:修复了可怕的错误:typeof(Function02)应该typeof(T)当然

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

参数化 DllImport 以在 C# 应用程序中使用 的相关文章

  • IIS应用程序池回收+quartz调度

    我正在 IIS 7 5 上运行一个 Web 应用程序 它需要偶尔回收 否则内存使用情况会失控 这是我正在研究的问题 当它回收时 它实际上不会运行 直到另一个请求到来 而quartz不会运行 有没有办法让IIS在回收应用程序池后立即自动启动1
  • 将 new 与 decltype 一起使用

    T t T is an implementation detail t new T want to avoid naming T to allow for flexibility t new decltype t error cannot
  • 在 OnModelCreating 期间设置列名称

    Issue 我目前正在尝试通过设置的属性为我的表及其列添加前缀 我正在使用实体框架核心 我已经正确地为表名添加了前缀 但我似乎无法弄清楚列的前缀 我有一种感觉 我需要使用反射 我已经留下了我的 可能很糟糕的 反思尝试 有人有办法在实体中设置
  • 删除是如何工作的? [复制]

    这个问题在这里已经有答案了 可能的重复 C 编程 free 如何知道要释放多少 https stackoverflow com questions 1518711 c programming how does free know how m
  • 检测wlan是否关闭

    任何人都可以给我一个提示 如何在 Windows Phone 上以编程方式检测 C 8 1 应用程序 不是 8 0 是否启用 禁用 WLAN 我不想更改这些设置 只是需要知道 该解决方案是一个 Windows 8 1 通用应用程序 Wind
  • 解析 JWT 令牌以仅获取有效负载内容,无需 C# 或 Blazor 中的外部库

    我正在使用 Blazor 编写可以访问 JWT 的客户端应用程序 我想知道一种简单的方法来读取令牌有效负载内容而不添加额外的依赖项 因为我不需要其他信息 也不需要验证令牌 我认为解析有效负载内容应该足够简单 只需将其写入方法即可 JwtTo
  • 检测到堆栈崩溃

    我正在执行我的 a out 文件 执行后 程序运行一段时间 然后退出并显示消息 stack smashing detected a out terminated Backtrace lib tls i686 cmov libc so 6 f
  • MFC:如何设置CEdit框的焦点?

    我正在开发我的第一个简单的 MFC 项目 但我正在努力解决一个问题 想要设置所有的焦点CEdit其中一个对话框中的框 我的想法是 当打开对话框时 焦点位于第一个编辑框上 然后使用 选项卡 在它们之间交换 我看到了方法SetFocus 但我无
  • 将接口转换为其具体实现对象,反之亦然?

    在 C 中 当我有一个接口和几个具体实现时 我可以将接口强制转换为具体类型 还是将具体类型强制转换为接口 这种情况下的规则是什么 Java 和 C 中都允许这两个方向 向下转型需要显式转型 如果对象类型不正确 可能会抛出异常 然而 向上转换
  • 使用 C# 和 wpf 创建类似 Dock 的应用程序

    我需要创建一个与我们购买笔记本电脑时获得的应用程序类似的应用程序 仅当鼠标指针到达窗口顶部时它才可见 那么我怎样才能使用 C 4 0 来做到这一点呢 http www notebookcheck net uploads pics win2
  • 如何对 NServiceBus.Configure.WithWeb() 进行单元测试?

    我正在构建一个 WCF 服务 该服务接收外部 IP 上的请求并将其转换为通过 NServiceBus 发送的消息 我的单元测试之一调用Global Application Start 它执行应用程序的配置 然后尝试将 Web 服务解析为 验
  • 析构函数中的异步操作

    尝试在类析构函数中运行异步操作失败 这是代码 public class Executor public static void Main var c1 new Class1 c1 DoSomething public class Class
  • 在 asp.net MVC 中使用活动目录进行身份验证

    我想使用活动目录对我的 asp net mvc 项目中的用户进行身份验证 在网上冲浪了几个小时后 我没有找到任何对我有用的东西 我已经看到了所有结果 但什么也没有 我尝试按照许多帖子的建议编辑我的 web config 如果有人可以帮助我提
  • 从BackgroundWorker线程更新图像UI属性

    在我正在编写的 WPF 应用程序中 我有一个 TransformedBitmap 属性 该属性绑定到 UI 上的 Image 对象 每当我更改此属性时 图像就会更新 因此显示在屏幕上的图像也会更新 为了防止在检索下一张图像时 UI 冻结或变
  • 逆向工程 ASP.NET Web 应用程序

    我有一个 ASP NET Web 应用程序 我没有源代码 该 bin 包含 10 个程序集和一个 compiled 文件 我在 App Code dll 上使用 Reflector 它向我显示了类和命名空间之类的东西 但它太混乱了 有没有什
  • 选择查询不适用于使用Parameters.AddWithValue 的参数

    C 中的以下查询不起作用 但我看不出问题所在 string Getquery select from user tbl where emp id emp id and birthdate birthdate cmdR Parameters
  • 使用 jQuery 从 ASP.Net JSON 服务获取数据

    我正在尝试调用 Google 地图地理编码 API 从纬度 经度对中获取格式化的地址 然后将其记录到控制台 我正在尝试获取为给定位置返回的第一个 formatted address 项目 我很简单无法从 JSON 中提取该项目 我不知道为什
  • 如何在 winforms 应用程序的主屏幕显示之前显示欢迎屏幕?

    我想在应用程序启动时加载欢迎屏幕 然后用户单击欢迎屏幕上的按钮 然后关闭欢迎屏幕 最后显示主屏幕 static void Main startup method being called Application EnableVisualSt
  • DataContractSerializer 事件/委托字段问题

    在我的 WPF 应用程序中 我正在使用DataContractSerializer序列化对象 我发现它无法序列化具有事件或委托声明的类型 考虑以下失败的代码 Serializable public abstract class BaseCl
  • 结构化绑定的用例有哪些?

    C 17 标准引入了新的结构化绑定 http en cppreference com w cpp language structured binding功能 最初是proposed http www open std org jtc1 sc

随机推荐

  • Span 文本更改上的 MutationObserver 不会触发

    这只是一个简单的例子 而不是实际情况 MutationObserver 仍然没有触发 所以我对其工作原理的假设是错误的 JSFiddle function var editButtonVisibility function console
  • 图像上缩进的透明箭头/三角形

    我想做一个图像上的透明箭头 该三角形应在半透明块中缩进并显示背景图像 期望的输出 barShow background color 000 opacity 0 5 barShow before top 0 left 50 border so
  • 如何在 Markdown 中的文本上应用颜色

    我想使用 Markdown 来存储文本信息 但快速谷歌搜索显示 Markdown 不支持颜色 另外 Stack Overflow 不支持颜色 与 GitHub markdown 的情况相同 有没有允许彩色文本的 Markdown 风格 TL
  • TypeError:只有长度为 1 的数组可以使用 NUMPY 转换为 Python 标量

    coding utf 8 import matplotlib pyplot as plt import numpy as np import math task 2e x np linspace 0 0001 0 1 50 constant
  • .rpt设计时如何使水晶报表页面宽度变大

    我正在寻找一种调整页面宽度大小的方法 Crystal Reports 使我可以将字段放置在 rpt 文件中 到目前为止 我发现如果右键单击报告 选择 设计 打印机设置 我可以选择要打印报告的纸张尺寸 如果我选择 方向 横向 我可以增加页面的
  • 使用 prism.js 突出显示 html

    我似乎无法使用 prism js 突出显示 html 因为它删除了仅打印文本的标记 pre 标签内的以下代码仅显示为文本 我将 代码 标签的类设置为 语言标记 table class data table tr td Title td td
  • 将 foreach 与 LINQ to Entities 结合使用时是否需要 ToList

    我有一个关于 LINQ 如何与foreach通过实体框架中的可查询实体 做哪一个更好 更快 foreach var thing in myentities GetThemAll or foreach var thing in myentit
  • 重定向根 URL - IIS 7

    我的网站上安装了一些论坛http ironnoob com forums 我的网站位于 c IronNoob 我的论坛安装在 c IronNoob forums 我希望 ironnoob com 和 www ironnoob com 都进行
  • 如何在docker容器中伪造cpu架构?

    当我创建 32 位 CentOS 5 docker 映像时 我希望 cpu 架构报告为 i386 i686 安装程序在此容器检查架构中进行测试并安装 64 位二进制文 件而不是 32 位 我设置了 yum 变量并创建了 uname 包装器
  • 批处理文件中删除特定扩展名的文件

    我想递归删除批处理文件中具有特定扩展名的所有文件 我知道以下命令 del s ext 但是 这在 Windows 上也会删除具有其他扩展名的文件 例如 ext1 或 ext2 其原因似乎是此类文件的 8 3 文件名以 ext 结尾 因此扩展
  • 实体框架 EF.Functions.Like 与 string.Contains

    我正在阅读实体框架核心2 0的公告https blogs msdn microsoft com dotnet 2017 08 14 announcing entity framework core 2 0 它说他们添加了新的 Sql 函数
  • 下拉 onchange 调用 PHP 函数

    我尝试使用下面的代码从下拉菜单中调用 PHP 函数 有没有一种干净的方法可以做到这一点 code section section
  • AngularJs 单元测试 - 嘲笑承诺不执行“然后”

    我们正在对控制器进行单元测试 我们已经成功模拟了对 REST 服务层的调用 并验证了它确实是使用给定的数据进行调用的 然而现在我们想在我们的控制器中测试then承诺改变location path 控制器 function app contr
  • Python字典迭代器性能

    在 Python 中使用字典时 这一页表示迭代字典元素的时间复杂度是O n where n是字典的最大尺寸 但是 我认为没有一种明显的方法可以迭代哈希表的元素 我可以假设性能良好吗dict iteritems 当迭代哈希表的元素时 没有太多
  • Alamofire POST 请求有进度

    我正在使用 Alamofire 执行 POST 请求 由于此 POST 请求可能需要一段时间 并且我想跟踪进度并将其显示为 ProgressView Alamofire request POST ApiLink create post pa
  • 在一台几乎相同的机器上获取“ld:未找到架构 x86_64 clang 的符号”

    我已经研究了两天 搜索了互联网的各个角落 但找不到任何解决方案 目前 当我尝试在 M1 Max 上构建 React Native Xcode 项目时 我得到ld symbol s not found for architecture x86
  • git中的文件和目录被修改后如何恢复权限?

    我有一个 git 结帐 所有文件权限都与 git 认为应有的权限不同 因此它们都显示为已修改 在不触及文件内容的情况下 只想修改权限 如何将所有文件权限设置为 git 认为应该的权限 Git 会跟踪文件权限并在使用创建补丁时公开权限更改gi
  • 是否有相当于 Stata 的 egen 函数? [复制]

    这个问题在这里已经有答案了 Stata 有一个非常好的命令 egen 这使得计算观察组的统计数据变得容易 例如 可以计算每个组的最大值 平均值和最小值 并将它们作为变量添加到详细数据集中 Stata 命令是一行代码 by group ege
  • 两个短整数的异或

    我正在计算XOR of two short integers using XOR 操作员以传统方式 下面是方法 short a 197 short b 341 short y short a b 然而 异或总是返回整数 但在我的例子中 输入
  • 参数化 DllImport 以在 C# 应用程序中使用

    我们有一家供应商提供了一个库来访问他们的硬件 不幸的是 如果您有多个设备 则需要使用不同的 dll 名称多次导入它们的库 因此 我们有大量的重复代码 我担心它很快就会成为维护的噩梦 我们现在拥有的是这样的 namespace MyNames