DLL動態鏈接庫編程入門之五: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:

C++代碼
  1. extern "C" int APIENTRY   
  2. DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)   
  3. {   
  4.    // Remove this if you use lpReserved   
  5.    UNREFERENCED_PARAMETER(lpReserved);   
  6.   
  7.    if (dwReason == DLL_PROCESS_ATTACH)   
  8.    {   
  9.       TRACE0("MFCEXPENDDLL.DLL Initializing!\n");   
  10.   
  11.       // Extension DLL one-time initialization   
  12.       if (!AfxInitExtensionModule(MfcexpenddllDLL, hInstance))   
  13.         return 0;   
  14.   
  15.       // Insert this DLL into the resource chain   
  16.       // NOTE: If this Extension DLL is being implicitly linked to by   
  17.       //  an MFC Regular DLL (such as an ActiveX Control)   
  18.       //  instead of an MFC application, then you will want to   
  19.       //  remove this line from DllMain and put it in a separate   
  20.       //  function exported from this Extension DLL.  The Regular DLL   
  21.       //  that uses this Extension DLL should then explicitly call that   
  22.       //  function to initialize this Extension DLL.  Otherwise,   
  23.       //  the CDynLinkLibrary object will not be attached to the   
  24.       //  Regular DLL's resource chain, and serious problems will   
  25.       //  result.   
  26.   
  27.       new CDynLinkLibrary(MfcexpenddllDLL);   
  28.    }   
  29.    else if (dwReason == DLL_PROCESS_DETACH)   
  30.    {   
  31.       TRACE0("MFCEXPENDDLL.DLL Terminating!\n");   
  32.       // Terminate the library before destructors are called   
  33.       AfxTermExtensionModule(MfcexpenddllDLL);   
  34.    }   
  35.    return 1;   // ok   
  36. }  

       上述代碼完成MFC擴展DLL的初始化和終止處理。

  由於MFC擴展DLL導出函數和變量的方式與其它DLL沒有什麼區別,我們不再細緻講解。下面直接給出一個MFC擴展DLL的創建及在應用程序中調用它的例子。

       1、MFC擴展DLL的創建

  下面我們將在MFC擴展DLL中導出一個按鈕類CSXButton(擴展自MFC的CButton類),類CSXButton是一個用以取代 CButton的類,它使你能在同一個按鈕上顯示位圖和文字,而MFC的按鈕僅可顯示二者之一。類CSXbutton的源代碼在Internet上廣泛流傳,有很好的“羣衆基礎”,因此用這個類來講解MFC擴展DLL有其特殊的功效。

  MFC中包含一些,這些宏在DLL和調用DLL的應用程序中被以不同的方式展開,這使得在DLL和應用程序中,使用統一的一個宏就可以表示出輸出和輸入的不同意思:

C++代碼
  1. // for data   
  2. #ifndef AFX_DATA_EXPORT   
  3.  #define AFX_DATA_EXPORT __declspec(dllexport)   
  4. #endif   
  5. #ifndef AFX_DATA_IMPORT   
  6.  #define AFX_DATA_IMPORT __declspec(dllimport)   
  7. #endif   
  8.   
  9. // for classes   
  10. #ifndef AFX_CLASS_EXPORT   
  11.  #define AFX_CLASS_EXPORT __declspec(dllexport)   
  12. #endif   
  13. #ifndef AFX_CLASS_IMPORT   
  14.  #define AFX_CLASS_IMPORT __declspec(dllimport)   
  15. #endif   
  16.   
  17. // for global APIs   
  18. #ifndef AFX_API_EXPORT   
  19.  #define AFX_API_EXPORT __declspec(dllexport)   
  20. #endif   
  21. #ifndef AFX_API_IMPORT   
  22.  #define AFX_API_IMPORT __declspec(dllimport)   
  23. #endif   
  24.   
  25. #ifndef AFX_EXT_DATA   
  26.  #ifdef _AFXEXT   
  27.   #define AFX_EXT_CLASS       AFX_CLASS_EXPORT   
  28.   #define AFX_EXT_API         AFX_API_EXPORT   
  29.   #define AFX_EXT_DATA        AFX_DATA_EXPORT   
  30.   #define AFX_EXT_DATADEF   
  31.  #else   
  32.   #define AFX_EXT_CLASS       AFX_CLASS_IMPORT   
  33.   #define AFX_EXT_API         AFX_API_IMPORT   
  34.   #define AFX_EXT_DATA        AFX_DATA_IMPORT   
  35.   #define AFX_EXT_DATADEF   
  36.  #endif   
  37. #endif  

       導出一個類,直接在類聲明頭文件中使用AFX_EXT_CLASS即可,以下是導出CSXButton類的例子:

C++代碼
  1. #ifndef _SXBUTTON_H   
  2. #define _SXBUTTON_H   
  3.   
  4. #define SXBUTTON_CENTER -1   
  5.   
  6. class AFX_EXT_CLASS CSXButton : public CButton   
  7. {   
  8. // Construction   
  9. public:   
  10.  CSXButton();   
  11.   
  12. // Attributes   
  13. private:   
  14.  // Positioning   
  15.  BOOL  m_bUseOffset;       
  16.  CPoint  m_pointImage;   
  17.  CPoint  m_pointText;   
  18.  int   m_nImageOffsetFromBorder;   
  19.  int   m_nTextOffsetFromImage;   
  20.   
  21.  // Image   
  22.  HICON  m_hIcon;        
  23.  HBITMAP  m_hBitmap;   
  24.  HBITMAP  m_hBitmapDisabled;   
  25.  int   m_nImageWidth, m_nImageHeight;   
  26.   
  27.  // Color Tab   
  28.  char  m_bColorTab;       
  29.  COLORREF m_crColorTab;   
  30.   
  31.  // State   
  32.  BOOL  m_bDefault;   
  33.  UINT  m_nOldAction;   
  34.  UINT  m_nOldState;   
  35.     
  36. // Operations   
  37. public:   
  38.  // Positioning   
  39.  int  SetImageOffset( int nPixels );    
  40.  int  SetTextOffset( int nPixels );   
  41.  CPoint SetImagePos( CPoint p );   
  42.  CPoint SetTextPos( CPoint p );   
  43.   
  44.  // Image   
  45.  BOOL SetIcon( UINT nID, int nWidth, int nHeight );   
  46.  BOOL SetBitmap( UINT nID, int nWidth, int nHeight );   
  47.  BOOL SetMaskedBitmap( UINT nID, int nWidth, int nHeight, COLORREF crTransparentMask );   
  48.  BOOL HasImage() { return (BOOL)( m_hIcon != 0  | m_hBitmap != 0 ); }   
  49.   
  50.  // Color Tab   
  51.  void SetColorTab(COLORREF crTab);   
  52.   
  53.  // State   
  54.  BOOL SetDefaultButton( BOOL bState = TRUE );   
  55. private:   
  56.  BOOL SetBitmapCommon( UINT nID, int nWidth, int nHeight, COLORREF crTransparentMask, BOOL bUseMask );   
  57.  void CheckPointForCentering( CPoint &p, int nWidth, int nHeight );   
  58.  void Redraw();   
  59.   
  60. // Overrides   
  61.  // ClassWizard generated virtual function overrides   
  62.  //{{AFX_VIRTUAL(CSXButton)   
  63.  public:   
  64.  virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);   
  65.  //}}AFX_VIRTUAL   
  66.   
  67. // Implementation   
  68. public:   
  69.  virtual ~CSXButton();   
  70.   
  71.  // Generated message map functions   
  72. protected:   
  73.  //{{AFX_MSG(CSXButton)   
  74.  afx_msg LRESULT OnGetText(WPARAM wParam, LPARAM lParam);   
  75.  //}}AFX_MSG   
  76.   
  77.  DECLARE_MESSAGE_MAP()   
  78. };   
  79.   
  80. #endif  

       把SXBUTTON.CPP文件直接添加到工程,編譯工程,得到“mfcexpenddll.lib”和“mfcexpenddll.dll”兩個文件。我們用Visual Studio自帶的Depends工具可以查看這個.dll,發現其導出了衆多符號(見圖1)。

DLL導出類時導出的大量符號

圖1 導出類時導出的大量符號

  這些都是類的構造函數、析構函數及其它成員函數和變量經編譯器處理過的符號,我們直接用__declspec(dllexport)語句聲明類就導出了這些符號。

  如果我們想用.lib文件導出這些符號,是非常困難的,我們需要在工程中生成.map文件,查詢.map文件的符號,然後將其一一導出。如圖2,打開DLL工程的settings選項,再選擇Link,勾選其中的產生MAP文件(Generate mapfile)就可以產生.map文件了。

  打開mfcexpenddll工程生成的.map文件,我們發現其中包含了圖1中所示的符號(symbol)。

C++代碼
  1. 0001:00000380  ?HasImage@CSXButton@@QAEHXZ 10001380 f i SXBUTTON.OBJ   
  2. 0001:000003d0  ??0CSXButton@@QAE@XZ       100013d0 f   SXBUTTON.OBJ   
  3. 0001:00000500  ??_GCSXButton@@UAEPAXI@Z   10001500 f i SXBUTTON.OBJ   
  4. 0001:00000570  ??_ECSXButton@@UAEPAXI@Z   10001570 f i SXBUTTON.OBJ   
  5. 0001:00000630  ??1CSXButton@@UAE@XZ       10001630 f   SXBUTTON.OBJ   
  6. 0001:00000700 ?_GetBaseMessageMap@CSXButton@@KGPBUAFX_MSGMAP@@XZ 10001700 f   SXBUTTON.OBJ   
  7. 0001:00000730 ?GetMessageMap@CSXButton@@MBEPBUAFX_MSGMAP@@XZ 10001730 f   SXBUTTON.OBJ   
  8. 0001:00000770    ?Redraw@CSXButton@@AAEXXZ  10001770 f i SXBUTTON.OBJ   
  9. 0001:000007d0    ?SetIcon@CSXButton@@QAEHIHH@Z 100017d0 f   SXBUTTON.OBJ   
  10. ……………………………………………………………………..//省略  

產生.map文件

圖2 產生.map文件

  所以,對於MFC擴展DLL,我們不宜以.lib文件導出類。

       2、MFC擴展DLL的調用

  在DLL所在工作區新增一個dllcall工程,它是一個基於對話框的MFC EXE程序。在其中增加兩個按鈕SXBUTTON1、SXBUTTON2,並設置其屬性爲“Owner draw”,如圖3。

設置按鈕屬性爲“Owner draw”

圖3 設置按鈕屬性爲“Owner draw”

       在工程中添加兩個ICON資源:IDI_MSN_ICON(MSN的圖標)、IDI_REFBAR_ICON(Windows的系統圖標)。

  修改工程的“calldllDlg.h”頭文件爲:

C++代碼
  1. #include "..\..\mfcexpenddll\SXBUTTON.h"  //包含dll的導出類頭文件   
  2. #pragma comment(lib,"mfcexpenddll.lib")    //隱式鏈接dll   
  3. /////////////////////////////////////////////////////////////////////////////   
  4. // CCalldllDlg dialog   
  5.   
  6. class CCalldllDlg : public CDialog   
  7. {   
  8. // Construction   
  9. public:   
  10.  CCalldllDlg(CWnd* pParent = NULL); // standard constructor   
  11.   
  12. // Dialog Data   
  13.  //{{AFX_DATA(CCalldllDlg)   
  14.  enum { IDD = IDD_CALLDLL_DIALOG };   
  15. //增加與兩個按鈕對應的成員變量   
  16.  CSXButton m_button1;      
  17.  CSXButton m_button2;   
  18. …   
  19. }  

       同時,修改“calldllDlg.cpp”文件,使得m_button1、m_button2成員變量與對話框上的按鈕控件建立關聯:

C++代碼
  1. void CCalldllDlg::DoDataExchange(CDataExchange* pDX)   
  2. {   
  3.  CDialog::DoDataExchange(pDX);   
  4.  //{{AFX_DATA_MAP(CCalldllDlg)   
  5.  DDX_Control(pDX, IDC_BUTTON2, m_button2);   
  6.  DDX_Control(pDX, IDC_BUTTON1, m_button1);   
  7.  //}}AFX_DATA_MAP   
  8. }  

       修改BOOL CCalldllDlg::OnInitDialog()函數,在其中增加對兩個按鈕設置ICON的代碼:

C++代碼
  1. BOOL CCalldllDlg::OnInitDialog()   
  2. {   
  3.  CDialog::OnInitDialog();   
  4.   
  5.  // Add "About..." menu item to system menu.   
  6.   
  7.  // IDM_ABOUTBOX must be in the system command range.   
  8.  ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);   
  9.  ASSERT(IDM_ABOUTBOX < 0xF000);   
  10.   
  11.  CMenu* pSysMenu = GetSystemMenu(FALSE);   
  12.  if (pSysMenu != NULL)   
  13.  {   
  14.   CString strAboutMenu;   
  15.   strAboutMenu.LoadString(IDS_ABOUTBOX);   
  16.   if (!strAboutMenu.IsEmpty())   
  17.   {   
  18.    pSysMenu->AppendMenu(MF_SEPARATOR);   
  19.    pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);   
  20.   }   
  21.  }   
  22.   
  23.  // Set the icon for this dialog.  The framework does this automatically   
  24.  //  when the application's main window is not a dialog   
  25.  SetIcon(m_hIcon, TRUE);   // Set big icon   
  26.  SetIcon(m_hIcon, FALSE);  // Set small icon   
  27.   
  28.  // TODO: Add extra initialization here   
  29.  m_button1.SetIcon(IDI_MSN_ICON,16,16);   
  30.  m_button2.SetIcon(IDI_REFBAR_ICON,16,16);   
  31.   
  32.  return TRUE;  // return TRUE  unless you set the focus to a control   
  33. }  

       運行程序,將出現如圖4的對話框,圖形和文字同時出現在按鈕上,這說明我們正確地調用了MFC擴展DLL。

DLL擴展的按鈕被顯示

圖4 DLL擴展的按鈕被顯示

  如果我們不修改void CCalldllDlg::DoDataExchange(CDataExchange* pDX),即不增加下列代碼:

C++代碼
  1. DDX_Control(pDX, IDC_BUTTON2,  m_button2);   
  2. DDX_Control(pDX, IDC_BUTTON1,  m_button1);  

       我們也可以在BOOL CCalldllDlg::OnInitDialog()函數中添加如下代碼實現m_button1、m_button2與IDC_BUTTON1、IDC_BUTTON2的關聯:

C++代碼
  1. m_button1.SubclassDlgItem(IDC_BUTTON1, this);   
  2. m_button2.SubclassDlgItem(IDC_BUTTON2, this);  

  但是,DDX_Control與按鈕類的SubclassDlgItem成員函數不能同時存在,否則程序會出錯。

       3、總結

  由以上分析可知,MFC擴展DLL的導出與引用方式與前幾節所講述的方式沒有太大的差別,MFC擴展DLL主要強調對MFC進行功能擴展。因此,如果DLL的目標不是增強MFC的功能,其與應用程序的接口也不是MFC,請不要將DLL建立爲MFC擴展DLL。

鏈接地址:http://www.jizhuomi.com/software/301.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章