国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

C#程序實現(xiàn)動態(tài)調用DLL的研究[轉]

2019-11-17 03:01:21
字體:
來源:轉載
供稿:網友
C#程序實現(xiàn)動態(tài)調用DLL的研究[轉]

摘要:在《csdn開發(fā)高手》2004年第03期中的《化功大法——將DLL嵌入EXE》一文,介紹了如何把一個動態(tài)鏈接庫作為一個資源嵌入到可執(zhí)行文件,在可執(zhí)行文件運行時,自動從資源中釋放出來,通過靜態(tài)加載延遲實現(xiàn)DLL函數的動態(tài)加載,程序退出后實現(xiàn)臨時文件的自動刪除,從而為解決“DLL Hell”提供了一種解決方案。這是一個很好的設計思想,而且該作者也用C++實現(xiàn)了,在Internet上也有相似的VB程序,但在某一技術論壇上提起這種設計方法時,有網友提出:“這種方法好是好,但就是啟動速度太慢”。這是因為程序啟動時實現(xiàn)DLL釋放,然后再加載釋放出來的DLL,這個過程會耗費一定的時間。鑒于此問題,經過思索,提出另一個設計方案:DLL作為資源文件嵌入程序,但不需進行DLL釋放及其重新加載。本文就是對該設計方案的原理分析及使用C#編程來實現(xiàn)該設計方案。

關鍵詞:動態(tài)調用DLL,嵌入DLL,C#

正文:

一、DLL與應用程序

動態(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調用來完成一定操作的函數(方法。注:C#中一般稱為“方法”),但這些函數不是執(zhí)行程序本身的一部分,而是根據進程的需要按需載入,此時才能發(fā)揮作用。

DLL只有在應用程序需要時才被系統(tǒng)加載到進程的虛擬空間中,成為調用進程的一部分,此時該DLL也只能被該進程的線程訪問,它的句柄可以被調用進程所使用,而調用進程的句柄也可以被該DLL所使用。在內存中,一個DLL只有一個實例,且它的編制與具體的編程語言和編譯器都沒有關系,所以可以通過DLL來實現(xiàn)混合語言編程。DLL函數中的代碼所創(chuàng)建的任何對象(包括變量)都歸調用它的線程或進程所有。

下面列出了當程序使用DLL時提供的一些優(yōu)點:[1]

1)使用較少的資源

當多個程序使用同一個函數庫時,DLL可以減少在磁盤和物理內存中加載的代碼的重復量。這不僅可以大大影響在前臺運行的程序,而且可以大大影響其他在Windows操作系統(tǒng)上運行的程序。

2)推廣模塊式體系結構

DLL有助于促進模塊式程序的開發(fā)。這可以幫助您開發(fā)要求提供多個語言版本的大型程序或要求具有模塊式體系結構的程序。模塊式程序的一個示例是具有多個可以在運行時動態(tài)加載的模塊的計帳程序。

3)簡化部署和安裝

當DLL中的函數需要更新或修復時,部署和安裝DLL不要求重新建立程序與該DLL的鏈接。此外,如果多個程序使用同一個DLL,那么多個程序都將從該更新或修復中獲益。當您使用定期更新或修復的第三方DLL時,此問題可能會更頻繁地出現(xiàn)。

二、DLL的調用

每種編程語言調用DLL的方法都不盡相同,在此只對用C#調用DLL的方法進行介紹。首先,您需要了解什么是托管,什么是非托管。一般可以認為:非托管代碼主要是基于win 32平臺開發(fā)的DLL,activeX的組件,托管代碼是基于.net平臺開發(fā)的。如果您想深入了解托管與非托管的關系與區(qū)別,及它們的運行機制,請您自行查找資料,本文件在此不作討論。

(一)調用DLL中的非托管函數一般方法

首先,應該在C#語言源程序中聲明外部方法,其基本形式是:

[DLLImport(“DLL文件”)]

修飾符extern返回變量類型方法名稱(參數列表)

其中

DLL文件:包含定義外部方法的庫文件。

修飾符:訪問修飾符,除了abstract以外在聲明方法時可以使用的修飾符。

返回變量類型:在DLL文件中你需調用方法的返回變量類型。

方法名稱:在DLL文件中你需調用方法的名稱。

參數列表:在DLL文件中你需調用方法的列表。

注意:需要在程序聲明中使用System.Runtime.InteropServices命名空間。

DllImport只能放置在方法聲明上。

DLL文件必須位于程序當前目錄或系統(tǒng)定義的查詢路徑中(即:系統(tǒng)環(huán)境變量中Path所設置的路徑)。

返回變量類型、方法名稱、參數列表一定要與DLL文件中的定義相一致。

若要使用其它函數名,可以使用EntryPoint屬性設置,如:

[DllImport("user32.dll", EntryPoint="MessageBoxA")]

static extern int MsgBox(int hWnd, string msg, string caption, int type);

其它可選的DllImportAttribute屬性:

CharSet指示用在入口點中的字符集,如:CharSet=CharSet.Ansi;

SetLastError指示方法是否保留Win32"上一錯誤",如:SetLastError=true;

ExactSpelling指示EntryPoint是否必須與指示的入口點的拼寫完全匹配,如:ExactSpelling=false;

PReserveSig指示方法的簽名應當被保留還是被轉換,如:PreserveSig=true;

CallingConvention指示入口點的調用約定,如:CallingConvention=CallingConvention.Winapi;

此外,關于“數據封送處理”及“封送數字和邏輯標量”請參閱其它一些文章[2]。

C#例子:

1.啟動VS.NET,新建一個項目,項目名稱為“Tzb”,模板為“Windows應用程序”。

2.在“工具箱”的“Windows窗體”項中雙擊“Button”項,向“Form1”窗體中添加一個按鈕。

3.改變按鈕的屬性:Name為“B1”,Text為“用DllImport調用DLL彈出提示框”,并將按鈕B1調整到適當大小,移到適當位置。

4.在類視圖中雙擊“Form1”,打開“Form1.cs”代碼視圖,在“namespace Tzb”上面輸入“using System.Runtime.InteropServices;”,以導入該命名空間。

5.在“Form1.cs[設計]”視圖中雙擊按鈕B1,在“B1_Click”方法上面使用關鍵字static和extern聲明方法“MsgBox”,將DllImport屬性附加到該方法,這里我們要使用的是“user32.dll”中的“MessageBoxA”函數,具體代碼如下:

[DllImport("user32.dll", EntryPoint="MessageBoxA")]

static extern int MsgBox(int hWnd, string msg, string caption, int type);

然后在“B1_Click”方法體內添加如下代碼,以調用方法“MsgBox”:

MsgBox(0," 這就是用 DllImport 調用 DLL 彈出的提示框哦! "," 挑戰(zhàn)杯 ",0x30);

6.按“F5”運行該程序,并點擊按鈕B1,便彈出如下提示框:

(二)動態(tài)裝載、調用DLL中的非托管函數

在上面已經說明了如何用DllImport調用DLL中的非托管函數,但是這個是全局的函數,假若DLL中的非托管函數有一個靜態(tài)變量S,每次調用這個函數的時候,靜態(tài)變量S就自動加1。結果,當需要重新計數時,就不能得出想要的結果。下面將用例子說明:

1.DLL的創(chuàng)建

1)啟動Visual C++ 6.0;

2)新建一個“Win32 Dynamic-Link Library”工程,工程名稱為“Count”;

3)在“Dll kind”選擇界面中選擇“A simple dll project”;

4)打開Count.cpp,添加如下代碼:

// 導出函數,使用“ _stdcall ” 標準調用

extern "C" _declspec(dllexport)int _stdcall count(int init);

int _stdcall count(int init)

{//count 函數,使用參數 init 初始化靜態(tài)的整形變量 S ,并使 S 自加 1 后返回該值

static int S=init;

S++;

return S;

}

5)按“F7”進行編譯,得到Count.dll(在工程目錄下的Debug文件夾中)。

2.用DllImport調用DLL中的count函數

1)打開項目“Tzb”,向“Form1”窗體中添加一個按鈕。

2)改變按鈕的屬性:Name為“B2”,Text為“用DllImport調用DLL中count函數”,并將按鈕B1調整到適當大小,移到適當位置。

3)打開“Form1.cs”代碼視圖,使用關鍵字static和extern聲明方法“count”,并使其具有來自Count.dll的導出函數count的實現(xiàn),代碼如下:

[DllImport("Count.dll")]

static extern int count(int init);

4)在“Form1.cs[設計]”視圖中雙擊按鈕B2,在“B2_Click”方法體內添加如下代碼:

MessageBox.Show(" 用 DllImport 調用 DLL 中的 count 函數, /n 傳入的實參為 0 ,得到的結果是: "+count(0).ToString()," 挑戰(zhàn)杯 ");

MessageBox.Show(" 用 DllImport 調用 DLL 中的 count 函數, /n 傳入的實參為 10 ,得到的結果是: "+count(10).ToString()+"/n 結果可不是想要的 11 哦!!! "," 挑戰(zhàn)杯 ");

MessageBox.Show(" 所得結果表明: /n 用 DllImport 調用 DLL 中的非托管 /n 函數是全局的、靜態(tài)的函數!!! "," 挑戰(zhàn)杯 ");

5)把Count.dll復制到項目“Tzb”的bin/Debug文件夾中,按“F5”運行該程序,并點擊按鈕B2,便彈出如下三個提示框:

第1個提示框顯示的是調用“count(0)”的結果,第2個提示框顯示的是調用“count(10)”的結果,由所得結果可以證明“用DllImport調用DLL中的非托管函數是全局的、靜態(tài)的函數”。所以,有時候并不能達到我們目的,因此我們需要使用下面所介紹的方法:C#動態(tài)調用DLL中的函數。

3.C#動態(tài)調用DLL中的函數

因為C#中使用DllImport是不能像動態(tài)load/unload assembly那樣,所以只能借助API函數了。在kernel32.dll中,與動態(tài)庫調用有關的函數包括[3]:

①LoadLibrary(或MFC的AfxLoadLibrary),裝載動態(tài)庫。

②GetProcAddress,獲取要引入的函數,將符號名或標識號轉換為DLL內部地址。

③FreeLibrary(或MFC的AfxFreeLibrary),釋放動態(tài)鏈接庫。

它們的原型分別是:

HMODULE LoadLibrary(LPCTSTR lpFileName);

FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);

BOOL FreeLibrary(HMODULE hModule);

現(xiàn)在,我們可以用IntPtr hModule=LoadLibrary(“Count.dll”);來獲得Dll的句柄,用IntPtr farProc=GetProcAddress(hModule,”_count@4”);來獲得函數的入口地址。

但是,知道函數的入口地址后,怎樣調用這個函數呢?因為在C#中是沒有函數指針的,沒有像C++那樣的函數指針調用方式來調用函數,所以我們得借助其它方法。經過研究,發(fā)現(xiàn)我們可以通過結合使用System.Reflection.Emit及System.Reflection.Assembly里的類和函數達到我們的目的。為了以后使用方便及實現(xiàn)代碼的復用,我們可以編寫一個類。

1)dld類的編寫:

1.打開項目“Tzb”,打開類視圖,右擊“Tzb”,選擇“添加”-->“類”,類名設置為“dld”,即dynamic loading dll的每個單詞的開頭字母。

2.添加所需的命名空間及聲明參數傳遞方式枚舉:

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 察哈| 新安县| 逊克县| 福海县| 略阳县| 于都县| 新宾| 明水县| 内江市| 石城县| 桐梓县| 津南区| 镇原县| 志丹县| 九龙县| 东安县| 井冈山市| 祁门县| 邮箱| 广宁县| 石首市| 平乐县| 永善县| 华坪县| 镇雄县| 从化市| 桓台县| 长子县| 乳山市| 泗水县| 垣曲县| 西宁市| 南昌市| 平阳县| 天水市| 陆河县| 永顺县| 莆田市| 石台县| 蓝山县| 金堂县|