MFC擴展DLL的內涵爲MFC的擴展,用戶使用MFC擴展DLL就像使用MFC本身的DLL一樣。除了可以在MFC擴展DLL的內部使用MFC以外, MFC擴展DLL與應用程序的接口部分也可以是MFC。我們一般使用MFC擴展DLL來包含一些MFC的增強功能,譬如擴展MFC的CStatic、 CButton等類使之具備更強大的能力。
使用Visual C++嚮導生產MFC擴展DLL時,MFC嚮導會自動增加DLL的入口函數DllMain:
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// Remove this if you use lpReserved
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("MFCEXPENDDLL.DLL Initializing!/n");
// Extension DLL one-time initialization
if (!AfxInitExtensionModule(MfcexpenddllDLL, hInstance))
return 0;
// Insert this DLL into the resource chain
// NOTE: If this Extension DLL is being implicitly linked to by
// an MFC Regular DLL (such as an ActiveX Control)
// instead of an MFC application, then you will want to
// remove this line from DllMain and put it in a separate
// function exported from this Extension DLL. The Regular DLL
// that uses this Extension DLL should then explicitly call that
// function to initialize this Extension DLL. Otherwise,
// the CDynLinkLibrary object will not be attached to the
// Regular DLL's resource chain, and serious problems will
// result.
new CDynLinkLibrary(MfcexpenddllDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("MFCEXPENDDLL.DLL Terminating!/n");
// Terminate the library before destructors are called
AfxTermExtensionModule(MfcexpenddllDLL);
}
return 1; // ok
}
上述代碼完成MFC擴展DLL的初始化和終止處理。
由於MFC擴展DLL導出函數和變量的方式與其它DLL沒有什麼區別,我們不再細緻講解。下面直接給出一個MFC擴展DLL的創建及在應用程序中調用它的例子。
6.1 MFC擴展DLL的創建
下面我們將在MFC擴展DLL中 導出一個按鈕類CSXButton(擴展自MFC的CButton類),類CSXButton是一個用以取代 CButton的類,它使你能在同一個按鈕上顯示位圖和文字,而MFC的按鈕僅可顯示二者之一。類CSXbutton的源代碼在Internet上廣泛流 傳,有很好的“羣衆基礎”,因此用這個類來講解MFC擴展DLL有其特殊的功效。
MFC中包含一些宏,這些宏在DLL和調用DLL的應用程序中被以不同的方式展開,這使得在DLL和應用程序中,使用統一的一個宏就可以表示出輸出和輸入的不同意思:
// for data
#ifndef AFX_DATA_EXPORT
#define AFX_DATA_EXPORT __declspec(dllexport)
#endif
#ifndef AFX_DATA_IMPORT
#define AFX_DATA_IMPORT __declspec(dllimport)
#endif
// for classes
#ifndef AFX_CLASS_EXPORT
#define AFX_CLASS_EXPORT __declspec(dllexport)
#endif
#ifndef AFX_CLASS_IMPORT
#define AFX_CLASS_IMPORT __declspec(dllimport)
#endif
// for global APIs
#ifndef AFX_API_EXPORT
#define AFX_API_EXPORT __declspec(dllexport)
#endif
#ifndef AFX_API_IMPORT
#define AFX_API_IMPORT __declspec(dllimport)
#endif
#ifndef AFX_EXT_DATA
#ifdef _AFXEXT
#define AFX_EXT_CLASS AFX_CLASS_EXPORT
#define AFX_EXT_API AFX_API_EXPORT
#define AFX_EXT_DATA AFX_DATA_EXPORT
#define AFX_EXT_DATADEF
#else
#define AFX_EXT_CLASS AFX_CLASS_IMPORT
#define AFX_EXT_API AFX_API_IMPORT
#define AFX_EXT_DATA AFX_DATA_IMPORT
#define AFX_EXT_DATADEF
#endif
#endif
導出一個類,直接在類聲明頭文件中使用AFX_EXT_CLASS即可,以下是導出CSXButton類的例子:
#ifndef _SXBUTTON_H
#define _SXBUTTON_H
#define SXBUTTON_CENTER -1
class AFX_EXT_CLASS CSXButton : public CButton
{
// Construction
public:
CSXButton();
// Attributes
private:
// Positioning
BOOL m_bUseOffset;
CPoint m_pointImage;
CPoint m_pointText;
int m_nImageOffsetFromBorder;
int m_nTextOffsetFromImage;
// Image
HICON m_hIcon;
HBITMAP m_hBitmap;
HBITMAP m_hBitmapDisabled;
int m_nImageWidth, m_nImageHeight;
// Color Tab
char m_bColorTab;
COLORREF m_crColorTab;
// State
BOOL m_bDefault;
UINT m_nOldAction;
UINT m_nOldState;
// Operations
public:
// Positioning
int SetImageOffset( int nPixels );
int SetTextOffset( int nPixels );
CPoint SetImagePos( CPoint p );
CPoint SetTextPos( CPoint p );
// Image
BOOL SetIcon( UINT nID, int nWidth, int nHeight );
BOOL SetBitmap( UINT nID, int nWidth, int nHeight );
BOOL SetMaskedBitmap( UINT nID, int nWidth, int nHeight, COLORREF crTransparentMask );
BOOL HasImage() { return (BOOL)( m_hIcon != 0 | m_hBitmap != 0 ); }
// Color Tab
void SetColorTab(COLORREF crTab);
// State
BOOL SetDefaultButton( BOOL bState = TRUE );
private:
BOOL SetBitmapCommon( UINT nID, int nWidth, int nHeight, COLORREF crTransparentMask, BOOL bUseMask );
void CheckPointForCentering( CPoint &p, int nWidth, int nHeight );
void Redraw();
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSXButton)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CSXButton();
// Generated message map functions
protected:
//{{AFX_MSG(CSXButton)
afx_msg LRESULT OnGetText(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#endif
把SXBUTTON.CPP文件直接添加到工程,編譯工程,得到“mfcexpenddll.lib”和“mfcexpenddll.dll”兩個文 件。我們用Visual Studio自帶的Depends工具可以查看這個.dll,發現其導出了衆多符號(見圖15)。
這些都是類的構造函數、析構函數及其它成員函數和變量經編譯器處理過的符號,我們直接用__declspec(dllexport)語句聲明類就導出了這些符號。
如果我們想用.lib文件導出這些符號,是非常困難的,我們需要在工程中生成.map文件,查詢.map文件的符號,然後將其一一導出。如圖16,打開 DLL工程的settings選項,再選擇Link,勾選其中的產生MAP文件(Generate mapfile)就可以產生.map文件了。
打開mfcexpenddll工程生成的.map文件,我們發現其中包含了圖15中所示的符號(symbol)
0001:00000380 ?HasImage@CSXButton@@QAEHXZ 10001380 f i SXBUTTON.OBJ
0001:000003d0 ??0CSXButton@@QAE@XZ 100013d0 f SXBUTTON.OBJ
0001:00000500 ??_GCSXButton@@UAEPAXI@Z 10001500 f i SXBUTTON.OBJ
0001:00000570 ??_ECSXButton@@UAEPAXI@Z 10001570 f i SXBUTTON.OBJ
0001:00000630 ??1CSXButton@@UAE@XZ 10001630 f SXBUTTON.OBJ
0001:00000700 ?_GetBaseMessageMap@CSXButton@@KGPBUAFX_MSGMAP@@XZ 10001700 f SXBUTTON.OBJ
0001:00000730 ?GetMessageMap@CSXButton@@MBEPBUAFX_MSGMAP@@XZ 10001730 f SXBUTTON.OBJ
0001:00000770 ?Redraw@CSXButton@@AAEXXZ 10001770 f i SXBUTTON.OBJ
0001:000007d0 ?SetIcon@CSXButton@@QAEHIHH@Z 100017d0 f SXBUTTON.OBJ
……………………………………………………………………..//省略
所以,對於MFC擴展DLL,我們不宜以.lib文件導出類。
6.2 MFC擴展DLL的調用
在DLL所在工作區新增一個dllcall工程,它是一個基於對話框的MFC EXE程序。在其中增加兩個按鈕SXBUTTON1、SXBUTTON2,並設置其屬性爲“Owner draw”,如圖17。
在工程中添加兩個ICON資源:IDI_MSN_ICON(MSN的圖標)、IDI_REFBAR_ICON(Windows的系統圖標)。
修改工程的“calldllDlg.h”頭文件爲:
#include "../../mfcexpenddll/SXBUTTON.h" //包含dll的導出類頭文件
#pragma comment(lib,"mfcexpenddll.lib") //隱式鏈接dll
/////////////////////////////////////////////////////////////////////////////
// CCalldllDlg dialog
class CCalldllDlg : public CDialog
{
// Construction
public:
CCalldllDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CCalldllDlg)
enum { IDD = IDD_CALLDLL_DIALOG };
//增加與兩個按鈕對應的成員變量
CSXButton m_button1;
CSXButton m_button2;
…
}
同時,修改“calldllDlg.cpp”文件,使得m_button1、m_button2成員變量與對話框上的按鈕控件建立關聯:
void CCalldllDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCalldllDlg)
DDX_Control(pDX, IDC_BUTTON2, m_button2);
DDX_Control(pDX, IDC_BUTTON1, m_button1);
//}}AFX_DATA_MAP
}
修改BOOL CCalldllDlg::OnInitDialog()函數,在其中增加對兩個按鈕設置ICON的代碼:
BOOL CCalldllDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_button1.SetIcon(IDI_MSN_ICON,16,16);
m_button2.SetIcon(IDI_REFBAR_ICON,16,16);
return TRUE; // return TRUE unless you set the focus to a control
}
運行程序,將出現如圖18的對話框,圖形和文字同時出現在按鈕上,這說明我們正確地調用了MFC擴展DLL。
如果我們不修改void CCalldllDlg::DoDataExchange(CDataExchange* pDX),即不增加下列代碼:
DDX_Control(pDX, IDC_BUTTON2, m_button2);
DDX_Control(pDX, IDC_BUTTON1, m_button1);
我們也可以在BOOL CCalldllDlg::OnInitDialog()函數中添加如下代碼實現m_button1、m_button2與IDC_BUTTON1、IDC_BUTTON2的關聯:
m_button1.SubclassDlgItem(IDC_BUTTON1, this);
m_button2.SubclassDlgItem(IDC_BUTTON2, this);
但是,DDX_Control與按鈕類的SubclassDlgItem成員函數不能同時存在,否則程序會出錯。
6.3 總結
在MFC擴展DLL中需要將AFX_MANAGE_STATE(AfxGetStaticModuleState());
換成AFX_MANAGE_STATE(AfxGetAppModuleState());才能正確切換當前模塊狀態。
由以上分析可知,MFC擴展DLL的導出與引用方式與前幾節所講述的方式沒有太大的差別,MFC擴展DLL主要強調對MFC進行功能擴展。因此,如果DLL的目標不是增強MFC的功能,其與應用程序的接口也不是MFC,請不要將DLL建立爲MFC擴展DLL。