C#动态加载第三方非托管DLL,LoadLibraryEx,LoadLibrary

C#动态加载第三方DLL

当我们需要加载第三方非托管DLL时,通常会直接使用DllImport的方式,代码如下:

  1. [DllImport("GetFile.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]

  2. static extern string GetFileData(string fileName);

上图的调用方式,默认GetFile.dll文件位于与调用程序(.exe文件)相同的目录中(这里不考虑System32目录、环境变量目录,因为通常情况下,不会将第三方DLL放到这些目录中)。

如果不想将DLL放到exe所在目录,那也可以手动指定DLL文件路径,代码如下:

  1. [DllImport("C:\\Customer\\GetFile.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]

  2. static extern string GetFileData(string fileName);

更进一步,如果此时我们想动态指定DLL文件路径,则以上方式将无法应对,原因是DllImport中的DLL文件路径必须是常量。

为了动态调用DLL,我们需要通过其它方式,具体代码如下。这里我们定义了一个DllInvoke类,其中用到了LoadLibrary()函数,通过该函数导入DLL(如上文中的GetFile.dll)文件,然后再通过GetProcAddress()函数获取DLL中欲使用的API(上文中为GetFileData())的指针,最终通过Marshal.GetDelegateForFunctionPointer()函数返回API对应的委托。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;class DllInvoke
{[DllImport("kernel32.dll", SetLastError = true)]private static extern IntPtr LoadLibrary(string lpFileName);[DllImport("kernel32.dll")]private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);[DllImport("kernel32.dll")]private extern static bool FreeLibrary(IntPtr lib);private IntPtr hLib;public DllInvoke(String DllName){hLib = LoadLibrary(DllName);if (hLib == IntPtr.Zero){var err = Marshal.GetLastWin32Error(); //只有SetLastError = true时,才能获取到Error Code}}~DllInvoke(){FreeLibrary(hLib);}//将要执行的函数转换为委托public Delegate Invoke(String ApiName, Type t){IntPtr api = GetProcAddress(hLib, ApiName);return (Delegate)Marshal.GetDelegateForFunctionPointer(api, t);}
}

另外,有个问题需要注意。当被调用的DLL,它自身也在调用其它DLL时,此时使用上文中的LoadLibrary()函数时,会有值为126的Error Code(通过Marshal.GetLastWin32Error()函数获取Error Code,126表示找不到指定的模块)。之所以会有此报错,是因为对于被调用DLL自身调用的“其它DLL”,会按照和DllImport同样的顺序(即exe所在目录、System32目录、环境变量目录)去寻找它们,而这些“其它DLL”你可能放在了和被调用DLL同样的目录中,而没有放在exe所在目录、System32目录、环境变量目录中,所以显然是找不到的。

如果我就是想把所有的DLL(不论是被调用的DLL,还是被调用DLL自身调用的其它DLL)都放在一个我自己指定的目录中呢?此时我们可以使用LoadLibraryEx()函数,通过该函数导入的DLL,如果它本身也调用了其它DLL的话,会强制先在被调用DLL所在的目录中查找它调用的“其它DLL”。使用LoadLibraryEx()函数的完整代码如下,新的DllInvoke类中,定义了一个LoadLibraryFlags枚举变量,其中的LOAD_WITH_ALTERED_SEARCH_PATH值会被传入LoadLibraryEx()函数中,从而强制在DLL所在目录中查找。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;class DllInvoke
{/// <summary>/// LoadLibraryFlags/// </summary>public enum LoadLibraryFlags : uint{DONT_RESOLVE_DLL_REFERENCES = 0x00000001,LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,LOAD_LIBRARY_AS_DATAFILE = 0x00000002,LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008}[DllImport("kernel32.dll", SetLastError = true)]private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);[DllImport("kernel32.dll")]private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);[DllImport("kernel32.dll")]private extern static bool FreeLibrary(IntPtr lib);private IntPtr hLib;public DllInvoke(String DllName){hLib = LoadLibraryEx(DllName, IntPtr.Zero, LoadLibraryFlags.LOAD_WITH_ALTERED_SEARCH_PATH);if (hLib == IntPtr.Zero){var err = Marshal.GetLastWin32Error(); //只有SetLastError = true时,才能获取到Error Code}}~DllInvoke(){FreeLibrary(hLib);}//将要执行的函数转换为委托public Delegate Invoke(String ApiName, Type t){IntPtr api = GetProcAddress(hLib, ApiName);return (Delegate)Marshal.GetDelegateForFunctionPointer(api, t);}
}

如何使用以上定义的DllInvoke类呢?具体代码如下,首先定义一个委托CustomerAPI(该委托的形参列表需要和待调用的DLL中的API一致),然后即可用类似下图Example()函数中的代码,进行API的调用。

public delegate int CustomerAPI(string fileName);
private void Example()
{string dllName = "C:\\Customer\\GetFile.dll";DllInvoke customerDll = new DllInvoke(dllName);CustomerAPI GetFileData = (CustomerAPI)customerDll.Invoke("GetFileData", typeof(CustomerAPI));string fileName = "C:\\Customer\\ExampleFile.txt";int data = GetFileData(fileName);MessageBox.Show("The data got from file is: " + data.ToString());
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/233311.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【华为OD题库-095】字符串划分-Java

题目 给定一个小写字母组成的字符串S&#xff0c;请找出字符串中两个不同位置的字符作为分割点&#xff0c;使得字符串分成三个连续子串且子串权重相等&#xff0c;注意子串不包含分割点。 若能找到满足条件的两个分割点&#xff0c;请输出这两个分割点在字符串中的位置下标&am…

Python+pip下载与安装

Hi, I’m Shendi Pythonpip下载与安装 最近有识别图片中物体的需求&#xff0c;于是选用了TensorFlow&#xff0c;在一番考虑下&#xff0c;还是选择直接使用Python。 Python下载安装 直接在搜索引擎搜索Python或通过 https://www.python.org 进入官网 在 Downloads 处点击 Al…

Gin之GORM的表关联查询操作详解

前期工作&#xff1a; 先查看下要操作的两张表&#xff1a; carton carton_cate //关系如下&#xff1a; // 一个章节对应一个动漫&#xff08;一对一&#xff1b;两种方法&#xff1a;belong to&#xff1b;has one&#xff09; // 一个动漫可以对应多个章节&#xff08;一…

Flink-状态后端

状态后端是一个“开箱即用”的组件&#xff0c;可以在不改变应用程序逻辑的情况下独立配置。 Flink中提供了两类不同的状态后端&#xff0c;一种是“哈希表状态后端”&#xff08;HashMapStateBackend&#xff09;&#xff0c;另一种是“内嵌RocksDB状态后端”&#xff08;Embe…

42道CSS高频题整理(附答案背诵版)

1、简述CSS3选择器优先级及计算&#xff1f; CSS的选择器优先级是一个相对复杂的概念&#xff0c;它规定了在一组样式冲突时&#xff0c;哪些样式将被浏览器采纳。选择器优先级是通过一个四位的值来计算的&#xff0c;形式为&#xff1a;[内联样式, ID选择器, 类选择器/属性选…

Eclipse_01_如何设置代码文件背景颜色为护眼沙绿色

设置方法 Window --> Preference 参考文档 参考文档 1

uni-app ucharts中饼图与圆环图区别

项目情况&#xff1a; uni-app的用于移动端H5项目&#xff0c;包使用uni_modules目录存放。 图表引用ucharts中的echarts配置的组件方式 区别1 饼图与圆环图在echarts使用的配置都是pie类型。但是配置raduis使用&#xff1a; radius: [40%, 70%] 区别2 组件type指明&#xf…

Linux 非阻塞网络IO模式

非阻塞网络IO模式介绍 当用户线程发起一个 read 操作后&#xff0c;并不需要等待&#xff0c;而是马上就得到了一个结果。如果结果是一个 error 时&#xff0c;它就知道数据还没有准备好&#xff0c;于是它可以再次发送 read 操作。一旦内核中的数据准备好了&#xff0c;并且又…

修复泰坦陨落2缺少msvcr120.dll的5种方法,亲测有效

游戏《泰坦陨落2》缺少msvcr120.dll的问题困扰着许多玩家。这个问题的主要原因可能是系统环境不完整、软件或游戏版本不匹配、DLL文件丢失或损坏以及杀毒软件误判等。msvcr120.dll是Microsoft Visual C 2013 Redistributable的一个组件&#xff0c;它包含了许多运行库文件&…

Linux——安装ESMF和ESMPY

1、下载esmf git clone https://githubfast.com/esmf-org/esmf.git2、安装 2.1 指定文件路径、安装路径和编译器 2.1.1Gfortran编译器 export ESMF_DIR/home/yuh/ESMF export ESMF_INSTALL_PREFIX/home/yuh/ESMF/esmf export ESMF_COMMmpiuni export ESMF_COMPILERgfortran…

BMS专有名词英文总称

1、BMS : battery management system 电池管理系统 2、EMS : energy management system 能源管理系统 3、PCS : power conversion system 储能变流器 4、SOC : state of charge 剩余容量 5、SOH : state of healthy 电池健康状态 6、E…

【百度PARL】强化学习笔记

文章目录 强化学习基本知识一些框架Value-based的方法Q表格举个例子 强化的概念TD更新 Sarsa算法SampleSarsa Agent类 On_policy vs off_policy函数逼近与神经网络DQN算法DQN创新点DQN代码实现model.pyalgorithm.pyagent.py总结&#xff1a;举个例子 实战 视频&#xff1a;世界…

elasticsearch|大数据|低版本的elasticsearch集群的官方安全插件x-pack的详解

前言&#xff1a; elasticsearch集群说实话是比较好部署的&#xff0c;也就是从开箱即用这方面来说&#xff0c;是比较简单的&#xff0c;大体步骤就是首先处理好集群的环境&#xff0c;比如时间服务器&#xff0c;域名映射&#xff0c;内核层面的文件打开数这些小问题&#x…

力扣刷题日常(一)

136.只出现一次的数字 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间 示例 1 &#xf…

Js中浅拷贝和深拷贝有什么区别,如何实现?

在 JavaScript 中&#xff0c;浅拷贝&#xff08;Shallow Copy&#xff09;和深拷贝&#xff08;Deep Copy&#xff09;是两种常见的数据拷贝方式&#xff0c;它们的区别在于拷贝的程度。 浅拷贝&#xff1a;浅拷贝仅拷贝对象或数组的引用&#xff0c;而不是拷贝其内部的值。这…

苏宁开放平台API接口全攻略:掌握电商数据,提升业务效率

一、概述 苏宁平台提供了丰富的API接口&#xff0c;用于开发人员与平台进行交互&#xff0c;实现各种功能。本文将介绍苏宁平台API接口的基本概念、使用方法和注意事项&#xff0c;帮助开发人员更好地利用这些接口&#xff0c;提高开发效率和质量。 二、API接口介绍 商品详情…

html之CSS的高级选择器应用

文章目录 一、CSS高级选择器有哪些呢&#xff1f;二、高级选择器的应用1、层次选择器后代选择器子选择器相邻兄弟选择器通用兄弟选择器 2、结构伪类选择器&#xff08;不常用&#xff09;3、属性选择器E[attr]E[attrval]E[attr^val]E[attr$val]E[attr*val] 一、CSS高级选择器有…

Golang中方法定义及使用

一、介绍 方法是与特定类型关联的函数。它允许在类型上执行操作&#xff0c;并可以访问和修改该类型的字段。方法的定义包括接收者类型、方法名和参数列表。接收者类型可以是结构体、接口或其他自定义类型。 方法的接收者可以是值或指针类型。如果接收者是值类型&#xff0c;…

excel该如何实现生成条形码/二维码?

如何在Excel中制作条形码/二维码&#xff1f; 1.首先&#xff0c;打开电脑上的Excel。进入后&#xff0c;在上方菜单栏中找到并点击“开发工具”。如果没有找到开发工具&#xff0c;就先点击“文件”&#xff0c;在弹出菜单中再点击“选项”。 2.打开Excel选项窗口后&#xff0…

【Flink-cdc-Mysql-To-Kafka】使用 Flinksql 利用集成的 connector 实现 Mysql 数据写入 Kafka

【Flink-cdc-Mysql-To-Kafka】使用 Flinksql 利用集成的 connector 实现 Mysql 数据写入 Kafka 1&#xff09;环境准备2&#xff09;准备相关 jar 包3&#xff09;实现场景4&#xff09;准备工作4.1.Mysql4.2.Kafka 5&#xff09;Flink-Sql6&#xff09;验证 1&#xff09;环境…