C#总结(七)动态加载C++动态链接库
C#调用C++ 链接库的方式分为静态调用和动态调用这两种方式。静态调用之前的文章里面都有介绍,使用.net 提供的DllImport 导入相关的C++ 库即可。请看之前的文章,https://www.cnblogs.com/zhangweizhong/p/8119340.html 。
今天介绍动态调用的方法。很多时候,Dll库的目录可能是变化的,或是有些场景,需要根据具体的情况,来动态加载这些Dll库。这样使用静态调用的方式就很不方便,C#中我们经常通过配置动态的调用托管Dll,那么是不是也可以这样动态调用C++动态链接呢?
只要通过LoadLibrary, GetProcess, FreeLibrary这几个函数是可以动态调用动态链接的(它们包含在kernel32.dll中)。
原理
LoadLibrary ( string lpFileName):载入指定的动态链接库,并将它映射到当前进程使用的地址空间。载入成功后即可访问库内保存的资源 , 除了LoadLibrary 方法,还有一个类似的 LoadLibraryEx 方法。
GetProcAddress (int hModule, string lpProcName):GetProcAddress函数检索指定的动态链接库(DLL)中的输出库函数地址。 如果函数调用成功,返回值是DLL中的输出函数地址。 如果函数调用失败,返回值是NULL。调用函数GetLastError ,得到具体的错误信息。
FreeLibrary ( int hModule) :释放指定的动态链接库,它们早先是用LoadLibrary API函数装载的。
GetLastError() : 获取错误信息
实现
1. 将kernel32中的几个方法封装成本地调用类 DLLWrapper
using System; using System.IO; using System.Runtime.InteropServices; namespace Irisking.Basic.Util { /// <summary> /// DLLWrapper /// </summary> internal class DLLWrapper { [DllImport("kernel32.dll")] private static extern uint GetLastError(); /// <summary> /// API LoadLibraryEx /// </summary> /// <param name="lpFileName"></param> /// <param name="hReservedNull"></param> /// <param name="dwFlags"></param> /// <returns></returns> [DllImport("kernel32.dll", EntryPoint = "LoadLibraryEx", SetLastError = true)] private static extern int LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags); /// <summary> /// API GetProcAddress /// </summary> /// <param name="handle"></param> /// <param name="funcname"></param> /// <returns></returns> [DllImport("Kernel32", EntryPoint = "GetProcAddress", SetLastError = true)] public static extern int GetProcAddress(int handle, string funcname); /// <summary> /// API FreeLibrary /// </summary> /// <param name="handle"></param> /// <returns></returns> [DllImport("Kernel32", EntryPoint = "FreeLibrary", SetLastError = true)] private static extern int FreeLibrary(int handle); ///<summary> /// 通过非托管函数名转换为对应的委托 , by jingzhongrong ///</summary> ///<param name="dllModule"> 通过 LoadLibrary 获得的 DLL 句柄 </param> ///<param name="functionName"> 非托管函数名 </param> ///<param name="t"> 对应的委托类型 </param> ///<returns> 委托实例,可强制转换为适当的委托类型 </returns> public static Delegate GetFunctionAddress(int dllModule, string functionName, Type t) { int address = GetProcAddress(dllModule, functionName); if (address == 0) return null; else return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t); } ///<summary> /// 将表示函数地址的 intPtr 实例转换成对应的委托 ///</summary> public static Delegate GetDelegateFromIntPtr(IntPtr address, Type t) { if (address == IntPtr.Zero) return null; else return Marshal.GetDelegateForFunctionPointer(address, t); } ///<summary> /// 将表示函数地址的 int 转换成对应的委托 ///</summary> public static Delegate GetDelegateFromIntPtr(int address, Type t) { if (address == 0) return null; else return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t); } /// <summary> /// 加载sdk /// </summary> /// <param name="lpFileName"></param> /// <returns></returns> public static int LoadSDK(string lpFileName) { if (File.Exists(lpFileName)) { var hReservedNull = IntPtr.Zero; var dwFlags = LoadLibraryFlags.LOAD_WITH_ALTERED_SEARCH_PATH; var result = LoadLibraryEx(lpFileName, hReservedNull, dwFlags); var errCode = GetLastError(); LogHelper.Info($"LoadSDK Result:{result}, ErrorCode: {errCode}"); return result; } return 0; } /// <summary> /// 释放sdk /// </summary> /// <param name="handle"></param> /// <returns></returns> public static int ReleaseSDK(int handle) { try { if (handle > 0) { LogHelper.Info($"FreeLibrary handle:{handle}"); var result = FreeLibrary(handle); var errCode = GetLastError(); LogHelper.Info($"FreeLibrary Result:{result}, ErrorCode: {errCode}"); return 0; } return -1; } catch (Exception ex) { LogHelper.Error(ex); return -1; } } } /// <summary> /// LoadLibraryFlags /// </summary> public enum LoadLibraryFlags : uint { /// <summary> /// DONT_RESOLVE_DLL_REFERENCES /// </summary> DONT_RESOLVE_DLL_REFERENCES = 0x00000001, /// <summary> /// LOAD_IGNORE_CODE_AUTHZ_LEVEL /// </summary> LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010, /// <summary> /// LOAD_LIBRARY_AS_DATAFILE /// </summary> LOAD_LIBRARY_AS_DATAFILE = 0x00000002, /// <summary> /// LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE /// </summary> LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, /// <summary> /// LOAD_LIBRARY_AS_IMAGE_RESOURCE /// </summary> LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020, /// <summary> /// LOAD_LIBRARY_SEARCH_APPLICATION_DIR /// </summary> LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200, /// <summary> /// LOAD_LIBRARY_SEARCH_DEFAULT_DIRS /// </summary> LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000, /// <summary> /// LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR /// </summary> LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100, /// <summary> /// LOAD_LIBRARY_SEARCH_SYSTEM32 /// </summary> LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800, /// <summary> /// LOAD_LIBRARY_SEARCH_USER_DIRS /// </summary> LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400, /// <summary> /// LOAD_WITH_ALTERED_SEARCH_PATH /// </summary> LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 } }
2. 使用DLLWrapper类动态读取C++Dll,获得函数指针,并且将指针封装成C#中的委托。原因很简单,C#中已经不能使用指针了,如下:
定义委托
UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] public delegate int Delegate_IKUSBSDK_GetVersion([In] [Out] [MarshalAs(UnmanagedType.LPArray)] byte[] version);
3. 调用函数
//1. 加载sdk var sdkModule = DLLWrapper.LoadSDK(_route.DeviceA_PATH); // 2. 通过handle 找到相关的函数 Delegate_IKUSBSDK_GetVersion getVersion = (Delegate_IKUSBSDK_GetVersion)DLLWrapper.GetFunctionAddress(sdkModule, "IKUSBSDK_GetVersion", typeof(Delegate_IKUSBSDK_GetVersion)); var result = getVersion(version);
最后
通过如上例子,我们可以在C#中动态或者静态的调用C++写的代码了。
相关文章
- C#中的异常处理
- excel的C#操作教程
- C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序
- [译]聊聊C#中的泛型的使用(新手勿入) Seaching TreeVIew WPF 可编辑树Ztree的使用(包括对后台数据库的增删改查) 字段和属性的区别 C# 遍历Dictionary并修改其中的Value 学习笔记——异步 程序员常说的「哈希表」是个什么鬼?
- 如何使用T-SQL备份还原数据库及c#如何调用执行? C#中索引器的作用和实现。 jquery控制元素的隐藏和显示的几种方法。 localStorage、sessionStorage用法总结 在AspNetCore中扩展Log系列 - 介绍开源类库的使用(一) span<T>之高性能字符串操作实测
- 反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑) C#中缓存的使用 C#操作redis WPF 控件库——可拖动选项卡的TabControl 【Bootstrap系列】详解Bootstrap-table AutoFac event 和delegate的分别 常见的异步方式async 和 await C# Task用法 c#源码的执行过程
- 史上最全的CSS hack方式一览 jQuery 图片轮播的代码分离 JQuery中的动画 C#中Trim()、TrimStart()、TrimEnd()的用法 marquee 标签的使用详情 js鼠标事件 js添加遮罩层 页面上通过地址栏传值时出现乱码的两种解决方法 ref和out的区别在c#中 总结
- c# 把一个匿名对象赋值给一个Object类型的变量后,怎么取这个变量? c# dynamic动态类型和匿名类 详解C# 匿名对象(匿名类型)、var、动态类型 dynamic 深入浅析C#中的var和dynamic
- C# 字符串拼接性能探索 c#中+、string.Concat、string.Format、StringBuilder.Append四种方式进行字符串拼接时的性能
- c# Linq之求和,平均值,最大值,最小值
- C#【多线程篇】直面Invoke——经典!!!
- C#【必备技能篇】通过CMD打开一个C#程序,并传入参数
- C#,普洛尼克数(Pronic Number)的算法与源代码
- C#学习笔记(2)
- C# 获取可执行文件路径的上上级目录的方法
- c#,c++,qt中多线程访问UI控件线程的问题汇总
- C#调用C++Dll封装时遇到的一系列问题 参考
- [C++]:万字超详细讲解多态以及多态的实现原理(面试的必考的c++考点)
- c# 字段成员
- 用c#开发微信 (4) 基于Senparc.Weixin框架的接收事件推送处理 (源码下载)
- Redis for Windows(C#缓存)安装和使用
- C#创建DataTable(转载)
- C#访问远程主机资源的方法,多种方式
- 《大话设计模式》C#/C++版pdf/源码下载
- C#实现一个倒计时
- c# 引用外部dll
- C#-asp.net-获取当前,相对,绝对路径
- c#中变量的作用域
- C# 读取Excel 单元格是日期格式
- 浅谈比较C#中的char 和 C++中的char 之间的区别