1. 程式人生 > >MFC下DLL程式設計(圖解)一定要認真看(包含了很多東西)

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。

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

8.1.2  放置DLL的目錄

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

  1. 所在目錄——當前程序的可執行模組所在的目錄,即應用程式的可執行檔案(*.exe)所在的目錄。
  2. 當前目錄——程序的當前目錄。
  3. 系統目錄——Windows作業系統安裝目錄的系統子目錄,如C:\Windows\ System32。可用GetSystemDirectory函式檢索此目錄的路徑。
  4. Windows目錄——Windows作業系統安裝目錄,如C:\Windows\。可用GetWindowsDirectory函式檢索此目錄的路徑。
  5. 搜尋目錄——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。

 

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

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

 

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

8.2.2  新增匯出類

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

 

圖8-4  新增類對話方塊

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

 

圖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);

複製程式碼

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

複製程式碼

// 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;

}

複製程式碼

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

 

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

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

8.2.4  新增客戶程式專案

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

 

圖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。

 

圖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。

 

圖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)" ([email protected]@[email protected]@Z),該符號在函式 "public: void __thiscall CExtClientDlg::Comp(unsigned int)" ([email protected]@@[email protected]) 中被引用

1>ExtClientDlg.obj : error LNK2019: 無法解析的外部符號 "__declspec(dllimport) public: virtual __thiscall CCompute::~CCompute(void)" ([email protected]@[email protected]),該符號在函式 "public: virtual void * __thiscall CCompute::`scalar deleting destructor'(unsigned int)" ([email protected]@[email protected]) 中被引用

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

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

右鍵“ExtClient”專案名稱出現

下一步新增lib庫

新增路徑:

 

問題解決。

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

 

圖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。

 

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

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

 

圖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。

 

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

2.設定依賴項

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

 

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

3.編輯對話方塊資源

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

 

開啟ExtClient.rc檔案

 

如下圖

 

選擇主對話方塊:

 

複製貼上

 

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所示:

 

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

相關推薦

MFCDLL程式設計圖解一定認真包含很多東西

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

MFCCSocket程式設計詳解

MFC下CSocket程式設計詳解:  1. 常用的函式和注意事項(詳細的函式介面說明請檢視MSDN):     CSocket::Create 初始化(一般寫伺服器程式都不要用為好,用下面的 CSocket::Socket 初始化)     CSocket::So

新手必粗心的一定看看

roo 技術分享 ack mar packages MF tex 幸好 term 本人centos6.6系統,由於安裝需要,需要安裝python2.7 粗心的地方就在這了 我是先卸載了python2.6命令是:rpm -e rpm -qa|grep python --nod

房價是一定跌的原創首發,深度好文 ——何學林中國房地產大策劃之二

原創作者:何學林中國策劃一人 目前是房地產整個都在跌,但筆者是在房價一片喊漲聲中說的。當時遭到了普遍一致的反對,反對者的觀點是房價一定要漲,不可能跌,因為地球只有一個,土地越來越少,所以房價一定是 漲的。現在看來這種觀點和理由很幼稚,但當時確實都持這種觀點,在房

C++類的靜態成員變數一定初始化分配記憶體

文章轉載自https://my.oschina.net/u/1537391/blog/219432 我們知道C++類的靜態成員變數是需要初始化的,但為什麼要初始化呢。其實這句話“靜態成員變數是需要初始化的”是有一定問題的,應該說“靜態成員變數需要定義”才是準確的,而不是初始化

用webpack2打包包含vue-router釋出。但在tomcat出錯。

遇到個比較奇怪的問題。用了vue-cli初始化的專案,包含了vue-router模板。但釋出到tomcat下面居然報錯。內容是: 在開發模式下除錯程式並沒有報錯。為了證實不是我程式產生的問題,我用vue-cli初始化了一個空專案,一個字都沒改。居然報錯的內容一樣。不知道

MFCDLL/lib的呼叫

 1、簡介: dll和.lib都是程式集合,便於程式碼重用。都是二進位制的檔案。 .dll也叫動態連結庫,與程式連結的方式為執行時連結(run-time linked),為PE(portable executable)格式,也就是程完整的程式。.exe、.dll、.fon、.mod、.drv、.ocx等等都

跟著alex學習格式化輸出,最大的感受就是程式設計這個事,一定自己動手去做,才能學會。會和自己會做完全是兩碼事

學習了三天,現在學到格式化輸出。看視訊教程,alex和那個美女學員打情罵俏,真是羨慕啊。 教程看懂很容易,完全會了。 可是上手程式設計馬上歇菜。 就這麼幾行的程式碼,我遇到了n多錯誤 首先是中文輸入的錯誤,輸入括號,居然就報錯,仔細一看是中文的括號。 然後是單引號和雙引號的錯誤,這也屬於中文輸入法的

深入Preact原始碼jsx轉化成virtualDOM發生什麼

本文和自己在掘金的同步 jsx要轉化成virtualDOM,首先經過babel,再經過h函式的呼叫形成virtualDOM。具體如下 原始碼連結 ./src/h.js 相當於react得createElement(),jsx經過babel轉碼後是h的迴圈

Win7 VS2010裝不上裝frame work 4.0 就報錯:oxc8000222

 1.開始——執行——輸入cmd(以管理員身份執行)——回車——在開啟的視窗中輸入net stop WuAuServ 2.開始——執行——輸入%windir% 3.在開啟的視窗中有個資料夾叫SoftwareDistribution,把它重新命名為SDold 4.開始—

centos 6.5 安裝qemu-2.1.3雖然失敗但還是學到一些東西

在http://wiki.qemu.org/Download中下載關於qemu的原始碼壓縮檔案,並放入/home/qemu中 cd /home/qemu tar -jxvf qemu-2.1.3.tar.bz2進入/home/qemu/qemu-2.1.3中,並進行配置,

ibatis 獲取表格條數countibatis對於測試、校驗資料是個好東西

校驗list好麻煩,校驗條數還是比較方便的,也比較有用。 1、定義資料訪問介面dao publicint countAll(); 2、建立MyBatis對映檔案(UserDaoMapper.xml)mapping <select id="countAll"

讀《大學生上課為什麽一定認真聽講》有感

學生 自己 www. 很好 觀後感 很多 就會 cal 為什麽 http://www.scalerstalk.com/816-attention 大學生上課為什麽一定要認真聽講:可能很多大學生會覺得在大學老師教的東西不多,老師的水平不高。就會在課堂放水。雖然我也有過這樣的一

詳細講解transform,一就懂!(贊

前面我們一起學習了CSS3中的漸變、圓角、陰影等幾個屬性的使用,今天開始我們一起來學習有關於CSS3製作動畫的幾個屬性:等更高階的CSS3技術。本文主要介紹的是這三個屬性之中的第一個──變形transform。 Transform字面上就是變形,改變的意思。在CSS3中

IDictionary 序列化一定實現父類實現ISerializable介面的,子類也必須有序列化建構函式,否則反序列化時會出錯。

//public class ThreadSafeDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICloneable [Serializable] public class

Windows-API程式例子--//一定學會MSDN

//一定要學會看MSDN #include "StdAfx.h" #include <windows.h> LRESULT  CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);//視窗函式宣告,這一函式將處理髮往應用程式視窗的各

MFC/Qt呼叫caffe原始碼---MFC呼叫caffe的動態連線庫dll檔案

首先,先看下最後的效果吧。 win7 vs2013 新建一個MFC 工程 注:MFC中如何最簡便的方法將圖片顯示到對話方塊中?大家可以搜下,很多種方法,但是我採用的是最簡單的方法,即將opencv視窗繫結到MFC的PIcture control上。 在:

MFC與Matlab程式設計總結 以《Matlab與C/C++混合程式設計技術第三版》-劉維 第五章 生成DLL為例

     近期要完成一個任務,把人臉超解析度的演算法整合在一個系統中,嵌入人臉庫及字典集等。老闆的要求是有比較好的介面,目前也只能是VS那一套了,前一段時間完成的專案是用的MFC,這次也就是用MFC來完成吧。但是問題是,以前寫的人臉超解析度的演算法都是用Matlab寫的,Matlab有非常好的矩陣計算能力,要

java程式設計師菜鳥進階十三linux基礎入門vmvare安裝linux RedHat圖解超詳細篇

對於linux,我從大二就想學習一下,但一直苦於無從下手,所以一直拖到現在,鑑於筆者瞭解很多人在linux入門的困難在何處,所以我認為本套入門基礎文章還是挺適合想學習linux的朋友,本系列文章大約十篇文章左右,近期會不斷更新下來,沒有linux基礎但又想學習linux的朋友可以關注一下本系列

WindowsJDK的安裝與環境變數的配置教程附詳細圖解

JDK安裝配置教程 一、下載jdk 1.首先要確定自己電腦的作業系統是多少位的: 右鍵“我的電腦”>>選擇“屬性”>>系統型別(如下圖,我的是win7 64位) 2.然後到官網下載適合自己電腦版本的jdk。 官網連結:http://www.oracle.