C#調用C++ 連接庫的方式分爲靜態調用和動態調用這兩種方式。靜態調用以前的文章裏面都有介紹,使用.net 提供的DllImport 導入相關的C++ 庫便可。請看以前的文章,https://www.cnblogs.com/zhangweizhong/p/8119340.html 。html
今天介紹動態調用的方法。不少時候,Dll庫的目錄多是變化的,或是有些場景,須要根據具體的狀況,來動態加載這些Dll庫。這樣使用靜態調用的方式就很不方便,C#中咱們常常經過配置動態的調用託管Dll,那麼是否是也能夠這樣動態調用C++動態連接呢?
只要經過LoadLibrary, GetProcess, FreeLibrary這幾個函數是能夠動態調用動態連接的(它們包含在kernel32.dll中)。app
LoadLibrary ( string lpFileName):載入指定的動態連接庫,並將它映射到當前進程使用的地址空間。載入成功後便可訪問庫內保存的資源 , 除了LoadLibrary 方法,還有一個相似的 LoadLibraryEx 方法。
函數
GetProcAddress (int hModule, string lpProcName):GetProcAddress函數檢索指定的動態連接庫(DLL)中的輸出庫函數地址。 若是函數調用成功,返回值是DLL中的輸出函數地址。 若是函數調用失敗,返回值是NULL。調用函數GetLastError ,獲得具體的錯誤信息。ui
FreeLibrary ( int hModule) :釋放指定的動態連接庫,它們早先是用LoadLibrary API函數裝載的。spa
GetLastError() : 獲取錯誤信息.net
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#中已經不能使用指針了,以下: code
定義委託htm
UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] public delegate int Delegate_IKUSBSDK_GetVersion([In] [Out] [MarshalAs(UnmanagedType.LPArray)] byte[] version);
3. 調用函數blog
//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++寫的代碼了。
原文出處:https://www.cnblogs.com/zhangweizhong/p/10594422.html