什么是DLL?
DLL指的是動(dòng)態(tài)鏈接庫(Dynamic Link Library),它是一個(gè)可以被多個(gè)應(yīng)用程序(甚至是不同語言編寫的應(yīng)用程序)同時(shí)調(diào)用的可執(zhí)行二進(jìn)制文件,是一個(gè)可共享的庫。DLL是建立在客戶/服務(wù)器通信的概念上,包含若干函數(shù)、類或資源的庫文件,函數(shù)和數(shù)據(jù)被存儲在一個(gè)DLL(服務(wù)器)上并由一個(gè)或多個(gè)客戶導(dǎo)出而使用,這些客戶可以是應(yīng)用程序或者是其它的DLL。
在下面我們將通過一個(gè)具體的例子來說明如何利用
VC.Net定義一個(gè)DLL文件,并且在
VC.Net的應(yīng)用程序中調(diào)用,這個(gè)例子的主要功能是通過DLL獲取系統(tǒng)的機(jī)器名、操作系統(tǒng)類型和IP地址。
在
VC.Net中定義DLL文件
選擇
VC.Net菜單項(xiàng),選擇文件->新建->項(xiàng)目,在彈出的新建項(xiàng)目的對話框中,選擇項(xiàng)目類型為Visual C++ 項(xiàng)目,類別為MFC的工程,在右邊的模板中,選擇MFC DLL模板,給項(xiàng)目取名為TestDLL,選擇好項(xiàng)目的位置,按確定健,進(jìn)入應(yīng)用程序設(shè)置。
在應(yīng)用程序設(shè)置中,我們可以看到,有三種DLL類型,它們依次對應(yīng)著三類DLL。
靜態(tài)DLL與共享DLL的區(qū)別是:前者使用的是MFC的靜態(tài)鏈接庫,生成的DLL文件長度大,一般不使用這種方式,后者使用MFC的動(dòng)態(tài)鏈接庫,生成的DLL文件長度??;動(dòng)態(tài)鏈接到MFC的共享DLL所有輸出的函數(shù)應(yīng)該以如下語句開始(用于正確切換MFC模塊狀態(tài)): AFX_MANAGE_STATE(AfxGetStaticModuleState( )) 擴(kuò)展DLL用來建立MFC的派生類,只被用MFC類庫所編寫的應(yīng)用程序調(diào)用。常規(guī)DLL(包括靜態(tài)與動(dòng)態(tài))的一個(gè)特點(diǎn)是在源文件里有一個(gè)繼承CWinApp的類(從CWinApp派生,但沒有消息循環(huán)),被導(dǎo)出的函數(shù)是C++類或者C++成員函數(shù),調(diào)用常規(guī)DLL的應(yīng)用程序不必一定是MFC應(yīng)用程序。擴(kuò)展DLL和常規(guī)DLL不一樣,它沒有一個(gè)從CWinApp繼承而來的類的對象,編譯器默認(rèn)了一個(gè)DLL入口函數(shù)DLLMain()作為對DLL的初始化。
另外還可以添加兩個(gè)附加功能:自動(dòng)化和windows套接字,如果選擇了這兩項(xiàng),程序會做一些初始化,在這里我們就不做討論了。
在這個(gè)例子里,我們選擇“使用共享的MFC DLL”。
添加代碼:
1、在工程中導(dǎo)入systeminfo.cpp和systeminfo.h文件,這兩個(gè)文件用來獲取本機(jī)的機(jī)器名,操作系統(tǒng)版本和本機(jī)IP列表,具體的定義,請參考源文件。
在TestDLL.h頭文件中,引入systemInfo.h頭文件
#include "systemInfo.h"
添加變量:
CSystemInfo m_SystemInfo;
2、在CTestDLLApp類中添加三個(gè)函數(shù)用戶獲取信息:
//機(jī)器名 char* GetHostName(void); //系統(tǒng)類型 char* GetSystemType(void); //IP地址 void GetIPAddressList(char ** lpIPList,DWORD *lpNumber); |
函數(shù)定義如下:
//機(jī)器名 char* CTestDLLApp::GetHostName(void) { char* lpsz = new char[1024]; m_SystemInfo.GetHostName(lpsz); return lpsz; } //系統(tǒng)類型 char* CTestDLLApp::GetSystemType(void) { char* lpsz = new char[1024]; m_SystemInfo.GetlSystemType(lpsz); return lpsz; } //IP地址 void CTestDLLApp::GetIPAddressList(char ** lpIPList,DWORD *lpNumber) { m_SystemInfo.GetIPAddressList(lpIPList,lpNumber); } |
3、添加輸出函數(shù):
打開TestDLL工程中的“TestDLL.cpp”文件,在:
// 唯一的一個(gè) CTestDLLApp 對象 CTestDLLApp theApp;
的后面添加輸出的DLL函數(shù),函數(shù)定義如下:
/******************* 在這里添加輸出函數(shù) ***************************/ /******************************************** 函數(shù)名稱:GetHostName 功能:獲取本機(jī)的機(jī)器名稱 返回:strHostName-本機(jī)機(jī)器名稱 *********************************************/ extern "C" _declspec(dllexport) void GetHostName(LPTSTR strHostName ) { //如果是傳遞字符串需要使用strcpy拷貝字符串的地址,而不能直接等于。 strcpy(strHostName,theApp.GetHostName()); } /******************************************** 函數(shù)名稱:GetSystemType 功能:獲取本機(jī)操作系統(tǒng)版本 返回:strSystemType-本機(jī)操作系統(tǒng)版本 *********************************************/ extern "C" _declspec(dllexport) void GetSystemType(char * strSystemType) { strcpy(strSystemType,theApp.GetSystemType()); } /******************************************** 函數(shù)名稱:GetIPAddressList 功能:獲取本機(jī)的IP地址 返回:lpIPList-本機(jī)的IP地址數(shù)組,lpNumber IP地址個(gè)數(shù) *********************************************/ extern "C" _declspec(dllexport) void GetIPAddressList(char ** lpIPList,DWORD *lpNumber) { theApp.GetIPAddressList(lpIPList,lpNumber); } |
最后編譯工程文件,生成TestDLL.dll文件。
至此,一個(gè)DLL文件已經(jīng)做好了。
在
VC.Net中使用DLL文件
新建一個(gè)基于對話框的
VC.Net工程DemoTestDLL,界面如下圖(運(yùn)行結(jié)果圖):
為了讓DemoTestDLL能夠調(diào)用TestDLL.dll程序,需要讓前者能夠"看見" DLL程序。我們將TestDLL.dll文件考到DemoTestDLL的Debug目錄下,一個(gè)Windows程序定位DLL的次序是:
1、 包含EXE文件的目錄。
2、 進(jìn)程的當(dāng)前工作目錄。
3、 Windows系統(tǒng)目錄。
4、 Windows目錄。
5、 列在Path環(huán)境變量中的一系列目錄。
在測試DLL按鈕添加下面代碼:
void CDemoTestDLLDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知處理程序代碼 //聲明DLL函數(shù) typedef void (_cdecl *GETHOSTNAME)(LPTSTR strHostName); typedef void (_cdecl *GETSYSTEMTYPE)(char * strSystemType); typedef void (_cdecl *GETIPADDRESSLIST)(char ** lpIPList,DWORD *lpNumber);
//聲明函數(shù)句柄 HMODULE hTestDLL = NULL; GETHOSTNAME GetHostName = NULL; GETSYSTEMTYPE GetSystemType = NULL; GETIPADDRESSLIST GetIpAddressList = NULL;
// 加載動(dòng)態(tài)鏈接庫 hTestDLL = LoadLibrary("TestDLL.dll"); if(hTestDLL == NULL)\ { printf("cannot load LCDDLL.dll\n"); exit(0); }
/*** 找到每個(gè)函數(shù)的入口 ****/ //系統(tǒng)名稱 GetHostName = (GETHOSTNAME)GetProcAddress(hTestDLL,"GetHostName"); if(GetHostName==NULL) { printf("cannot load process GetHostName\n"); FreeLibrary(hTestDLL); exit(1); }
//操作系統(tǒng)類型 GetSystemType = (GETSYSTEMTYPE)GetProcAddress(hTestDLL,"GetSystemType"); if(GetSystemType==NULL) { printf("cannot load process GetSystemType\n"); FreeLibrary(hTestDLL); exit(1); }
//IP地址列表 GetIpAddressList = (GETIPADDRESSLIST)GetProcAddress(hTestDLL,"GetIPAddressList"); if(GetSystemType==NULL) { printf("cannot load process GetIpAddressList\n"); FreeLibrary(hTestDLL); exit(1); }
/*** 使用LPTSTR和使用char*定義的效果是一樣的 ***/
//取機(jī)器名稱 LPTSTR szHostName = new char[1024]; (*GetHostName)(szHostName);
//取操作系統(tǒng)類型 char* szSystemType = new char[1024]; (*GetSystemType)(szSystemType);
//IP Address List DWORD ipListNumber = 0; //聲明方式一 //LPTSTR* lpAddress = new LPTSTR[256]; //聲明方式二 char** lpAddress = new char*[256]; for(int i=0;i<256;i++) { lpAddress[i] = NULL; } (*GetIpAddressList)(lpAddress,&ipListNumber);
//顯示在界面 m_setHostName.SetWindowText(szHostName); m_setSystemType.SetWindowText(szSystemType);
//將IP添加到list 中 for(int i=0;i<ipListNumber;i++) { m_IPList.AddString(lpAddress[i]); } } |
編譯運(yùn)行的結(jié)果如上圖所示。