C#調(diào)用DLL函數(shù)方法(下)
本文的內(nèi)容接著C#調(diào)用DLL函數(shù)方法(上)。
因?yàn)镃#中使用DllImport是不能像動(dòng)態(tài)load/unload assembly那樣,所以只能借助API函數(shù)了。在kernel32.dll中,與動(dòng)態(tài)庫(kù)調(diào)用有關(guān)的函數(shù)包括[3]:
①LoadLibrary(或MFC 的AfxLoadLibrary),裝載動(dòng)態(tài)庫(kù)。
②GetProcAddress,獲取要引入的函數(shù),將符號(hào)名或標(biāo)識(shí)號(hào)轉(zhuǎn)換為DLL內(nèi)部地址。
③FreeLibrary(或MFC的AfxFreeLibrary),釋放動(dòng)態(tài)鏈接庫(kù)。
它們的原型分別是:
HMODULE LoadLibrary(LPCTSTR lpFileName);
FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);
BOOL FreeLibrary(HMODULE hModule);
現(xiàn)在,我們可以用IntPtr hModule=LoadLibrary(“Count.dll”);來(lái)獲得Dll的句柄,用IntPtr farProc=GetProcAddress(hModule,”_count@4”);來(lái)獲得函數(shù)的入口地址。
但是,知道函數(shù)的入口地址后,怎樣調(diào)用這個(gè)函數(shù)呢?因?yàn)樵贑#中是沒有函數(shù)指針的,沒有像C++那樣的函數(shù)指針調(diào)用方式來(lái)調(diào)用函數(shù),所以我們得借助其它方法。經(jīng)過(guò)研究,發(fā)現(xiàn)我們可以通過(guò)結(jié)合使用System.Reflection.Emit及System.Reflection.Assembly里的類和函數(shù)達(dá)到我們的目的。為了以后使用方便及實(shí)現(xiàn)代碼的復(fù)用,我們可以編寫一個(gè)類。
1) dld類的編寫:
1.打開項(xiàng)目“Test”,打開類視圖,右擊“Tzb”,選擇“添加”-->“類”,類名設(shè)置為“dld”,即dynamic loading dll 的每個(gè)單詞的開頭字母。
2.添加所需的命名空間及聲明參數(shù)傳遞方式枚舉:
- using System.Runtime.InteropServices; // 用DllImport 需用此命名空間
- using System.Reflection; // 使用Assembly 類需用此命名空間
- using System.Reflection.Emit; // 使用ILGenerator 需用此命名空間
3. 在namespace test中,“public class dld”的上面,添加如下代碼聲明參數(shù)傳遞方式枚舉:
- /// < summary>
- /// 參數(shù)傳遞方式枚舉,ByValue 表示值傳遞,ByRef 表示址傳遞
- /// < /summary>
- public enum ModePass
- {
- ByValue = 0x0001,
- ByRef = 0x0002
- }
4、在public class DLD中,添加如下代碼:
- public class DLD
- {
- [DllImport("kernel32.dll")]
- public static extern IntPtr LoadLibrary(string lpFileName);
- [DllImport("kernel32.dll")]
- public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProceName);
- [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]
- public static extern bool FreeLibrary(IntPtr hModule);
- /// < summary>
- /// Loadlibrary 返回的函數(shù)庫(kù)模塊的句柄
- /// < /summary>
- private IntPtr hModule = IntPtr.Zero;
- /// < summary>
- /// GetProcAddress 返回的函數(shù)指針
- /// < /summary>
- private IntPtr farProc = IntPtr.Zero;
- /// < summary>
- /// 裝載 Dll
- /// < /summary>
- /// < param name="lpFileName">DLL 文件名 < /param>
- public void LoadDll(string lpFileName)
- {
- hModule = LoadLibrary(lpFileName);
- if (hModule == IntPtr.Zero)
- throw (new Exception(" 沒有找到 :" + lpFileName + "."));
- }
- /// < summary>
- /// 獲得函數(shù)指針
- /// < /summary>
- /// < param name="lpProcName"> 調(diào)用函數(shù)的名稱 < /param>
- public void LoadFun(string lpProcName)
- { // 若函數(shù)庫(kù)模塊的句柄為空,則拋出異常
- if (hModule == IntPtr.Zero)
- throw (new Exception(" 函數(shù)庫(kù)模塊的句柄為空 , 請(qǐng)確保已進(jìn)行 LoadDll 操作 !"));
- // 取得函數(shù)指針
- farProc = GetProcAddress(hModule, lpProcName);
- // 若函數(shù)指針,則拋出異常
- if (farProc == IntPtr.Zero)
- throw (new Exception(" 沒有找到 :" + lpProcName + " 這個(gè)函數(shù)的入口點(diǎn) "));
- }
- /// < summary>
- /// 卸載 Dll
- /// < /summary>
- public void UnLoadDll()
- {
- FreeLibrary(hModule);
- hModule = IntPtr.Zero;
- farProc = IntPtr.Zero;
- }
- /// < summary>
- /// 調(diào)用所設(shè)定的函數(shù)
- /// < /summary>
- /// < param name="ObjArray_Parameter"> 實(shí)參 < /param>
- /// < param name="TypeArray_ParameterType"> 實(shí)參類型 < /param>
- /// < param name="ModePassArray_Parameter"> 實(shí)參傳送方式 < /param>
- /// < param name="Type_Return"> 返回類型 < /param>
- /// < returns> 返回所調(diào)用函數(shù)的 object< /returns>
- public object Invoke(object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return)
- {
- // 下面 3 個(gè) if 是進(jìn)行安全檢查 , 若不能通過(guò) , 則拋出異常
- if (hModule == IntPtr.Zero)
- throw (new Exception(" 函數(shù)庫(kù)模塊的句柄為空 , 請(qǐng)確保已進(jìn)行 LoadDll 操作 !"));
- if (farProc == IntPtr.Zero)
- throw (new Exception(" 函數(shù)指針為空 , 請(qǐng)確保已進(jìn)行 LoadFun 操作 !"));
- if (ObjArray_Parameter.Length != ModePassArray_Parameter.Length)
- throw (new Exception(" 參數(shù)個(gè)數(shù)及其傳遞方式的個(gè)數(shù)不匹配 ."));
- // 下面是創(chuàng)建 MyAssemblyName 對(duì)象并設(shè)置其 Name 屬性
- AssemblyName MyAssemblyName = new AssemblyName();
- MyAssemblyName.Name = "InvokeFun";
- // 生成單模塊配件
- AssemblyBuilder MyAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName, AssemblyBuilderAccess.Run);
- ModuleBuilder MyModuleBuilder = MyAssemblyBuilder.DefineDynamicModule("InvokeDll");
- // 定義要調(diào)用的方法 , 方法名為“ MyFun ”,返回類型是“ Type_Return ”參數(shù)類型是“ TypeArray_ParameterType ”
- MethodBuilder MyMethodBuilder = MyModuleBuilder.DefineGlobalMethod("MyFun", MethodAttributes.Public | MethodAttributes.Static, Type_Return, TypeArray_ParameterType);
- // 獲取一個(gè) ILGenerator ,用于發(fā)送所需的 IL
- ILGenerator IL = MyMethodBuilder.GetILGenerator();
- int i;
- for (i = 0; i < ObjArray_Parameter.Length; i++)
- {// 用循環(huán)將參數(shù)依次壓入堆棧
- switch (ModePassArray_Parameter[i])
- {
- case ModePass.ByValue:
- IL.Emit(OpCodes.Ldarg, i);
- break;
- case ModePass.ByRef:
- IL.Emit(OpCodes.Ldarga, i);
- break;
- default:
- throw (new Exception(" 第 " + (i + 1).ToString() + " 個(gè)參數(shù)沒有給定正確的傳遞方式 ."));
- }
- }
- if (IntPtr.Size == 4)
- {// 判斷處理器類型
- IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32());
- }
- else if (IntPtr.Size == 8)
- {
- IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64());
- }
- else
- {
- throw new PlatformNotSupportedException();
- }
- IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType);
- IL.Emit(OpCodes.Ret); // 返回值
- MyModuleBuilder.CreateGlobalFunctions();
- // 取得方法信息
- MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("MyFun");
- return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 調(diào)用方法,并返回其值
- }
- }
2) dld類的使用:
1.打開項(xiàng)目“Test”,向“Form1”窗體中添加一個(gè)按鈕,和一個(gè)TestBox,Name改為txRet。視圖中雙擊按鈕,在“button1_Click”方法體上面添加代碼,創(chuàng)建一個(gè)dld類實(shí)例,并進(jìn)行測(cè)試。具體如下:
- private void button1_Click(object sender, EventArgs e)
- {
- int ret = 0;
- dld myDLD = new dld();
- myDLD.LoadDll("xxx.dll");
- myDLD.LoadFun("InitSDK");
- object[] Parameters = new object[] { }; // 實(shí)參為0
- Type[] ParameterTypes = new Type[] { }; // 實(shí)參類型為int
- ModePass[] themode = new ModePass[] { }; // 傳送方式為值傳
- Type Type_Return = typeof(int); // 返回類型為int
- ret = (int)myDLD.Invoke(Parameters, ParameterTypes, themode, Type_Return);
- txRet.Text = ret.ToString();
- if (ret != 1)
- {
- MessageBox.Show("InitSDK failed !");
- }
- if (ret == 1)
- {
- MessageBox.Show("InitSDK Sucessed !");
- }
- }
其中,xxx為要測(cè)試的dll名稱,InitSDK為dll中的要測(cè)試的函數(shù)。
至此,C#調(diào)用DLL函數(shù)方法就介紹完了,希望對(duì)大家有所幫助。
【編輯推薦】