MFC DLL—Regular DLL and Extension DLL

2  MFC DLL ——擴展的和正規的

MFC 的 AppWizard 可以讓我們創建 MFC 庫支持的兩種 DLL :擴展的 DLL 和正規的 DLL 。這兩種類型的區別是什麼呢?

說明: 當然, Developer Studio 也讓我們創建純的、與 MFC 庫無關的 Win32 DLL ,就像它讓我們創建與 MFC 庫無關的 Windows 程序一樣。

²      擴展 DLL 支持 C++ 接口,換句話說,該 DLL 可以導出整個類,客戶可以構造這些類的對象或從這些類進行派生。擴展 DLL 動態連接到 MFC 庫的 DLL 版本的代碼,因此,擴展 DLL 要求客戶程序被動態連接到 MFC 庫 (AppWizard 默認設置 ) ,並且客戶程序和擴展 DLL 要一致連接到 MFC DLL 的相同版本 (mfc42.dll 、 mfcd42.dll 等 ) 。擴展 DLL 很小,我們可以創建一個簡單的擴展 DLL ,大約 10KB 左右,它的裝載會很快。

²      如果我們需要一個 DLL ,並希望它可以被任何 Win32 編程環境 ( 包括 Visual Basic 5.0) 裝載,那麼我們必須使用正規 DLL 。這裏有一個很大的限制就是,正規 DLL 可以導出 C 風格的函數,但不能導出 C++ 類、成員函數或重載函數,因爲每一個 C++ 編譯器都有自己修飾名字的方法。不過,我們可以在正規 DLL 內部使用 C++ 類 ( 特別是 MFC 庫的類 ) 。

當我們創建 MFC 正規 DLL 時,我們可以選擇靜態連接或動態連接到 MFC 庫。如果選擇了靜態連接, DLL 將包括所有它需要的 MFC 庫代碼的拷貝,因此它可以獨立於 MFC 庫。一個典型的 Release 版本靜態連接的正規 DLL 大約爲 144KB 左右。如果選擇動態連接,大小可降到 17KB 左右,但必須保證適當的 MFC DLL 在目標機器上存在。

當我們指定 AppWizard 創建 DLL 或 EXE 的類型時,編譯器 #define 常量將按下表設置:

 
 動態連接到共享的 MFC 庫
 靜態連接到 MFC 庫
 
正規 DLL
 _AFXDLL 、 _USRDLL
 _USRDLL
 
擴展 DLL
 _AFXEXT 、 _AFXDLL
                  不支持                 
 
客戶 EXE
 _AFXDLL
 沒有定義常量
 

如果我們查看 MFC 源代碼和頭文件,將會看到大量的這些常量的 #ifdef 語句。這表明庫代碼的編譯也是非常不同的,具體取決於我們產生工程時指定的類型。

2.1  共享的MFC DLL 和Windows DLL

²      如果我們使用共享 MFC DLL 選項創建一個 Windows 的 Debug 目標的程序,則該程序將動態連接到下列一個或多個 (ANSI)MFC DLL :

mfc42d.dll
 核心 MFC 類
 
mfco42d.dll
 ActiveX(OLE) 類
 
mfcd42d.dll
 數據庫類 (ODBC 和 DAO)
 
Mfcn42.dll
 Winsock 和 WinInet 類
 

²      當創建 Release 目標時,程序只是動態連接到 mfc42.dll 。對這些 MFC DLL 的連接都是通過導入庫隱式連接。我們也可以假定隱式連接到 Windows 裏的 ActiveX 和 ODBC 的 DLL ,這種情況下,我們可以認爲當裝入用 Release 目標創建的客戶時,所有這些 DLL 都被連接到了程序中,而不管客戶是否使用了 ActiveX 和 ODBC 特性。然而,實際情況並不是這樣。通過一些創造性的手段,當這些函數中的某一個首先被調用時, MFC 會顯示裝入 ActiveX 和 ODBC DLL( 通過調用 LoadLibrary) 。這樣客戶程序只是裝入自己所需要的 DLL 。

2.2  MFC 擴展DLL ——導出類

如果擴展 DLL 只包含被導出的 C++ 類,那麼我們可以很方便地創建和使用該 DLL 。創建 EX21A 示例程序 (P433) 的步驟顯示瞭如何讓 AppWizard 創建一個擴展 DLL 的框架。該框架只有 DllMain 函數,我們只要簡單地把 C++ 類加到工程裏。但有一件特殊的事情我們必須要做,即我們必須把宏 AFX_EXT_CLASS 加到類聲明中,如下所示:

view plaincopy to clipboardprint?
class AFX_EXT_CLASS Cstudent:public Cobject 
class AFX_EXT_CLASS Cstudent:public Cobject

不僅對 DLL 工程中的 H 文件要作這樣的修改,對客戶程序使用的 H 文件同樣也要作修改。換句話說, H 文件對於客戶和 DLL 是一樣的。該宏根據相應條件會產生不同的代碼——在 DLL 裏導出類,在客戶程序裏導入類。

2.3  MFC 擴展DLL 資源搜索的順序

如果我們創建一個動態連接到 MFC 的客戶程序,許多 MFC 庫的標準資源 ( 錯誤信息字符串和打印預覽對話框模板等 ) 都被保存在 MFC DLL 裏 (mfc42.dll 、 mfcd42.dll 等 ) ,但應用程序也有自己的資源。當程序調用一個 MFC 函數 ( 如 Cstring::LoadString 或 Cbitmap::LoadBitmap) 時,框架首先搜索 EXE 的資源,然後搜索 MFC DLL 的資源 。

如果程序包含一個擴展的 DLL ,並且 EXE 需要一個資源,則搜索順序爲:首先是 EXE 文件,然後是擴展 DLL ,再是 MFC DLL 。例如我們有一個字符串資源 ID ,在所有資源中它是唯一的,則 MFC 庫將會找到該資源。如果在 EXE 文件和擴展 DLL 文件裏有重複的字符串 ID ,則 MFC 庫會裝入 EXE 文件裏的字符串。

如果擴展 DLL 裝入一個資源,則搜索序列爲:首先是擴展 DLL ,然後是 MFC DLL ,再是 EXE 。

說明: 如果需要的話,我們可以改變搜索序列。假定我們希望 EXE 代碼首先搜索擴展 DLL 的資源,則可以使用下面的代碼:

+ expand sourceview plaincopy to clipboardprint?
HINSTANCE hInstResourceClient=AfxGetResourceHandle();  
//Use DLL's instance handle  
AfxSetResourceHandle(::GetModuleHandle("mydllname.dll"));  
CString strRes;  
strRes.LoadString(IDS_MYSTRING);  
//Restore client's instance handle  
AfxSetResourceHandle(hInstResourceClient); 
HINSTANCE hInstResourceClient=AfxGetResourceHandle();
//Use DLL's instance handle
AfxSetResourceHandle(::GetModuleHandle("mydllname.dll"));
CString strRes;
strRes.LoadString(IDS_MYSTRING);
//Restore client's instance handle
AfxSetResourceHandle(hInstResourceClient);

 


我們不能用 AfxGetInstanceHandle 代替 ::GetModuleHandle 。在一個擴展 DLL 裏, AfxGetInstanceHandle 返回的是 EXE 的實例句柄,而不是 DLL 的句柄。

2.4  如何使用MFC 擴展DLL

u      將一個獨立的類 MyClass ,做成一個擴展的 DLL

a)         首先製作一個單獨的功能簡單的類,內容如下:

+ expand sourceview plaincopy to clipboardprint?
// MyClass.h  
class CMyClass  
{     
public:   
    CMyClass();   
    void SetValue(int n);     
    void GetValue();      
private:      
    int _a;   
};  
// MyClass.cpp  
#include "StdAfx.h"  
#include "MyClass.h"  
CMyClass::CMyClass()  
{     
    printf("CMyClass::CMyClass()/n");     
}  
void CMyClass::SetValue(int n)  
{     
    printf("SetValue(int n)/n");      
    _a=n;     
}  
void CMyClass::GetValue()  
{     
    printf("GetValue()/n");   
    printf("_a=%d/n",_a);     

// MyClass.h
class CMyClass

public: 
 CMyClass(); 
 void SetValue(int n); 
 void GetValue(); 
private: 
 int _a; 
};
// MyClass.cpp
#include "StdAfx.h"
#include "MyClass.h"
CMyClass::CMyClass()

 printf("CMyClass::CMyClass()/n"); 
}
void CMyClass::SetValue(int n)

 printf("SetValue(int n)/n"); 
 _a=n; 
}
void CMyClass::GetValue()

 printf("GetValue()/n"); 
 printf("_a=%d/n",_a); 
}

b)         然後,運行 AppWizard 產生一個擴展的 DLL ,通過選擇 MFC AppWizard(dll)->MFC Extension DLL->Finish ,工程名爲 ex21a 。  

c)         將 MyClass.h 和 MyClass.cpp 這兩個文件拷貝到剛纔創建的工程中,通過 Project->Add to Project->Files 。添加後,編輯 MyClass.h 文件如下:

+ expand sourceview plaincopy to clipboardprint?
// MyClass.h  
// 擴展DLL使用AFX_EXT_CLASS宏導出類,  
// 在_AFXDLL和_AFXEXT被定義時,AFX_EXT_CLASS宏被定義爲:  
// #define AFX_EXT_CLASS       AFX_CLASS_EXPORT  
// #define AFX_CLASS_EXPORT __declspec(dllexport)  
//class CMyClass  
class AFX_EXT_CLASS CMyClass // 修改此處  
{     
public:   
    CMyClass();   
    void SetValue(int n);     
    void GetValue();      
private:      
    int _a;   
}; 
// MyClass.h
// 擴展DLL使用AFX_EXT_CLASS宏導出類,
// 在_AFXDLL和_AFXEXT被定義時,AFX_EXT_CLASS宏被定義爲:
// #define AFX_EXT_CLASS       AFX_CLASS_EXPORT
// #define AFX_CLASS_EXPORT __declspec(dllexport)
//class CMyClass
class AFX_EXT_CLASS CMyClass // 修改此處

public: 
 CMyClass(); 
 void SetValue(int n); 
 void GetValue(); 
private: 
 int _a;
};

d)         最後編譯工程,得到了我們需要的兩個文件 ex21a.dll 和 ex21a.lib 。

u          DLL 測試客戶程序

a)         運行 AppWizard 產生一個 Win32 控制檯程序,通過選擇 Win32 Console Application-> 工程名爲 client-> 一個空白工程,然後再新建一個 C++ Source File ,文件名爲 client 。完成後,在 client.cpp 文件中添加如下測試代碼:

+ expand sourceview plaincopy to clipboardprint?
// client.cpp  
#include <stdio.h>  
#include <iostream>  
#include "MyClass.h"  
int main()  
{  
    printf("main()/n");   
    int nVal;     
    nVal=100;     
      
    //測試使用我們導入的類      
    CMyClass mc;      
    mc.SetValue(nVal);    
    mc.GetValue();    
      
    system("pause");      
    return 0;  

// client.cpp
#include <stdio.h>
#include <iostream>
#include "MyClass.h"
int main()
{
 printf("main()/n"); 
 int nVal; 
 nVal=100; 
 
 //測試使用我們導入的類 
 CMyClass mc; 
 mc.SetValue(nVal); 
 mc.GetValue(); 
 
 system("pause"); 
 return 0;
}

b)         修改設置,在 Project->Settings->General-> 在 Microsoft Foundation Classes 中選擇 Use MFC in a Shared DLL 。

c)         將 ex21a.dll 、 ex21a.lib 和 MyClass.h 三個文件拷貝到 client 工程目錄下,類似上述相同的方法,將 MyClass.h 添加到工程中,並且在 Project->Settings->Link ,在 Object/library modules 中添加 ex21a.lib( 多個 lib 用空格分開 ) 。

d)         修改 MyClass.h 文件的內容。當時我在調試的時候,出現了錯誤,需要小心。我們在擴展 DLL 工程中的 MyClass.h 文件中使用宏 AFX_EXT_CLASS 來聲明導出類,而同時要在 client 工程中的 MyClass.h 文件中使用宏 AFX_EXT_CLASS 來聲明導入類。但是在我編譯 client 工程的時候問題出現了:

error C2079: 'CMyClass' uses undefined class 'AFX_EXT_CLASS'

提示 AFX_EXT_CLASS 沒有定義,應該是少加頭文件了。在網上 g 一下找到了一些相同的問題貼:

http://www.eggheadcafe.com/software/aspnet/29755654/problem-using-self-built.aspx

http://hi.baidu.com/qingdi963/blog/item/c75fa8501ffada878c5430b7.html/cmtid/24b28d832df3729af703a6c7#24b28d832df3729af703a6c7

http://msdn.microsoft.com/en-us/library/h5f7ck28.aspx (Extension DLLs)

http://msdn.microsoft.com/en-us/library/4fezhh3d.aspx (DLLHUSK Sample: Dynamically Links the MFC Library)

http://www.codeguru.com/Cpp/W-P/dll/article.php/c119 (Using one extension DLL in another)

 

解決方法如下,修改 MyClass.h 文件內容爲:

方法 1 :

+ expand sourceview plaincopy to clipboardprint?
// MyClass.h  
// 客戶程序使用AFX_EXT_CLASS宏導入類,  
// 在只定義了_AFXDLL時,AFX_EXT_CLASS宏被定義爲:  
// #define AFX_EXT_CLASS       AFX_CLASS_IMPORT  
// #define AFX_CLASS_IMPORT __declspec(dllimport)  
#include <afxext.h> // 缺少頭文件MFC extensions (可以在StdAfx.h中找到)  
//class CMyClass  
class AFX_EXT_CLASS CMyClass  
{     
public:   
    CMyClass();   
    void SetValue(int n);     
    void GetValue();      
private:  
    int _a;      
}; 
// MyClass.h
// 客戶程序使用AFX_EXT_CLASS宏導入類,
// 在只定義了_AFXDLL時,AFX_EXT_CLASS宏被定義爲:
// #define AFX_EXT_CLASS       AFX_CLASS_IMPORT
// #define AFX_CLASS_IMPORT __declspec(dllimport)
#include <afxext.h> // 缺少頭文件MFC extensions (可以在StdAfx.h中找到)
//class CMyClass
class AFX_EXT_CLASS CMyClass

public: 
 CMyClass(); 
 void SetValue(int n); 
 void GetValue(); 
private:
 int _a;   
};

方法 2 :

+ expand sourceview plaincopy to clipboardprint?
// MyClass.h  
// 客戶程序使用AFX_EXT_CLASS宏導入類,  
// 在只定義了_AFXDLL時,AFX_EXT_CLASS宏被定義爲:  
// #define AFX_EXT_CLASS       AFX_CLASS_IMPORT  
// #define AFX_CLASS_IMPORT __declspec(dllimport)  
//class CMyClass  
//class AFX_EXT_CLASS CMyClass  
class __declspec(dllimport) CMyClass //通過上述分析,直接使用__declspec(dllimport)  
{     
public:   
    CMyClass();   
    void SetValue(int n);     
    void GetValue();      
private:      
    int _a;       
}; 
// MyClass.h
// 客戶程序使用AFX_EXT_CLASS宏導入類,
// 在只定義了_AFXDLL時,AFX_EXT_CLASS宏被定義爲:
// #define AFX_EXT_CLASS       AFX_CLASS_IMPORT
// #define AFX_CLASS_IMPORT __declspec(dllimport)
//class CMyClass
//class AFX_EXT_CLASS CMyClass
class __declspec(dllimport) CMyClass //通過上述分析,直接使用__declspec(dllimport)

public: 
 CMyClass(); 
 void SetValue(int n); 
 void GetValue(); 
private: 
 int _a;     
};

e)         現在重新編譯沒問題了,可以得到如下的輸出結果,即可以在 client 程序中成功地調用我們在擴展 DLL 中導出的類了。

main()

CMyClass::CMyClass()

SetValue(int n)

GetValue()

_a=100

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/delphiwcdj/archive/2010/01/06/5142937.aspx

發佈了12 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章