MFC下DLL程式設計(圖解)

DLL(Dynamic Link Library,動態連結庫)是微軟公司為Windows和OS/2作業系統設計一種供應用程式在執行時呼叫的共享函式庫。DLL是應用程式的一種擴充套件,也是軟體共享和重用的傳統方法。

DLL除了可同時被多個應用程式共享外,還可以在不改變呼叫介面(從而不需修改使用它的應用程式)的情況下,改進和升級裡面的庫函式。而且DLL與編寫它的語言無關,例如,用VC生成的規則DLL,可以被VB、Delphi等生成的應用程式使用。

DLL可以用多種語言和工具編寫,我們這裡只介紹如何使用MFC來編寫和使用DLL。相關說明文件位於MSDN幫助的“目錄\開發工具和語言\Visual Studio\Visual C++\常見程式設計方法\DLL\”中。

8。1 基礎

本節先討論DLL與靜態庫的區別,然後列出幾種適合放置DLL的目錄,最後介紹MFC DLL的三種類型。

8。1。1 DLL與靜態連結庫

靜態連結庫Lib(Static Link Library),是在編譯的連結階段將庫函式嵌入到應用程式的內部。如果系統中執行的多個應用程式都包含所用到的公共庫函式,則必然造成很大的浪費。這樣即增加了連結器的負擔,也增大了可執行程式的大小,還加大了記憶體的消耗。Lib的好處是應用程式可以獨立執行,而不需要在作業系統中另外安裝對應的DLL。

而DLL採用動態連結,對公用的庫函式,系統只有一個複製(一般是位於系統目錄的*。DLL檔案),而且只有在應用程式真正呼叫時,才載入到記憶體。在記憶體中的庫函式,也只有一個複製,可供所有執行的程式呼叫。當再也沒有程式需要呼叫它時,系統會自動將其解除安裝,並釋放其所佔用的記憶體空間。參見圖8-1。

MFC下DLL程式設計(圖解)

DLL的缺點是應用程式不能獨立執行,需要在作業系統中另外安裝對應的DLL。例如,如果你的MFC專案被設定成“在共享DLL中使用MFC”的,則雖然生成的可執行程式很小,但是在其他沒有安裝Visual C++(執行環境)的機器上是不能直接執行的,需要另外安裝MFC的動態連結庫(如mfc90。dll)。

8。1。2放置DLL的目錄

為了使需要動態連結庫的應用程式可以執行,需要將DLL檔案放在作業系統能夠找到的地方。Windows作業系統查詢DLL的目錄順序為:

所在目錄——當前程序的可執行模組所在的目錄,即應用程式的可執行檔案(*。exe)所在的目錄。

當前目錄——程序的當前目錄。

系統目錄——Windows作業系統安裝目錄的系統子目錄,如C:\Windows\ System32。可用GetSystemDirectory函式檢索此目錄的路徑。

Windows目錄——Windows作業系統安裝目錄,如C:\Windows\。可用GetWindowsDirectory函式檢索此目錄的路徑。

搜尋目錄——PATH環境變數中所包含的自動搜尋路徑目錄,一般包含C:\Windows\和C:\Windows\System32\等目錄。可在命令列用Path命令來檢視和設定,也可以透過(在“我的電腦”右鍵選單中選“屬性”選單項)“系統屬性”中的環境變數,來檢視或編輯“Path”系統變數和“PATH”使用者變數。

8。1。3 MFC DLL的型別

使用MFC編寫的DLL,可以分成兩大類:

l規則DLL——規則(regular)DLL中所包含的函式,可以被所有Windows應用程式使用;

共享MFC——DLL中不包含MFC庫函式,需要另外安裝MFC動態連結庫後才能使用;

靜態MFC——DLL中包含MFC庫函式,可以脫離MFC動態連結庫獨立使用。

擴充套件DLL——擴充套件(extension)DLL中所定義的類和函式,只能被所MFC應用程式使用。而且擴充套件DLL中不能包含MFC庫函式,也需要另外安裝MFC動態連結庫後才能使用。

8。1。4匯出函式的方法

使用MFC建立DLL時,從專案中匯出(export)函式到DLL檔案的方法有:

使用模組定義檔案(。def)。

使用__declspec(dllexport)關鍵字或其替代宏AFX_EXT_CLASS。

這兩種方法是互斥的,對每個函式只需用一種方法即可。另外,DEF檔案只能用來匯出函式,不能用於匯出整個類。匯出C++類,必須用__declspec(dllexport)關鍵字或其替代宏AFX_EXT_CLASS。

1.DEF檔案

模組定義(module definition)檔案(。def)是包含一個或多個描述DLL各種屬性的模組語句的文字檔案。DEF檔案必須至少包含下列模組定義語句:

l 檔案中的第一個語句必須是LIBRARY語句。此語句將。def檔案標識為屬於DLL。LIBRARY語句的後面是DLL的名稱(預設為DLL專案名)。連結器將此名稱放到DLL的匯入庫中。

l EXPORTS語句列出名稱,可能的話還會列出DLL匯出函式的序號值。透過在函式名的後面加上@符和一個數字,給函式分配序號值。當指定序號值時,序號值的範圍必須是從1到N,其中N是DLL匯出函式的個數。

即,DEF檔案的格式為:(在這兩個語句之間,還可以加上可選的描述語句:DESCRIPTION “庫描述串”。分號;後的文字內容行為註釋)

; 庫名。def

LIBRARY 庫名

EXPORTS

函式名1 @1

函式名2 @2

……

函式名n @n

在使用MFC DLL嚮導建立MFC DLL專案時,VC會自動建立一個與專案同名但沒有任何函式匯出項的DEF檔案(專案名。def),格式為:

; 專案名。def : 宣告 DLL 的模組引數。

LIBRARY “專案名”

EXPORTS

; 此處可以是顯式匯出

例如,專案名為RegDll的DEF檔案(RegDll。def)的內容為:

; RegDll。def : 宣告 DLL 的模組引數。

LIBRARY “RegDll”

EXPORTS

; 此處可以是顯式匯出

如果生成擴充套件DLL並使用。def檔案匯出,則將下列程式碼放在包含匯出類的標頭檔案的開頭和結尾:

#undef AFX_DATA

#define AFX_DATA AFX_EXT_DATA

// <你的標頭檔案體>

#undef AFX_DATA

#define AFX_DATA

這些程式碼行確保內部使用的MFC變數或新增到類的變數是從擴充套件DLL匯出(或匯入)的。例如,當使用DLAECRE_DYNAMIC派生類時,該宏擴充套件以將CRuntimeClass成員變數新增到類。省去這四行程式碼可能會導致不能正確編譯或連結DLL,或在客戶端應用程式連結到DLL時導致錯誤。

當生成DLL時,連結器使用。def檔案建立匯出(。exp)檔案和匯入庫(。lib)檔案。然後,連結器使用匯出檔案生成DLL檔案。隱式連結到DLL的可執行檔案在生成時連結到匯入庫。請注意,MFC本身就是使用。def檔案從MFCx0。dll匯出函式和類的。

2.關鍵字或宏

除了使用DEF檔案來匯出函式外,還可以在源程式中使用__declspec(dllexport)關鍵字或其替代宏AFX_EXT_CLASS:

#define AFX_EXT_CLASS AFX_CLASS_EXPORT (定義在標頭檔案afxv_dll。h中)

#define AFX_CLASS_EXPORT __declspec(dllexport) (定義在標頭檔案afxver_。h中)

來匯出函式和整個C++類。

具體的格式為:

l匯出整個類:

class AFX_EXT_CLASS 類名[ : public基類]

{

……

}

l 匯出類的成員函式:

class 類名[ : public基類]

{

AFX_EXT_CLASS 返回型別 函式名1(……) ;

AFX_EXT_CLASS 返回型別 函式名2(……) ;

……

}

l 匯出外部C格式的(全域性)函式:

extern “C” __declspec(dllexport) 返回型別 函式名(……)

{

……

}

如果希望用MFC(C++)編寫的規則DLL中的函式,也能夠被非MFC程式來呼叫,需要為函式宣告指定extern “C”。

不然,C++編譯器會使用C++型別安全命名約定(也稱作名稱修飾)和C++呼叫約定(使用此呼叫約定從C呼叫會很困難)。

為了使用方便,可以定義宏:

#define DllExport extern “C” __declspec(dllexport)

然後再使用它,例如:

DllExport int Add(int d1, int d2) {……}

8。2 擴充套件DLL

使用MFC編寫的擴充套件DLL,可以匯出整個類(從而能使用類中的所有成員,包括資料成員和成員函式),也可以匯出指定的若干(成員或全域性)函式。

下面我們透過一個四則運算的例子,看看如何用宏AFX_EXT_CLASS來編寫和使用匯出整個C++類的擴充套件MFC DLL。

8。2。1建立DLL專案

我們建立一個名為ExtDll的擴充套件DLL的“Visual C++”之“MFC”的“MFC DLL”專案,注意需選中“建立解決方案的目錄”複選框,參見圖8-2。

MFC下DLL程式設計(圖解)

圖8-2 新建MFC DLL專案ExtDll的對話方塊

按“確定”鈕,彈出“MFC DLL嚮導”對話方塊。在“DLL型別”欄中,選中“擴充套件DLL”單選鈕,參見圖8-3。按“完成”鈕,建立ExtDll解決方案和專案。

MFC下DLL程式設計(圖解)

圖8-3 選擇“擴充套件DLL”的MFC DLL嚮導對話方塊

8。2。2新增匯出類

為新專案新增用於四則計算的匯出類CCompute。方法有多種,可以在專案管理區的“類檢視”頁中,選中專案名“ExtDll”,按滑鼠右鍵,在彈出選單中選“新增\類”。在彈出的“新增類”對話方塊中,選擇“Visual C++”之“MFC”的“MFC類”項,參見圖8-4。

MFC下DLL程式設計(圖解)

圖8-4 新增類對話方塊

按“新增”鈕,彈出“MFC類嚮導”對話方塊。在“類名”欄中鍵入“CCompute”,在“基類”下拉式列表,選“CObject”,參見圖8-5。按“完成”鈕,新增該類到ExtDll專案。

MFC下DLL程式設計(圖解)

圖8-5 MFC類嚮導對話方塊

8。2。3編寫匯出類程式碼

我們將整個CCompute類設為匯出類,並在裡面新增2個成員變數、1個建構函式和4個用於四則運算的成員函式,外加1個演示匯出函式的取模全域性函式Mod。

下面是CCompute類的標頭檔案(Compute。h),其中紅色的部分是自己新增:(注意匯出宏AFX_EXT_CLASS的使用)

#pragma once // CCompute 命令目標 class AFX_EXT_CLASS CCompute : public CObject{public:int m_data1, m_data2;public:CCompute();CCompute(int d1, int d2);virtual ~CCompute();public:int Add();int Sub();int Mul();double Div();};AFX_EXT_CLASS int Mod(int d1, int d2);

MFC下DLL程式設計(圖解)

下面是CCompute類的程式碼原始檔(Compute。cpp),其中紅色為自己新增的部分:

MFC下DLL程式設計(圖解)

// Compute。cpp : 實現檔案// #include “stdafx。h”#include “Compute。h” // CCompute CCompute::CCompute(){} CCompute::CCompute(int d1, int d2){m_data1 = d1;m_data2 = d2;} CCompute::~CCompute(){} // CCompute 成員函式int CCompute::Add(){return m_data1 + m_data2;} int CCompute::Sub(){return m_data1 - m_data2;} int CCompute::Mul(){return m_data1 * m_data2;} double CCompute::Div(){if (m_data2 == 0 ) {AfxMessageBox(L“Divided by zero!”);return 0;}return (double)m_data1 / m_data2;} int Mod(int d1, int d2){if (d2 == 0 ) {AfxMessageBox(L“Modulo by zero!”);return 0;}return d1 % d2;}

MFC下DLL程式設計(圖解)

編譯執行時,後彈出圖8-6所示的對話方塊:

MFC下DLL程式設計(圖解)

圖8-6 除錯會話的可執行檔案對話方塊

要求你選擇或輸入使用此DLL的應用程式之可執行檔案的名稱或路徑。這是因為DLL雖然包含了可執行函式的二進位制程式碼,但是它並不是獨立的應用程式,不能單獨執行。因此,我們必須編寫使用DLL的客戶程式。

8。2。4新增客戶程式專案

為了演示擴充套件DLL的應用,我們在原解決方案ExtDll中,新增一個客戶程式專案ExtClient。具體做法是,開啟新建專案對話方塊,選中“Visual C++”之“MFC”的“MFC應用程式”模板,鍵入專案名ExtClient。注意,需選在對話方塊底部的“解決方案”下拉式列表中選中“添入解決方案”表項,參見圖8-7。

MFC下DLL程式設計(圖解)

圖8-6 新建客戶程式專案的對話方塊

按“確定”鈕進入“MFC應用程式嚮導”對話方塊,在“應用程式型別”頁,選中“基於對話方塊”單選鈕,按“完成”新增專案。

此時,ExtDll解決方案包含兩個專案:DLL專案ExtDll和客戶程式專案ExtClient,生成的檔案目錄結構為:

ExtDll ←解決方案目錄

Debug ←解決方案的除錯目錄

Release ←解決方案的發行目錄

ExtDll ←DLL專案目錄

Debug ←DLL的除錯目錄

Release ←DLL的發行目錄

res ←DLL的資源目錄

ExtClient ←客戶程式專案目錄

Debug ←客戶程式的除錯目錄

Release ←客戶程式的發行目錄

res ←客戶程式的資源目錄

8。2。5 設定依賴項(個人看還要新增引用ExtDll)

為了使客戶程式可以呼叫DLL,需要將它們關聯起來。最簡單的辦法是設定DLL專案為客戶專案的依賴項。具體做法是,在專案管理區中選中客戶專案名“ExtClient”,選中選單項“專案\專案依賴項”,在彈出的“專案依賴項”對話方塊中,選中“依賴欄”中的“ExtDll”複選框,參見圖8-7。

MFC下DLL程式設計(圖解)

圖8-7 設定ExtClient專案依賴項的對話方塊

8。2。6編寫客戶程式程式碼

1.編輯對話方塊資源

新增表示運算元的2個靜態文字框和2個文字編輯框(ID值分別為IDC_DATA1和IDC_DATA2)、5個表示四則運算和取模運算的按鈕(ID值分別為IDC_ADD、IDC_SUB、IDC_MUL、IDC_DIV和IDC_MOD)、表示計算結果的1個靜態文字框和1個文字編輯框(ID值為IDC_RESULT),刪除原來“確定”按鈕,將原來的“取消”按鈕的“Caption”屬性值改為“退出”,參見圖8-8。

MFC下DLL程式設計(圖解)

圖8-8 客戶程式的對話方塊介面

2.新增控制元件變數

為了動態獲取使用者輸入的資料,我們需要為2個表示操作資料的文字編輯框,新增控制元件的Value值類別int型變數m_iData1和m_iData2。

3.新增事件處理

分別對5個計算按鈕,為對話方塊類CExtClientDlg新增按鈕通知訊息BN_CLICKED(滑鼠單擊)事件的處理程式OnBnClickedAdd等。

4.編寫程式碼

為了讓客戶程式可以使用DLL專案中的計算類CCompute,需要在客戶程式對話方塊類CExtClientDlg的標頭檔案

在ExtClientDlg。h 中

新增 #include “。。\ExtDll\Compute。h”//相對路徑(這個比絕對路徑要好),看情況。。\表示相對自己上一級目錄。用來引入CCompute類。

檢查class CAboutDlg : public CDialogEx 中的,對話方塊資料enum { IDD = 操作對話方塊資料ID };

在對話方塊類的定義中,手工新增公共型類變數和成員函式:

public: CCompute *m_pComp; void Comp(UINT nID);

ExtClientDlg。c //檔案

// CExtClientDlg 對話方塊class CExtClientDlg : public CDialog{……public:int m_iData1;int m_iData2;CCompute *m_pComp;void Comp(UINT nID);afx_msg void OnBnClickedAdd();afx_msg void OnBnClickedSub();afx_msg void OnBnClickedMul();afx_msg void OnBnClickedDiv();afx_msg void OnBnClickedMod(); };

在客戶對話方塊類ExtClientDlg。cpp的初始化對話方塊成員函式OnInitDialog中,手工新增設定資料編輯框初值的程式碼(紅色部分):

BOOL CExtClientDlg::OnInitDialog(){CDialog::OnInitDialog();……// TODO: 在此新增額外的初始化程式碼SetDlgItemInt(IDC_DATA1, 5);SetDlgItemInt(IDC_DATA2, 3); return TRUE; // 除非將焦點設定到控制元件,否則返回 TRUE}

程式碼檔案ExtClientDlg。cpp中其他新加內容有:(其中紅色部分為手工新增的)

void CExtClientDlg::OnBnClickedAdd(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_ADD);} void CExtClientDlg::OnBnClickedSub(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_SUB);} void CExtClientDlg::OnBnClickedMul(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_MUL);} void CExtClientDlg::OnBnClickedDiv(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_DIV);}void CExtClientDlg::OnBnClickedMod(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_MOD);}void CExtClientDlg::Comp(UINT nID){UpdateData(); // 動態獲取使用者輸入的資料並賦值給對應的控制元件變數m_pComp = new CCompute(m_iData1, m_iData2); // 建立計算物件int r;double dr;switch(nID) { // 進行四則和取模運算case IDC_ADD: r = m_pComp->Add(); break;case IDC_SUB: r = m_pComp->Sub(); break;case IDC_MUL: r = m_pComp->Mul(); break;case IDC_DIV: dr = m_pComp->Div(); break;case IDC_MOD: r = Mod(m_iData1, m_iData2); break;}delete m_pComp;if (nID != IDC_DIV) SetDlgItemInt(IDC_RESULT, r); // 顯示整數結果else { // 顯示除法所得的實數結果wchar_t buf[20];swprintf_s(buf, 20, L“%g”, dr);SetDlgItemText(IDC_RESULT, buf);}}//修改點選退出按鈕動作——退出void CExtClientDlg::OnBnClickedCancel(){// TODO:在此新增控制元件通知處理程式程式碼

exit(0); }

8。2。7編譯執行

為了執行客戶程式,需要將客戶程式專案設定成啟動專案。具體做法是,先在專案管理區中選中ExtClient專案,然後選擇選單項“專案\設為啟動專案”。

編譯後,會在解決方案的Debug或Release目錄中生成動態連結庫檔案ExtDll。dll和客戶程式的可執行檔案ExtClient。exe,以及DLL的匯出檔案ExtDll。exp和(靜態連線)庫檔案ExtDll。lib。如果出現:

>ExtClientDlg。obj : error LNK2019: 無法解析的外部符號 “__declspec(dllimport) public: __thiscall CCompute::CCompute(int,int)” (__imp_??0CCompute@@QAE@HH@Z),該符號在函式 “public: void __thiscall CExtClientDlg::Comp(unsigned int)” (?Comp@CExtClientDlg@@QAEXI@Z) 中被引用

1>ExtClientDlg。obj : error LNK2019: 無法解析的外部符號 “__declspec(dllimport) public: virtual __thiscall CCompute::~CCompute(void)” (__imp_??1CCompute@@UAE@XZ),該符號在函式 “public: virtual void * __thiscall CCompute::`scalar deleting destructor‘(unsigned int)” (??_GCCompute@@UAEPAXI@Z) 中被引用

1>D:\ExtDll\Debug\ExtClient。exe : fatal error LNK1120: 7 個無法解析的外部命令

這樣的錯誤是因為缺少lib庫導致新增操作如下,這裡是缺少

右鍵“ExtClient”專案名稱出現

MFC下DLL程式設計(圖解)

下一步新增lib庫

MFC下DLL程式設計(圖解)

新增路徑:

MFC下DLL程式設計(圖解)

問題解決。

執行結果如圖8-9所示:

MFC下DLL程式設計(圖解)

圖8-9 客戶程式ExtClient的執行結果

8。3 規則DLL

使用MFC編寫的規則DLL,雖然只能匯出函式而不能匯出整個類,但是其匯出的函式卻可以其他被非MFC應用程式所呼叫。下面我們仍透過上面的四則運算的例子,看看如何用關鍵字__declspec(dllexport)和extern “C”來編寫和使用匯出若干(全域性)C函式的規則MFC DLL。

8。3。1建立DLL專案

我們建立一個名為RegDll的規則DLL的“Visual C++”之“MFC”的“MFC DLL”專案,注意仍需選中“建立解決方案的目錄”複選框,參見圖8-10。

MFC下DLL程式設計(圖解)

圖8-10 新建MFC DLL專案RegDll的對話方塊

按“確定”鈕,彈出“MFC DLL嚮導”對話方塊。在“DLL型別”欄中,選中“使用共享MFC DLL的規則DLL”單選鈕,參見圖8-11。按“完成”鈕,建立RegDll解決方案和專案。

MFC下DLL程式設計(圖解)

圖8-11 選擇規則DLL的MFC DLL嚮導對話方塊

也可以選擇“帶靜態連結MFC的規則DLL”,差別是所生成的DLL中會包含MFC庫,當然所生成的庫檔案也會大一些(但因此可不用另外安裝MFC動態連結庫)。例如,在此例中,選共享MFC所生成的RegDll。dll檔案只有13KB大,而選擇靜態MFC的則有199KB。

規則DLL專案是使用共享MFC還是使用靜態MFC,也可以在生成DLL專案之後,透過專案屬性對話方塊的“配置屬性\常規”頁中的“MFC的使用”欄中的下拉式列表選項來切換,這一點與普通MFC應用程式專案的類似。

8。3。2 使用DEF檔案匯出函式

1.編輯DEF檔案

在專案管理區中,選擇“解決方案資源管理器”頁,展開“RegDll”專案項,雙擊其“RegDll。def”子項,開啟DLL專案中自動生成的DEF檔案。在該DEF檔案中加入需要匯出的5個函式項:(紅色部分為手工新增的)

; RegDll。def : 宣告 DLL 的模組引數。

LIBRARY “RegDll”

EXPORTS

; 此處可以是顯式匯出

Add @1

Sub @2

Mul @3

Div @4

Mod @5

2.編寫匯出函式程式碼

可以在RegDll專案的應用程式類的程式碼檔案RegDll。cpp的尾部手工新增如下程式碼:

extern “C” int Add(int d1, int d2) { return d1 + d2;}extern “C” int Sub(int d1, int d2) { return d1 - d2;}extern “C” int Mul(int d1, int d2) { return d1 * d2;}extern “C” double Div(int d1, int d2) {if (d2 == 0) {AfxMessageBox(L“Divided by zero!”);return 0;}return (double)d1 / d2;}extern “C” int Mod(int d1, int d2) {return d1 % d2;}

注意,函式前的extern “C”是不可少的,它指定按C語言約定來生成匯出函式。不然,預設情況下,C++編譯器會生成冗長的函式修飾符,不能簡單地用函式名來呼叫。

8。3。3 使用關鍵字__declspec(dllexport)匯出函式

也可以不修改DEF檔案,而在程式碼檔案中直接用關鍵字__declspec(dllexport)和extern “C”來指定匯出函式。對應的程式碼為:(也加在RegDll。cpp的尾部)

#define DllExport extern “C” __declspec(dllexport) DllExport int Add(int d1, int d2) { return d1 + d2;}DllExport int Sub(int d1, int d2) { return d1 - d2;}DllExport int Mul(int d1, int d2) { return d1 * d2;}DllExport double Div(int d1, int d2) {if (d2 == 0) {AfxMessageBox(L“Divided by zero!”);return 0;}return (double)d1 / d2;}DllExport int Mod(int d1, int d2) {if (d2 == 0) {AfxMessageBox(L“Modulo by zero!”);return 0;}return d1 % d2;}

8。3。4編寫客戶程式

1.新增客戶程式專案

與上節類似,為例演示DLL的呼叫,我們也為RegDLL解決方案新增一個客戶程式——基於對話方塊的MFC應用程式專案RegClient,參見圖8-12。

MFC下DLL程式設計(圖解)

圖8-12 新增客戶程式專案RegClient的對話方塊

2.設定依賴項

我們也透過設定DLL專案為客戶專案的依賴項將RegClient與RegDll。dll關聯起來,參見圖8-13。

MFC下DLL程式設計(圖解)

圖8-13 設定RegClient專案依賴項的對話方塊

3.編輯對話方塊資源

為了節省時間,避免重複勞動,可以複製ExtClient專案中的對話方塊。具體做法是:在RegDll解決方案環境中開啟ExtDll解決方案中ExtClient專案的資原始檔ExtClient。rc檔案,(用滑鼠或按Ctrl + A組合鍵)選中其主對話方塊中的所有控制元件,(按Ctrl + C或Ctrl + Insert組合鍵)複製它們到剪接板。然後開啟RegClient專案的主對話方塊編輯器,先刪除其中的所有控制元件,然後再貼上剪接板中的控制元件到對話方塊,參見圖8-8。具體操作如下:

MFC下DLL程式設計(圖解)

開啟ExtClient。rc檔案

MFC下DLL程式設計(圖解)

如下圖

MFC下DLL程式設計(圖解)

選擇主對話方塊:

MFC下DLL程式設計(圖解)

複製貼上

MFC下DLL程式設計(圖解)

4.編寫程式碼

類似ExtClient程式,我們也需要為2個數據編輯框新增類變數,並逐個為運算子按鈕新增單擊事件處理函式。在標頭檔案RegClientDlg。h的尾部會出現如下程式碼:(其中紅色的Comp函式原型是手工新增的)

public:int m_iData1;int m_iData2;void Comp(UINT nID);afx_msg void OnBnClickedAdd();afx_msg void OnBnClickedSub();afx_msg void OnBnClickedMul();afx_msg void OnBnClickedDiv();afx_msg void OnBnClickedMod();

在客戶對話方塊類RegClientDlg。cpp的初始化對話方塊成員函式OnInitDialog中,手工新增設定資料編輯框初值的程式碼(紅色部分):

BOOL CRegClientDlg::OnInitDialog(){CDialog::OnInitDialog();……// TODO: 在此新增額外的初始化程式碼SetDlgItemInt(IDC_DATA1, 5);SetDlgItemInt(IDC_DATA2, 3); return TRUE; // 除非將焦點設定到控制元件,否則返回 TRUE}對應的程式碼檔案RegClientDlg。cpp尾部新增的程式碼為:(其中紅色部分是手工新增的)void CRegClientDlg::OnBnClickedAdd(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_ADD);} void CRegClientDlg::OnBnClickedSub(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_SUB);} void CRegClientDlg::OnBnClickedMul(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_MUL);} void CRegClientDlg::OnBnClickedDiv(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_DIV);} void CRegClientDlg::OnBnClickedMod(){// TODO: 在此新增控制元件通知處理程式程式碼Comp(IDC_MOD);} #define DllImport extern “C” _declspec(dllimport) DllImport int Add(int d1, int d2);DllImport int Sub(int d1, int d2);DllImport int Mul(int d1, int d2);DllImport double Div(int d1, int d2);DllImport int Mod(int d1, int d2); void CRegClientDlg::Comp(UINT nID){UpdateData();int r;double dr;switch(nID) {case IDC_ADD: r = Add(m_iData1, m_iData2); break;case IDC_SUB: r = Sub(m_iData1, m_iData2); break;case IDC_MUL: r = Mul(m_iData1, m_iData2); break;case IDC_MOD: r = Mod(m_iData1, m_iData2); break;case IDC_DIV: dr = Div(m_iData1, m_iData2); break;}if (nID != IDC_DIV) SetDlgItemInt(IDC_RESULT, r);else {wchar_t buf[20];swprintf_s(buf, 20, L“%g”, dr);SetDlgItemText(IDC_RESULT, buf);}}

5.編譯執行

似上節的ExtClient專案,先設定RegClient專案為啟動專案,再編譯執行,結果如圖8-14所示:

MFC下DLL程式設計(圖解)

圖8-14 客戶程式RegClient的執行結果