C程序?qū)崿F(xiàn)動態(tài)調(diào)用DLL的研究_第1頁
C程序?qū)崿F(xiàn)動態(tài)調(diào)用DLL的研究_第2頁
C程序?qū)崿F(xiàn)動態(tài)調(diào)用DLL的研究_第3頁
C程序?qū)崿F(xiàn)動態(tài)調(diào)用DLL的研究_第4頁
C程序?qū)崿F(xiàn)動態(tài)調(diào)用DLL的研究_第5頁
已閱讀5頁,還剩13頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、C#程序?qū)崿F(xiàn)動態(tài)調(diào)用DLL的研究一、DLL與應(yīng)用程序動態(tài)鏈接庫(也稱為DLL,即為“Dynamic Link Library”的縮寫)是Microsoft Windows最重要的組成要素之一,打開Windows系統(tǒng)文件夾,你會發(fā)現(xiàn)文件夾中有很多DLL文件,Windows就是將一些主要的系統(tǒng)功能以DLL模塊的形式實現(xiàn)。動態(tài)鏈接庫是不能直接執(zhí)行的,也不能接收消息,它只是一個獨立的文件,其中包含能被程序或其它DLL調(diào)用來完成一定操作的函數(shù)(方法。注:C#中一般稱為“方法”),但這些函數(shù)不是執(zhí)行程序本身的一部分,而是根據(jù)進(jìn)程的需要按需載入,此時才能發(fā)揮作用。DLL只有在應(yīng)用程序需要時才被系統(tǒng)加載到進(jìn)程

2、的虛擬空間中,成為調(diào)用進(jìn)程的一部分,此時該DLL也只能被該進(jìn)程的線程訪問,它的句柄可以被調(diào)用進(jìn)程所使用,而調(diào)用進(jìn)程的句柄也可以被該DLL所使用。在內(nèi)存中,一個DLL只有一個實例,且它的編制與具體的編程語言和編譯器都沒有關(guān)系,所以可以通過DLL來實現(xiàn)混合語言編程。DLL函數(shù)中的代碼所創(chuàng)建的任何對象(包括變量)都?xì)w調(diào)用它的線程或進(jìn)程所有。下面列出了當(dāng)程序使用 DLL 時提供的一些優(yōu)點:1)使用較少的資源當(dāng)多個程序使用同一個函數(shù)庫時,DLL 可以減少在磁盤和物理內(nèi)存中加載的代碼的重復(fù)量。這不僅可以大大影響在前臺運行的程序,而且可以大大影響其他在 Windows 操作系統(tǒng)上運行的程序。2)推廣模塊式體

3、系結(jié)構(gòu)DLL 有助于促進(jìn)模塊式程序的開發(fā)。這可以幫助您開發(fā)要求提供多個語言版本的大型程序或要求具有模塊式體系結(jié)構(gòu)的程序。模塊式程序的一個示例是具有多個可以在運行時動態(tài)加載的模塊的計帳程序。3)簡化部署和安裝當(dāng) DLL 中的函數(shù)需要更新或修復(fù)時,部署和安裝 DLL 不要求重新建立程序與該 DLL 的鏈接。此外,如果多個程序使用同一個 DLL,那么多個程序都將從該更新或修復(fù)中獲益。當(dāng)您使用定期更新或修復(fù)的第三方 DLL 時,此問題可能會更頻繁地出現(xiàn)。二、DLL的調(diào)用每種編程語言調(diào)用DLL的方法都不盡相同,在此只對用C#調(diào)用DLL的方法進(jìn)行介紹。首先,您需要了解什么是托管,什么是非托管。一般可以認(rèn)為

4、:非托管代碼主要是基于win 32平臺開發(fā)的DLL,activeX的組件,托管代碼是基于.net平臺開發(fā)的。如果您想深入了解托管與非托管的關(guān)系與區(qū)別,及它們的運行機(jī)制,請您自行查找資料,本文件在此不作討論。(一)調(diào)用DLL中的非托管函數(shù)一般方法首先,應(yīng)該在C#語言源程序中聲明外部方法,其基本形式是:DLLImport(“DLL文件”)修飾符 extern 返回變量類型 方法名稱 (參數(shù)列表)其中:DLL文件:包含定義外部方法的庫文件。修飾符: 訪問修飾符,除了abstract以外在聲明方法時可以使用的修飾符。返回變量類型:在DLL文件中你需調(diào)用方法的返回變量類型。方法名稱:在DLL文件中你需調(diào)

5、用方法的名稱。參數(shù)列表:在DLL文件中你需調(diào)用方法的列表。注意:需要在程序聲明中使用System.Runtime.InteropServices命名空間。DllImport只能放置在方法聲明上。DLL文件必須位于程序當(dāng)前目錄或系統(tǒng)定義的查詢路徑中(即:系統(tǒng)環(huán)境變量中Path所設(shè)置的路徑)。返回變量類型、方法名稱、參數(shù)列表一定要與DLL文件中的定義相一致。若要使用其它函數(shù)名,可以使用EntryPoint屬性設(shè)置,如:DllImport(user32.dll, EntryPoint=MessageBoxA)static extern int MsgBox(int hWnd, string msg,

6、 string caption, int type);其它可選的 DllImportAttribute 屬性:CharSet 指示用在入口點中的字符集,如:CharSet=CharSet.Ansi;SetLastError 指示方法是否保留 Win32上一錯誤,如:SetLastError=true;ExactSpelling 指示 EntryPoint 是否必須與指示的入口點的拼寫完全匹配,如:ExactSpelling=false;PreserveSig指示方法的簽名應(yīng)當(dāng)被保留還是被轉(zhuǎn)換, 如:PreserveSig=true;CallingConvention指示入口點的調(diào)用約定, 如:

7、CallingConvention= CallingConvention.Winapi;此外,關(guān)于“數(shù)據(jù)封送處理”及“封送數(shù)字和邏輯標(biāo)量”請參閱其它一些文章2。C#例子:1.啟動VS.NET,新建一個項目,項目名稱為“Tzb”,模板為“Windows 應(yīng)用程序”。2.在“工具箱”的“ Windows 窗體”項中雙擊“Button”項,向“Form1”窗體中添加一個按鈕。3.改變按鈕的屬性:Name為 “B1”,Text為 “用DllImport調(diào)用DLL彈出提示框”,并將按鈕B1調(diào)整到適當(dāng)大小,移到適當(dāng)位置。4.在類視圖中雙擊“Form1”,打開“Form1cs”代碼視圖,在“namespac

8、e Tzb”上面輸入“using System.Runtime.InteropServices;”,以導(dǎo)入該命名空間。5.在“Form1cs設(shè)計”視圖中雙擊按鈕B1,在“B1_Click”方法上面使用關(guān)鍵字 static 和 extern 聲明方法“MsgBox”,將 DllImport 屬性附加到該方法,這里我們要使用的是“user32dll”中的“MessageBoxA”函數(shù),具體代碼如下:DllImport(user32.dll, EntryPoint=MessageBoxA)static extern int MsgBox(int hWnd, string msg, string ca

9、ption, int type);然后在“B1_Click”方法體內(nèi)添加如下代碼,以調(diào)用方法“MsgBox”:MsgBox(0, 這就是用 DllImport 調(diào)用 DLL 彈出的提示框哦! , 挑戰(zhàn)杯 ,0x30);6.按“F5”運行該程序,并點擊按鈕B1,便彈出如下提示框:(二)動態(tài)裝載、調(diào)用DLL中的非托管函數(shù)在上面已經(jīng)說明了如何用DllImport調(diào)用DLL中的非托管函數(shù),但是這個是全局的函數(shù),假若DLL中的非托管函數(shù)有一個靜態(tài)變量S,每次調(diào)用這個函數(shù)的時候,靜態(tài)變量S就自動加1。結(jié)果,當(dāng)需要重新計數(shù)時,就不能得出想要的結(jié)果。下面將用例子說明:1.DLL的創(chuàng)建1)啟動Visual C+

10、 6.0;2)新建一個“Win32 Dynamic-Link Library”工程,工程名稱為“Count”;3)在“Dll kind”選擇界面中選擇“A simple dll project”;4)打開Count.cpp,添加如下代碼:/ 導(dǎo)出函數(shù),使用“ _stdcall ” 標(biāo)準(zhǔn)調(diào)用extern C _declspec(dllexport)int _stdcall count(int init); int _stdcall count(int init) /count 函數(shù),使用參數(shù) init 初始化靜態(tài)的整形變量 S ,并使 S 自加 1 后返回該值 static int S=init

11、; S+; return S; 5)按“F7”進(jìn)行編譯,得到Count.dll(在工程目錄下的Debug文件夾中)。2.用DllImport調(diào)用DLL中的count函數(shù)1)打開項目“Tzb”,向“Form1”窗體中添加一個按鈕。2)改變按鈕的屬性:Name為 “B2”,Text為 “用DllImport調(diào)用DLL中count函數(shù)”,并將按鈕B1調(diào)整到適當(dāng)大小,移到適當(dāng)位置。3)打開“Form1cs”代碼視圖,使用關(guān)鍵字 static 和 extern 聲明方法“count”,并使其具有來自 Count.dll 的導(dǎo)出函數(shù)count的實現(xiàn),代碼如下:DllImport(Count.dll)sta

12、tic extern int count(int init);4)在“Form1cs設(shè)計”視圖中雙擊按鈕B2,在“B2_Click”方法體內(nèi)添加如下代碼:MessageBox.Show( 用 DllImport 調(diào)用 DLL 中的 count 函數(shù), n 傳入的實參為 0 ,得到的結(jié)果是: +count(0).ToString(), 挑戰(zhàn)杯 );MessageBox.Show( 用 DllImport 調(diào)用 DLL 中的 count 函數(shù), n 傳入的實參為 10 ,得到的結(jié)果是: +count(10).ToString()+n 結(jié)果可不是想要的 11 哦! , 挑戰(zhàn)杯 );MessageBo

13、x.Show( 所得結(jié)果表明: n 用 DllImport 調(diào)用 DLL 中的非托管 n 函數(shù)是全局的、靜態(tài)的函數(shù)! , 挑戰(zhàn)杯 );5)把Count.dll復(fù)制到項目“Tzb”的binDebug文件夾中,按“F5”運行該程序,并點擊按鈕B2,便彈出如下三個提示框:第1個提示框顯示的是調(diào)用“count(0)”的結(jié)果,第2個提示框顯示的是調(diào)用“count(10)”的結(jié)果,由所得結(jié)果可以證明“用DllImport調(diào)用DLL中的非托管函數(shù)是全局的、靜態(tài)的函數(shù)”。所以,有時候并不能達(dá)到我們目的,因此我們需要使用下面所介紹的方法:C#動態(tài)調(diào)用DLL中的函數(shù)。3.C#動態(tài)調(diào)用DLL中的函數(shù) 因為C#中使用

14、DllImport是不能像動態(tài)load/unload assembly那樣,所以只能借助API函數(shù)了。在kernel32.dll中,與動態(tài)庫調(diào)用有關(guān)的函數(shù)包括:LoadLibrary(或MFC 的AfxLoadLibrary),裝載動態(tài)庫。GetProcAddress,獲取要引入的函數(shù),將符號名或標(biāo)識號轉(zhuǎn)換為DLL內(nèi)部地址。FreeLibrary(或MFC的AfxFreeLibrary),釋放動態(tài)鏈接庫。它們的原型分別是:HMODULE LoadLibrary(LPCTSTR lpFileName);FARPROC GetProcAddress(HMODULE hModule, LPCWSTR

15、 lpProcName);BOOL FreeLibrary(HMODULE hModule);現(xiàn)在,我們可以用IntPtr hModule=LoadLibrary(“Count.dll”);來獲得Dll的句柄,用IntPtr farProc=GetProcAddress(hModule,”_count4”);來獲得函數(shù)的入口地址。但是,知道函數(shù)的入口地址后,怎樣調(diào)用這個函數(shù)呢?因為在C#中是沒有函數(shù)指針的,沒有像C+那樣的函數(shù)指針調(diào)用方式來調(diào)用函數(shù),所以我們得借助其它方法。經(jīng)過研究,發(fā)現(xiàn)我們可以通過結(jié)合使用System.Reflection.Emit System.Reflection.Ass

16、embly里的類和函數(shù)達(dá)到我們的目的。為了以后使用方便及實現(xiàn)代碼的復(fù)用,我們可以編寫一個類。1)dld類的編寫:1.打開項目“Tzb”,打開類視圖,右擊“Tzb”,選擇“添加”-“類”,類名設(shè)置為“dld”,即dynamic loading dll 的每個單詞的開頭字母。2.添加所需的命名空間及聲明參數(shù)傳遞方式枚舉:using System.Runtime.InteropServices; / 用 DllImport 需用此 命名空間 using System.Reflection; / 使用 Assembly 類需用此 命名空間 using System.Reflection.Emit; /

17、 使用 ILGenerator 需用此 命名空間 /在“public class dld”上面添加如下代碼聲明參數(shù)傳遞方式枚舉:/ / 參數(shù)傳遞方式枚舉 ,ByValue 表示值傳遞 ,ByRef 表示址傳遞 / public enum ModePass ByValue = 0x0001, ByRef = 0x0002 3.聲明LoadLibrary、GetProcAddress、FreeLibrary及私有變量hModule和farProc:/ / 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName); / / DLL 文件名 / 函數(shù)庫模塊的句柄 Dll

18、Import(kernel32.dll) static extern IntPtr LoadLibrary(string lpFileName); / / 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName); / / 包含需調(diào)用函數(shù)的函數(shù)庫模塊的句柄 / 調(diào)用函數(shù)的名稱 / 函數(shù)指針 DllImport(kernel32.dll) static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); / / GetProcAddress 返回

19、的函數(shù)指針 / private IntPtr farProc=IntPtr.Zero; / / Loadlibrary 返回的函數(shù)庫模塊的句柄 / private IntPtr hModule=IntPtr.Zero; / / 原型是 : BOOL FreeLibrary(HMODULE hModule); / / 需釋放的函數(shù)庫模塊的句柄 / 是否已釋放指定的 Dll DllImport(kernel32,EntryPoint=FreeLibrary,SetLastError=true) static extern bool FreeLibrary(IntPtr hModule); 4.添加

20、LoadDll方法,并為了調(diào)用時方便,重載了這個方法:/ / 裝載 Dll / / DLL 文件名 public void LoadDll(string lpFileName) hModule=LoadLibrary(lpFileName); if(hModule=IntPtr.Zero) throw(new Exception( 沒有找到 :+lpFileName+. ); /若已有已裝載Dll的句柄,可以使用LoadDll方法的第二個版本: public void LoadDll(IntPtr HMODULE) if(HMODULE=IntPtr.Zero) throw(new Excep

21、tion( 所傳入的函數(shù)庫模塊的句柄 HMODULE 為空 . ); hModule=HMODULE; 5.添加LoadFun方法,并為了調(diào)用時方便,也重載了這個方法,方法的具體代碼及注釋如下:/ / 獲得函數(shù)指針 / / 調(diào)用函數(shù)的名稱 public void LoadFun(string lpProcName) / 若函數(shù)庫模塊的句柄為空,則拋出異常 if(hModule=IntPtr.Zero) throw(new Exception( 函數(shù)庫模塊的句柄為空 , 請確保已進(jìn)行 LoadDll 操作 !); / 取得函數(shù)指針 farProc = GetProcAddress(hModule

22、,lpProcName); / 若函數(shù)指針,則拋出異常 if(farProc=IntPtr.Zero) throw(new Exception( 沒有找到 :+lpProcName+ 這個函數(shù)的入口點 ); / / 獲得函數(shù)指針/ / 包含需調(diào)用函數(shù)的 DLL 文件名 / 調(diào)用函數(shù)的名稱 public void LoadFun(string lpFileName,string lpProcName) / 取得函數(shù)庫模塊的句柄 hModule=LoadLibrary(lpFileName); / 若函數(shù)庫模塊的句柄為空,則拋出異常 if(hModule=IntPtr.Zero) throw(ne

23、w Exception( 沒有找到 :+lpFileName+. ); / 取得函數(shù)指針 farProc = GetProcAddress(hModule,lpProcName); / 若函數(shù)指針,則拋出異常 if(farProc=IntPtr.Zero) throw(new Exception( 沒有找到 :+lpProcName+ 這個函數(shù)的入口點 ); 6.添加UnLoadDll及Invoke方法,Invoke方法也進(jìn)行了重載:/ / 卸載 Dll / public void UnLoadDll() FreeLibrary(hModule); hModule=IntPtr.Zero; f

24、arProc=IntPtr.Zero; Invoke方法的第一個版本:/ / 調(diào)用所設(shè)定的函數(shù) / / 實參 / 實參類型 / 實參傳送方式 / 返回類型 / 返回所調(diào)用函數(shù)的 object public object Invoke(object ObjArray_Parameter,Type TypeArray_ParameterType, ModePass ModePassArray_Parameter,Type Type_Return) / 下面 3 個 if 是進(jìn)行安全檢查 , 若不能通過 , 則拋出異常 if(hModule=IntPtr.Zero) throw(new Except

25、ion( 函數(shù)庫模塊的句柄為空 , 請確保已進(jìn)行 LoadDll 操作 !); if(farProc=IntPtr.Zero) throw(new Exception( 函數(shù)指針為空 , 請確保已進(jìn)行 LoadFun 操作 ! ) ); if(ObjArray_Parameter.Length!= ModePassArray_Parameter.Length) throw(new Exception( 參數(shù)個數(shù)及其傳遞方式的個數(shù)不匹配 . ) ); / 下面是創(chuàng)建 MyAssemblyName 對象并設(shè)置其 Name 屬性 AssemblyName MyAssemblyName = new A

26、ssemblyName(); MyAssemblyName.Name = InvokeFun; / 生成單模塊配件 AssemblyBuilder MyAssemblyBuilder= AppDomain.CurrentDomain.DefineDynamicAssembly (MyAssemblyName,AssemblyBuilderAccess.Run); ModuleBuilder MyModuleBuilder =MyAssemblyBuilder.DefineDynamicModule(InvokeDll); / 定義要調(diào)用的方法 , 方法名為“ MyFun ”,返回類型是“ Ty

27、pe_Return ”參數(shù)類型是“ TypeArray_ParameterType ” MethodBuilder MyMethodBuilder= MyModuleBuilder.DefineGlobalMethod(MyFun, MethodAttributes.Public|MethodAttributes.Static, Type_Return, TypeArray_ParameterType);/ 獲取一個 ILGenerator ,用于發(fā)送所需的 IL ILGenerator IL = MyMethodBuilder.GetILGenerator(); int i; for (i

28、= 0; i ObjArray_Parameter.Length; i+) / 用循環(huán)將參數(shù)依次壓入堆棧 switch (ModePassArray_Parameteri) 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() + 個參數(shù)沒有給定正確的傳遞方式 . ) ); if (IntPtr.Size = 4) / 判斷處

29、理器類型 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.

30、CreateGlobalFunctions(); / 取得方法信息 MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod(MyFun); return MyMethodInfo.Invoke(null, ObjArray_Parameter);/ 調(diào)用方法,并返回其值 Invoke方法的第二個版本,它是調(diào)用了第一個版本的:/ / 調(diào)用所設(shè)定的函數(shù) / / 函數(shù)指針 / 實參 / 實參類型 / 實參傳送方式 / 返回類型 / 返回所調(diào)用函數(shù)的 object public object Invoke(IntPtr IntPtr_Function,o

31、bject ObjArray_Parameter, Type TypeArray_ParameterType, ModePass ModePassArray_Parameter, Type Type_Return) / 下面 2 個 if 是進(jìn)行安全檢查 , 若不能通過 , 則拋出異常 if(hModule=IntPtr.Zero) throw(new Exception( 函數(shù)庫模塊的句柄為空 , 請確保已進(jìn)行 LoadDll 操作 !); if(IntPtr_Function=IntPtr.Zero) throw(new Exception( 函數(shù)指針 IntPtr_Function 為空

32、 ! ) ); farProc=IntPtr_Function; return Invoke(ObjArray_Parameter, TypeArray_ParameterType, ModePassArray_Parameter,Type_Return); 2)dld類的使用:1打開項目“Tzb”,向“Form1”窗體中添加三個按鈕。Name 和Text屬性分別為 “B3”、“用LoadLibrary方法裝載Count.dll”,“B4”、“調(diào)用count方法”,“B5”、“卸載Count.dll”,并調(diào)整到適當(dāng)?shù)拇笮〖拔恢谩?在“Form1cs設(shè)計”視圖中雙擊按鈕B3,在“B3_Click

33、”方法體上面添加代碼,創(chuàng)建一個dld類實例:/ / 創(chuàng)建一個 dld 類對象 / privatedld myfun=newdld(); 3在“B3_Click”方法體內(nèi)添加如下代碼:myfun.LoadDll(Count.dll); / 加載 Count.dll myfun.LoadFun(_count4); / 調(diào)入函數(shù) count, _count4 是它的入口,可通過 Depends 查看4“Form1cs設(shè)計”視圖中雙擊按鈕B4,在“B4_Click”方法體內(nèi)添加如下代碼:5“Form1cs設(shè)計”視圖中雙擊按鈕B5,在“B5_Click”方法體內(nèi)添加如下代碼:object Paramet

34、ers = new object(int)0; / 實參為 0 Type ParameterTypes = new Typetypeof(int); / 實參類型為 int ModePass themode=new ModePassModePass.ByValue; / 傳送方式為值傳 Type Type_Return = typeof(int); / 返回類型為 int / 彈出提示框,顯示調(diào)用 myfun.Invoke 方法的結(jié)果,即調(diào)用 count 函數(shù) MessageBox.Show( 這是您裝載該 Dll 后第 +myfun.Invoke(Parameters,ParameterTy

35、pes, themode,Type_Return).ToString() +次點擊此按鈕。 , 挑戰(zhàn)杯 ); myfun.UnLoadDll();6按“F5”運行該程序,并先點擊按鈕B3以加載“Count.dll”,接著點擊按鈕B4三次以調(diào)用3次“count(0)”,先后彈出的提示框如下:這三個提示框所得出的結(jié)果說明了靜態(tài)變量S 經(jīng)初始化后,再傳入實參“0”也不會改變其值為“0”。7點擊按鈕B5以卸載“Count.dll”,再點擊按鈕B3進(jìn)行裝載“Count.dll”,再點擊按鈕B4查看調(diào)用了“count(0)”的結(jié)果:從彈出的提示框所顯示的結(jié)果可以看到又開始重新計數(shù)了,也就是實現(xiàn)了DLL的動

36、態(tài)裝載與卸載了。(三)調(diào)用托管DLL一般方法C# 調(diào)用托管DLL是很簡單的,只要在“解決方案資源管理器”中的需要調(diào)用DLL的項目下用鼠標(biāo)右擊“引用”,并選擇“添加引用”,然后選擇已列出的DLL或通過瀏覽來選擇DLL文件,最后需要用using 導(dǎo)入相關(guān)的命名空間。(四)動態(tài)調(diào)用托管DLLC#動態(tài)調(diào)用托管DLL也需要借助System.Reflection.Assembly里的類和方法,主要使用了Assembly.LoadFrom?,F(xiàn)在,用例子說明:首先,啟動VS.NET,新建一個Visual C# 項目,使用的模板為“類庫”,名稱為“CsCount”,并在類“Class1”中添加靜態(tài)整型變量S及方

37、法count:/ 由于 static 不能修飾方法體內(nèi)的變量,所以需放在這里,且初始化值為 int.MinValuestatic int S=int.MinValue;public int count(int init)/ 判斷 S 是否等于 int.MinValue ,是的話把 init 賦值給 Sif(S=int.MinValue) S=init;S+; /S 自增 1return S; / 返回 S然后,打開項目“Tzb”,向“Form1”窗體中添加一個按鈕,Name屬性為“B6”,Text屬性為“用Assembly類來動態(tài)調(diào)用托管DLL”,調(diào)整到適當(dāng)大小和位置,雙擊按鈕B6,轉(zhuǎn)入代碼視

38、圖,先導(dǎo)入命名空間:using System.Reflection; 接著添加Invoke方法和B6_Click方法代碼:private object Invoke(string lpFileName,string Namespace,string ClassName,string lpProcName,object ObjArray_Parameter)Try / 載入程序集Assembly MyAssembly=Assembly.LoadFrom(lpFileName);Type type=MyAssembly.GetTypes();foreach(Type t in type)/ 查找要

39、調(diào)用的命名空間及類if(t.Namespace=Namespace&t.Name=ClassName)/ 查找要調(diào)用的方法并進(jìn)行調(diào)用MethodInfo m=t.GetMethod(lpProcName);if(m!=null)object o=Activator.CreateInstance(t);return m.Invoke(o,ObjArray_Parameter);else MessageBox.Show( 裝載出錯 !);/trycatch(System.NullReferenceException e)MessageBox.Show(e.Message);/catchreturn

40、 (object)0;/ Invoke“B6_Click”方法體內(nèi)代碼如下:/ 顯示 count(0) 返回的值MessageBox.Show( 這是您第 +Invoke(CsCount.dll,CsCount, Class1,count,new object(int)0).ToString()+ 次點擊此按鈕。 , 挑戰(zhàn)杯 );最后,把項目“CsCount”的binDebug文件夾中的CsCount.dll復(fù)制到項目“Tzb”的binDebug文件夾中,按“F5”運行該程序,并點擊按鈕B6三次,將會彈出3個提示框,內(nèi)容分別是“這是您第 1次點擊此按鈕?!薄ⅰ斑@是您第 2次點擊此按鈕。”、“這

41、是您第 3次點擊此按鈕?!?,由此知道了靜態(tài)變量S在這里的作用。(五) C#程序嵌入DLL的調(diào)用 DLL文件作為資源嵌入在C#程序中,我們只要讀取該資源文件并以“byte”返回,然后就用“Assembly Load(byte);”得到DLL中的程序集,最后就可以像上面的Invoke方法那樣對DLL中的方法進(jìn)行調(diào)用。當(dāng)然不用上面方法也可以,如用接口實現(xiàn)動態(tài)調(diào)用,但DLL中必須有該接口的定義并且程序中也要有該接口的定義;也可用反射發(fā)送實現(xiàn)動態(tài)調(diào)用4。現(xiàn)在我只對像上面的Invoke方法那樣對DLL中的方法進(jìn)行調(diào)用進(jìn)行討論,為了以后使用方便及實現(xiàn)代碼的復(fù)用,我們可以結(jié)合上一個編寫一個類。1)ldfs類的

42、編寫:在項目“Tzb”中新建一個名為ldfs的類,意為“l(fā)oad dll from resource”,請注意,在這個類中“resource”不只是嵌入在EXE程序中的資源,它也可以是硬盤上任意一個DLL文件,這是因為ldfs的類中的方法LoadDll有些特別,就是先從程序的內(nèi)嵌的資源中查找需加載的DLL,如果找不到,就查找硬盤上的。首先導(dǎo)入所需的命名空間:using System.IO; / 對文件的讀寫需要用到此命名空間using System.Reflection; / 使用 Assembly 類需用此命名空間using System.Reflection.Emit; / 使用 ILGe

43、nerator 需用此命名空間聲明一靜態(tài)變量MyAssembly:/ 記錄要導(dǎo)入的程序集static Assembly MyAssembly;添加LoadDll方法:private byte LoadDll(string lpFileName)Assembly NowAssembly = Assembly.GetEntryAssembly();Stream fs=null;try/ 嘗試讀取資源中的 DLLfs = NowAssembly.GetManifestResourceStream(NowAssembly.GetName().Name+.+lpFileName);finally/ 如果

44、資源沒有所需的 DLL ,就查看硬盤上有沒有,有的話就讀取if (fs=null&!File.Exists(lpFileName) throw(new Exception( 找不到文件 :+lpFileName);else if(fs=null&File.Exists(lpFileName)FileStream Fs = new FileStream(lpFileName, FileMode.Open);fs=(Stream)Fs;byte buffer = new byte(int) fs.Length;fs.Read(buffer, 0, buffer.Length);fs.Close()

45、;return buffer; / 以 byte 返回讀到的 DLL添加UnLoadDll方法來卸載DLL:public void UnLoadDll()/ 使 MyAssembly 指空MyAssembly=null;添加Invoke方法來進(jìn)行對DLL中方法的調(diào)用,其原理大體上和“Form1cs”中的方法Invoke相同,不過這里用的是“Assembly.Load”,而且用了靜態(tài)變量MyAssembly來保存已加載的DLL,如果已加載的話就不再加載,如果還沒加載或者已加載的不同現(xiàn)在要加載的DLL就進(jìn)行加載,其代碼如下所示:public object Invoke(string lpFileName,string Namespace,string ClassName,string lpProcName,object ObjArray_Parameter) try / 判斷 MyAssembly 是否為空或 MyAssembly 的命名空間不等于要調(diào)用方法的命名空間,如果條件為真,就用 Assembly.Load 加載所需 DLL 作為程序集 if(MyAssembly=null|MyAssembl

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論