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

  • 相关阅读:
    perl 解json数组
    华为云3大体系化防护实践,保障金融业云上数据安全
    弹性文件服务解密 -- 块存储、文件存储、对象存储的区别
    【nodejs原理&源码赏析(6)】深度剖析cluster模块源码与node.js多进程
    云+AI+5G时代,华为云已准备好多元化云服务架构
    高能街访 | 为什么他们都纷纷为深圳打Call?
    Angularjs进阶笔记(2)—自定义指令中的数据绑定
    Angularjs进阶笔记(1)—不同类型的双向数据绑定
    ServiceComb java-chassis和CSE java-chassis的区别
    使用inspector功能查看和管理契约
  • 原文地址:https://www.cnblogs.com/chinasoft/p/15925882.html
Copyright © 2020-2023  润新知