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 == )
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 == )
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 ;
} /// <summary>
/// 释放sdk
/// </summary>
/// <param name="handle"></param>
/// <returns></returns>
public static int ReleaseSDK(int handle)
{
try
{
if (handle > )
{
LogHelper.Info($"FreeLibrary handle:{handle}");
var result = FreeLibrary(handle);
var errCode = GetLastError();
LogHelper.Info($"FreeLibrary Result:{result}, ErrorCode: {errCode}");
return ;
}
return -;
}
catch (Exception ex)
{
LogHelper.Error(ex);
return -;
}
}
} /// <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#总结(七)动态加载C++动态链接库的更多相关文章

  1. 动态链接库dll的 静态加载 与 动态加载

    dll 两种链接方式  : 动态链接和静态链接(链接亦称加载) 动态链接是指在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统 ...

  2. Java_Java中动态加载jar文件和class文件

    转自:http://blog.csdn.net/mousebaby808/article/details/31788325 概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下 ...

  3. Android 动态加载 (二) 态加载机制 案例二

    探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法 重要说明 在实践的过程中大家都会发现资源引用的问题,这里重点声明两点: 1. 资源文件是不能直接inflate的,如果简单的话直接在程序 ...

  4. Linux中mod相关的命令 内核模块化 mod相关命令都是用来动态加载内核模块/驱动程序模块

    Linux中mod相关的命令 内核模块化   mod相关命令都是用来动态加载内核模块/驱动程序模块 http://baike.baidu.com/link?url=lxiKxFvYm-UfJIxMjz ...

  5. 动态加载JS代码

    到处查资料研究js动态脚本的加载,找到以下7种方法,总有一种适合你! 首先我们需要一个被加载的js文件,我在一个固定文件夹下创建了一个package.js,打开后在里面写一个方法functionOne ...

  6. 动态库DLL加载方式-静态加载和动态加载

    静态加载: 如果你有a.dll和a.lib,两个文件都有的话可以用静态加载的方式: message函数的声明你应该知道吧,把它的声明和下面的语句写到一个头文件中 #pragma comment(lib ...

  7. js动态加载脚本

    最近公司的前端地图产品需要做一下模块划分,希望用户用到哪一块的功能再加载哪一块的模块,这样可以提高用户体验. 所以到处查资料研究js动态脚本的加载,不过真让人伤心啊!,网上几乎都是同一篇文章,4种方法 ...

  8. Android应用开发提高系列(4)——Android动态加载(上)——加载未安装APK中的类

    前言 近期做换肤功能,由于换肤程度较高,受限于平台本身,实现起来较复杂,暂时搁置了该功能,但也积累了一些经验,将分两篇文章来写这部分的内容,欢迎交流! 关键字:Android动态加载 声明 欢迎转载, ...

  9. [转载] Java中动态加载jar文件和class文件

    转载自http://blog.csdn.net/mousebaby808/article/details/31788325 概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下 ...

随机推荐

  1. Fortran输入输出与声明--xdd

    1.建议程序总体格式: program ex1. ... end progr 2.想要打出 My name is "xdd". write(*,*)" My name i ...

  2. 6. 彤哥说netty系列之Java NIO核心组件之Buffer

    --日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第六篇. 简介 上一章我们一起学习了Java NIO的核心组件Channel,它可以看作是实体与实体之间的连接,而且需要与Buffer交 ...

  3. 【数据结构】之顺序表(Java语言描述)

    之前总结过使用C语言描述的顺序表数据结构.在C语言类库中没有为我们提供顺序表的数据结构,因此我们需要自己手写,详细的有关顺序表的数据结构描述和C语言代码请见[我的这篇文章]. 在Java语言的JDK中 ...

  4. jsp html 实现隐藏输入框,点击可以取消隐藏&&弹出输入框

    jsp代码: <script language="javascript" type="text/javascript"> function chg ...

  5. 深入理解inode和硬链接和软连接和挂载点

    inode 一.inode是什么? 理解inode,要从文件储存说起. 扇区 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512字节(相当于0.5 ...

  6. 配置防盗链、访问控制Directory和FilesMatch

    5月31日任务 课程内容: 11.25 配置防盗链11.26 访问控制Directory11.27 访问控制FilesMatch扩展几种限制ip的方法 http://ask.apelearn.com/ ...

  7. Scheme实现数字电路仿真(1)——组合电路

    EDA是个很大的话题,本系列只针对其中一小部分,数字电路的仿真,叙述一点概念性的东西,并不会过于深入,这方面的内容实则是无底洞.本系列并不是真的要做EDA,按照SICP里的相关内容,采用Lisp的方言 ...

  8. 外键(foreign key)的使用及其优缺点

    如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键.由此可见,外键表示了两个关系之间的相关联系.以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表. ...

  9. 大型情感剧集Selenium:1_介绍 #华为云·寻找黑马程序员#

    学习selenium能做什么? 很多书籍.文章中是这么定义selenium的: Selenium 是开源的自动化测试工具,它主要是用于Web 应用程序的自动化测试,不只局限于此,同时支持所有基于web ...

  10. Spring源码学习笔记之bean标签属性介绍及作用

    传统的Spring项目, xml 配置bean在代码中是经常遇到, 那么在配置bean的时候,这些属性的作用是什么呢? 虽然说现在boot项目兴起,基于xml配置的少了很多, 但是如果能够了解这些标签 ...