[[DLLImport("DLL文件")]
修飾符extern返回變量類型方法名(參數列表)
其中包括:
DLL文件:包含外部方法的庫文件。
修飾符:訪問修飾符,可以在聲明除abstract以外的方法時使用。
返回變量類型:在DLL文件中,需要調用方法的返回變量類型。
方法名:您需要在DLL文件中調用的方法的名稱。
參數列表:需要在DLL文件中調用的方法列表。
註意:需要使用系統。程序聲明中的Runtime.InteropServices命名空間。
DllImport只能放在方法聲明上。
DLL文件必須位於程序的當前目錄中或系統定義的查詢路徑中(即系統環境變量Path中設置的路徑)。
返回變量類型、方法名和參數列表必須與DLL文件中的定義壹致。
若要使用其他函數名,可以使用EntryPoint屬性設置,例如:
[DllImport("user32.dll ",EntryPoint="MessageBoxA")]
靜態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表示入口點的調用約定,如:calling conventi on = calling conventi on . winapi;
另外,請參考其他壹些關於“數據封送”和“封送數字和邏輯標量”的文章[2]。
C#示例:
1.啟動VS.NET,創建壹個名為“Tzb”的新項目,模板為“Windows應用程序”。
2.雙擊工具箱中Windows窗體項中的Button項,向窗體1添加壹個按鈕。
3.更改按鈕的屬性:名稱為“B1”,文本為“調用DllImport彈出提示框”,將按鈕B1調整到合適的大小,移動到合適的位置。
4.在類視圖中雙擊“Form1”,打開“Form1.cs”的代碼視圖,輸入“使用系統。runtime.interop服務在“命名空間Tzb”上。導入命名空間。
5.雙擊“表格1”中的按鈕B1。CS”視圖中,在“B1_Click”方法上方用關鍵字static和extern聲明方法“MsgBox”,並將DllImport屬性附加到該方法。這裏我們要用“user32.dll”。
[DllImport("user32.dll ",EntryPoint="MessageBoxA")]
靜態extern int MsgBox(int hWnd,string msg,string caption,int type);
然後在“B1_Click”方法體中添加以下代碼,以調用方法“MsgBox”:
MsgBox(0,“這是調用DllImport時彈出的提示框!”,“挑戰杯”,0x 30);
6.按F5運行程序,點擊B1,彈出如下提示框:
(2)動態加載和調用DLL中的非托管函數。
上面已經解釋了如何使用DllImport調用DLL中的非托管函數,但這是壹個全局函數。如果DLL中的非托管函數有壹個靜態變量S,那麽每次調用這個函數時,靜態變量S都會自動加上1。結果當需要重新計數時,得不到想要的結果。下面將通過例子來說明:
創建1。動態鏈接庫
1)啟動Visual c++ 6.0;;
2)新建壹個名為“Win32動態鏈接庫”的項目;
3)在“dll種類”選擇界面選擇“壹個簡單的Dll項目”;
4)打開Count.cpp並添加以下代碼:
//導出函數,由“_stdcall”標準調用。
extern " C " _ declspec(dll export)int _ stdcall count(int init);
int _stdcall計數(int init)
{//count函數,用參數init初始化靜態塑料變量S,使S返回加1後的值。
static int S = init
s++;
返回S;
}
5)按“F7”編譯並獲取Count.dll(在項目目錄下的調試文件夾中)。
2.用DllImport調用DLL中的count函數。
1)打開項目“Tzb”並向“Form1”窗體添加壹個按鈕。
2)更改按鈕的屬性:名稱為“B2”,文本為“調用DllImport中的計數函數”,將按鈕B1調整到合適的大小,移動到合適的位置。
3)打開“Form1.cs”的代碼視圖,用關鍵字static和extern聲明方法“count ”,使其具有從Count.dll導出函數count的實現。代碼如下:
[DllImport("Count.dll")]
靜態外部整數計數(整數初始化);
4)在"窗體1 "視圖中雙擊按鈕B2。CS”,並在“B2_Click”方法的正文中添加以下代碼:
MessageBox。Show("調用DllImport中的count函數,傳入的參數為0,結果為"+count(0)。ToString(),“挑戰杯”);
MessageBox。Show("調用DllImport中的count函數,傳入的參數是10,結果是:"+count (10)。tostring()+" n結果不是預期的11!!! "、“挑戰杯”);
MessageBox。Show("結果表明在DllImport中調用非托管n函數是壹個全局的靜態函數!!! "、“挑戰杯”);
5)將Count.dll復制到項目“Tzb”的binDebug文件夾中,按“F5”運行程序,點擊B2按鈕,彈出如下三個提示框:
提示框1顯示調用“count(0)”的結果,第二個提示框顯示調用“count(10)”的結果,證明“在DllImport中調用非托管函數是壹個全局靜態函數”。所以,有時我們無法達到目的,所以我們需要使用以下方法:C#動態調用DLL中的函數。
3.C#動態調用DLL中的函數
因為在C#中使用DllImport不能像動態加載/卸載匯編壹樣,只能使用API函數。在kernel32.dll,與動態庫調用相關的功能包括[3]:
①LoadLibrary(或MFC的AfxLoadLibrary)加載動態庫。
②GetProcAddress,獲取要引入的函數,將符號名或標識號轉換成DLL的內部地址。
③FreeLibrary(或MFC的AfxFreeLibrary),釋放動態鏈接庫。
他們的原型是:
h module LoadLibrary(LPCTSTR lpFileName);
FARPROC GetProcAddress(HMODULE HMODULE,LPCWSTR lpProcName);
BOOL free library(HMODULE HMODULE);
現在,我們可以使用intptrhmodule = loadlibrary(" count . dll ");要獲取Dll的句柄,請使用intptrfarproc = getprocaddress(hm odule," _ count @ 4 ");獲取函數的入口地址。
但是,知道了函數的入口地址之後,如何調用這個函數呢?因為C#中沒有函數指針,沒有像C++那樣的函數指針調用方法,所以我們要用其他方法。通過研究發現,我們可以通過結合系統中的類和函數來實現我們的目標。Reflection.Emit和System.Reflection.Assembly .為了以後使用和代碼重用的方便,我們可以寫壹個類。
1) dld類編寫:
1.打開工程Tzb,打開類視圖,右鍵單擊Tzb,選擇Add-Class,將類名設置為dld,即動態加載dll中每個單詞的首字母。
2.添加所需的命名空間,並聲明參數傳遞方法的枚舉:
使用系統。Runtime . InteropServicesDllImport需要此命名空間。
使用系統。反思;//使用Assembly類需要此命名空間。
使用系統。反射。發射;//此命名空間是使用ILGenerator所必需的。
在“公共類dld”上方添加以下代碼,以聲明參數傳遞方法的枚舉:
///摘要
///枚舉參數傳遞方法,ByValue表示值傳遞,ByRef表示地址傳遞。
////摘要
公共枚舉模式通行證
{
ByValue = 0x0001,
ByRef = 0x0002
}
3.聲明LoadLibrary、GetProcAddress、FreeLibrary和私有變量hModule和farProc:
///摘要
///原型為:Hmodule LoadLibrary(lpctstr lpfilename);
////摘要
///param name="lpFileName"DLL文件名/param
///返回庫模塊的句柄///返回
[DllImport("kernel32.dll")]
靜態extern IntPtr LoadLibrary(字符串LP filename);
///摘要
///原型為:farproc getprocaddress(hmodule hmodule,lpcwstr lpprocname);
////摘要
///param name="hModule "包含要調用的函數庫模塊的句柄/param。
///param name="lpProcName "調用函數的名稱/param
///返回函數指針/返回
[DllImport("kernel32.dll")]
靜態extern IntPtr GetProcAddress(IntPtr hModule,string lpProcName);
///摘要
///原型是BOOL free library(HMODULE HMODULE);
////摘要
///param name="hModule "要釋放的函數庫模塊的句柄/param
/// returns已經釋放了指定的Dll/returns?
[DllImport("kernel32 ",EntryPoint="FreeLibrary ",SetLastError=true)]
靜態extern bool free library(IntPtr hModule);
///摘要
///LoadLibrary返回的函數庫模塊的句柄
////摘要
private IntPtr hModule=IntPtr。零;
///摘要
///getProcAddress返回的函數指針
////摘要
private IntPtr farProc=IntPtr。零;
4.添加LoadDll方法,為了調用方便,重載此方法:
///摘要
///加載Dll
////摘要
///param name="lpFileName"DLL文件名/param
公共void LoadDll(字符串lpFileName)
{
hm odule = LoadLibrary(LP filename);
if(hModule==IntPtr。零)
Throw(新異常("未找到:"+lpFileName+")。"));
}
如果已經有了已加載Dll的句柄,可以使用LoadDll方法的第二個版本:
公共void LoadDll(IntPtr HMODULE)
{
if(HMODULE==IntPtr。零)
Throw(new Exception("傳入的函數庫HMODULE的句柄為空。"));
hModule = HMODULE
}
5.添加LoadFun方法並重載它,以便於調用。方法的具體代碼和註釋如下:
///摘要
///獲取函數指針
////摘要
///param name="lpProcName "調用函數的名稱/param
public void LoadFun(字符串lpProcName)
{//如果函數庫模塊的句柄為空,則拋出異常。
if(hModule==IntPtr。零)
拋出(新異常("函數庫模塊的句柄為空,請確保已經執行了LoadDll的操作!"));
//獲取函數指針
farProc = GetProcAddress(hModule,lpProcName);
//如果函數指針,拋出異常。
if(farProc==IntPtr。零)
Throw(new Exception("未找到:"+lpProcName+"該函數的入口點"));
}
///摘要
///獲取函數指針
////摘要
///param name="lpFileName "包含要調用的函數的DLL文件名/param。
///param name="lpProcName "調用函數的名稱/param
public void LoadFun(字符串lpFileName,字符串lpProcName)
{//獲取函數庫模塊的句柄。
hm odule = LoadLibrary(LP filename);
//如果函數庫模塊的句柄為空,則拋出異常。
if(hModule==IntPtr。零)
Throw(新異常("未找到:"+lpFileName+")。"));
//獲取函數指針
farProc = GetProcAddress(hModule,lpProcName);
//如果函數指針,拋出異常。
if(farProc==IntPtr。零)
Throw(new Exception("未找到:"+lpProcName+"該函數的入口點"));
}
6.添加UnLoadDll和Invoke方法,該方法也是重載的:
///摘要
///卸載Dll
////摘要
公共void UnLoadDll()
{
free library(hModule);
hModule=IntPtr。零;
farProc=IntPtr。零;
}