1. 程式人生 > >C#總結(七)動態載入C++動態連結庫

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++寫的程式碼了。

&n