C#总结(七)动态加载C++动态链接库

Wesley13
• 阅读 835

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++写的代码了。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
VC++动态链接库(DLL)编程深入浅出(一)
1.概论  先来阐述一下DLL(DynamicLinkableLibrary)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。  静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直
Stella981 Stella981
3年前
Nepxion Discovery 5.5.0 发布
!(https://oscimg.oschina.net/oscnet/f81c043194ef4732880459d00c1a720e.png)发布日志功能更新:增加基于Opentracing调用链的支持,目前支持UberJaeger,实现在SpringCloudGateway、Zuul和服务上的灰度
Stella981 Stella981
3年前
QT开发——动态库(.so文件)的生成与调用
1.qmake方式生成和调用动态库链接:https://blog.csdn.net/lywzgzl/article/details/428059912.cmake方式生成和调用动态库2.1创建共享库项目Cmake新建一个QtCreator项目,在C
Stella981 Stella981
3年前
C# 调用 Delphi DLL
l技术实现如何逐步实现动态库的加载,类型的匹配,动态链接库函数导出的定义,参考下面宏定义即可:defineLIBEXPORT_APIextern"C"__declspec(dllexport)第一步,我先从简单的调用出发,定义了一个简单的函数,该函数仅仅实现一
Wesley13 Wesley13
3年前
2014百度校园招聘软件研发类笔试(深圳)
时间:2013年9月28日。地点:深圳职位:软件研发问答题:(10\3)1.动态链接库(DLL)与静态链接库(lib)的优缺点:静态链接库和动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都全部被直接包含在最终的EXE文件中,若使用DLL,该DLL不必被包含在最终的EXE中,EXE文件
Wesley13 Wesley13
3年前
C++创建动态库C#调用(二)
前言上一篇《C创建动态库C调用(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzA4Nzk0NTU0Nw%3D%3D%26mid%3D2247486189%26idx%3D1%26sn%3D46a4d0
Stella981 Stella981
3年前
Python调用C语言函数
在C调用Python模块时需要初始化Python解释器,导入模块等但Python调用C模块却比较简单,ctypes模块提供了和C语言兼容的数据类型和函数来加载dll或so文件,因此在调用时不需对源文件做任何的修改1.编写C语言代码,可参考https://www.cnblogs.com/zhouzhishuai/p/9529487.html(ht
Wesley13 Wesley13
3年前
C语言调用C++动态链接库
C调用C接口,这个问题的解决和C调用C动态链接库(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fzy010101.blog.csdn.net%2Farticle%2Fdetails%2F109441831)是类似的。话不多说,直接上例子来进行说明。helloworld\_c