六. MFC擴展DLL的創建

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。

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