程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> VC++動態鏈接庫編程之MFC擴展 DLL

VC++動態鏈接庫編程之MFC擴展 DLL

編輯:C++入門知識
DLL類型入口函數 非 MFC DLL 編程者提供DllMain函數 MFC規則 DLL CWinApp對象的InitInstance 和 ExitInstance MFC擴展 DLL MFC DLL向導生成DllMain 函數
  對於MFC擴展DLL,系統會自動在工程中添加如下表所示的宏,這些宏為DLL和應用程序的編寫提供了方便。像AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA這樣的宏,在DLL和應用程序中將具有不同的定義,這取決於_AFXEXT宏是否被定義。這使得在DLL和應用程序中,使用統一的一個宏就可以表示出輸出和輸入的不同意思。在DLL中,表示輸出(因為_AFXEXT被定義,通常是在編譯器的標識參數中指定/D_AFXEXT);在應用程序中,則表示輸入(_AFXEXT沒有定義)。
  
  宏定義 AFX_CLASS_IMPORT __declspec(dlleXPort) AFX_API_IMPORT __declspec(dllexport) AFX_DATA_IMPORT __declspec(dllexport) AFX_CLASS_EXPORT __declspec(dllexport) AFX_API_EXPORT __declspec(dllexport) AFX_DATA_EXPORT __declspec(dllexport) AFX_EXT_CLASS #ifdef _AFXEXT
   AFX_CLASS_EXPORT
  #else
   AFX_CLASS_IMPORT AFX_EXT_API #ifdef _AFXEXT
   AFX_API_EXPORT
  #else
   AFX_API_IMPORT AFX_EXT_DATA #ifdef _AFXEXT
   AFX_DATA_EXPORT
  #else
   AFX_DATA_IMPORT
  6.2 MFC擴展DLL導出MFC派生類
  
  在這個例子中,我們將產生一個名為“ExtDll”的MFC擴展DLL工程,在這個DLL中導出一個對話框類,這個對話框類派生自MFC類CDialog。
  
  使用MFC向導生成MFC擴展DLL時,系統會自動添加如下代碼:
  
  static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };
  extern "C" int APIENTRY
  
  DllMain( HINSTANCE hInstance, DWord dwReason, LPVOID lpReserved )
  {
   // Remove this if you use lpReserved
  
   UNREFERENCED_PARAMETER( lpReserved );
  
   //說明:lpReserved是一個被系統所保留的參數,對於隱式鏈接是一個非零值,對於顯式鏈接值是零
  
   if (dwReason == DLL_PROCESS_ATTACH)
   {
  TRACE0( "EXTDLL.DLL Initializing! " );
  // Extension DLL one-time initialization
  if ( !AfxInitExtensionModule( ExtDllDLL, hInstance ))
   return 0;
   // Insert this DLL into the resource chain
  new CDynLinkLibrary( ExtDllDLL );
   }
   else if (dwReason == DLL_PROCESS_DETACH)
   {
  TRACE0( "EXTDLL.DLL Terminating! " );
  // Terminate the library before destrUCtors are called
  AfxTermExtensionModule( ExtDllDLL );
   }
   return 1; // ok
  }

  這一段代碼含義晦澀,我們需要對其進行解讀:
  
  (1)上述代碼完成MFC擴展DLL的初始化和終止處理;
  
  (2)初始化期間所創建的 CDynLinkLibrary 對象使MFC擴展 DLL 可以將 DLL中的CRuntimeClass 對象或資源導出到應用程序;
  
  (3)AfxInitExtensionModule函數捕捉模塊的CRuntimeClass 結構和在創建 CDynLinkLibrary 對象時使用的對象工廠(COleObjectFactory 對象);
  
  (4)AfxTermExtensionModule函數使 MFC 得以在每個進程與擴展 DLL 分離時(進程退出或使用AfxFreeLibrary卸載DLL時)清除擴展 DLL;
  
  (5)第一條語句static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };定義了一個AFX_EXTENSION_MODULE類的靜態全局對象,AFX_EXTENSION_MODULE的定義如下:
  
  struct AFX_EXTENSION_MODULE
  {
   BOOL bInitialized;
   HMODULE hModule;
   HMODULE hResource;
   CRuntimeClass* pFirstSharedClass;
   COleObjectFactory* pFirstSharedFactory;
  
   };

  由AFX_EXTENSION_MODULE的定義我們可以更好的理解(2)、(3)、(4)點。
  
  在資源編輯器中添加一個如圖15所示的對話框,並使用MFC類向導為其添加一個對應的類CExtDialog,系統自動添加了ExtDialog.h和ExtDialog.cpp兩個頭文件。
  
  
  圖15 MFC擴展DLL中的對話框
  修改ExtDialog.h中CExtDialog類的聲明為:
  
  class AFX_EXT_CLASS CExtDialog : public CDialog
  {
   public:
  CExtDialog( CWnd* pParent = NULL );
  enum { IDD = IDD_DLL_DIALOG };
   protected:
  virtual void DoDataExchange( CDataExchange* pDX );
  DECLARE_MESSAGE_MAP()
  };

  這其中最主要的改變是我們在class AFX_EXT_CLASS CExtDialog語句中添加了“AFX_EXT_CLASS”宏,則使得DLL中的CExtDialog類被導出。 6.3 MFC擴展DLL的加載
  
  6.3.1 隱式加載
  
  我們在6.2工程所在的工作區中添加一個LoadExtDllDlg工程,用於演示MFC擴展DLL的加載。在LoadExtDllDlg工程中添加一個如圖16所示的對話框,這個對話框上包括一個“調用DLL”按鈕。
  
  
  圖16 MFC擴展DLL調用工程中的對話框
  在與圖16對應對話框類實現文件的頭部添加:
  
  // LoadExtDllDlg.cpp : implementation file
  //
  
  #include "..ExtDialog.h"
  #pragma comment( lib, "ExtDll.lib" )
  
  而“調用DLL”按鈕的單擊事件的消息處理函數為:
  
  void CLoadExtDllDlg::OnDllcallButton()
  {
   CExtDialog extDialog;
   extDialog.DoModal();
  }

  當我們單擊“調用DLL”的時候,彈出了如圖15的對話框。
  
  為提供給用戶隱式加載(MFC擴展DLL一般使用隱式加載,具體原因見下節),MFC擴展DLL需要提供三個文件:
  
  (1)描述DLL中擴展類的頭文件;
  
  (2)與動態鏈接庫對應的.LIB文件;
  
  (3)動態鏈接庫.DLL文件本身。
  
  有了這三個文件,應用程序的開發者才可充分利用MFC擴展DLL。
  
  6.3.2 顯示加載
  
  顯示加載MFC擴展DLL應使用MFC全局函數AfxLoadLibrary而不是WIN32 API中的LoadLibrary。AfxLoadLibrary 最終也調用了 LoadLibrary這個API,但是在調用之前進行了線程同步的處理。
  
  AfxLoadLibrary 的函數原型與 LoadLibrary完全相同,為:
  
  HINSTANCE AFXAPI AfxLoadLibrary( LPCTSTR lpszModuleName );
  與之相對應的是,MFC 應用程序應使用AfxFreeLibrary 而非FreeLibrary 卸載MFC擴展DLL。AfxFreeLibrary的函數原型也與 FreeLibrary完全相同,為:
  
  BOOL AFXAPI AfxFreeLibrary( HINSTANCE hInstLib );
  假如我們把上例中的“調用DLL”按鈕單擊事件的消息處理函數改為:
  
  void CLoadExtDllDlg::OnDllcallButton()
  {
   HINSTANCE hDll = AfxLoadLibrary( "ExtDll.dll" );
   if(NULL == hDll)
   {
  AfxMessageBox( "MFC擴展DLL動態加載失敗" );
  return;
   }
  
   CExtDialog extDialog;
   extDialog.DoModal();
   AfxFreeLibrary(hDll);
  }

  則工程會出現link錯誤:
  
  LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall CExtDialog::~CExtDialog(void)" (__imp_??1CExtDialogUAE@XZ)
  
  LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall CExtDialog::CExtDialog(class CWnd *)" (__imp_??0CExtDialogQAE@PAVCWnd@Z)

  提示CExtDialog的構造函數和析構函數均無法找到!是的,對於派生MFC類的MFC擴展DLL,當我們要在應用程序中使用DLL中定義的派生類時,我們不宜使用動態加載DLL的方法。
  
  6.4 MFC擴展DLL加載MFC擴展DLL
  
  我們可以在MFC擴展DLL中再次使用MFC擴展DLL,但是,由於在兩個DLL中對於AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA宏的定義都是輸出,這會導致調用的時候出現問題。
  
  
  我們將會在調用MFC擴展DLL的DLL中看到link錯誤:
  
  error LNK2001: unresolved external symbol ….......
  因此,在調用MFC擴展DLL的MFC擴展DLL中,在包含被調用DLL的頭文件之前,需要臨時重新定義AFX_EXT_CLASS的值。下面的例子顯示了如何實現:
  
  //臨時改變宏的含義“輸出”為“輸入”
  
  #undef AFX_EXT_CLASS
  #undef AFX_EXT_API
  #undef AFX_EXT_DATA
  #define AFX_EXT_CLASS AFX_CLASS_IMPORT
  #define AFX_EXT_API AFX_API_IMPORT
  #define AFX_EXT_DATA AFX_DATA_IMPORT
  
  //包含被調用MFC擴展DLL的頭文件
  
  #include "CalledDLL.h"
  
  //恢復宏的含義為輸出
  
  #undef AFX_EXT_CLASS
  #undef AFX_EXT_API
  #undef AFX_EXT_DATA
  #define AFX_EXT_CLASS AFX_CLASS_EXPORT
  #define AFX_EXT_API AFX_API_EXPORT
  #define AFX_EXT_DATA AFX_DATA_EXPORT
  6.5 MFC擴展DLL導出函數和變量
  
  
  MFC擴展DLL導出函數和變量的方法也十分簡單,下面我們給出一個簡單的例子。
  
  我們在MFC向導生成的MFC擴展DLL工程中添加gobal.h和global.cpp兩個文件:
  
  //global.h:MFC擴展DLL導出變量和函數的聲明
  
  extern "C"
  {
   int AFX_EXT_DATA total; //導出變量
   int AFX_EXT_API add( int x, int y ); //導出函數
  }
  
  //global.cpp:MFC擴展DLL導出變量和函數定義
  
  #include "StdAfx.h"
  #include "global.h"
  
  extern "C" int total;
  int add(int x,int y)
  {
   total = x + y;
   return total;
  }

  編寫一個簡單的控制台程序來調用這個MFC擴展DLL:
  
  #include
  #include 單擊此處下載本工程)。
  
  我們知道static控件所對應的CStatic類不具備設置背景和文本顏色的接口,這使得我們不能在對話框或其它用戶界面上自由靈活地修改static控件的顏色風格,因此我們需要一個提供了SetBackColor和SetTextColor接口的CStatic派生類CMultiColorStatic。
   
  這個類的聲明如下:
  
  class AFX_EXT_CLASS CMultiColorStatic : public CStatic
  {
   // Construction
  
   public:
  CMultiColorStatic();
  virtual ~CMultiColorStatic();
  // Attributes
   protected:
  CString m_strCaption;
  COLORREF m_BackColor;
  COLORREF m_TextColor;
  // Operations
   public:
  void SetTextColor( COLORREF TextColor );
  void SetBackColor( COLORREF BackColor );
  void SetCaption( CString strCaption );
  
  // Generated message map functions
   protected:
  afx_msg void OnPaint();
  DECLARE_MESSAGE_MAP()
  };

  在這個類的實現文件中,我們需要為它提供WM_PAINT消息的處理函數(這是因為顏色的設置依靠於WM_PAINT消息):
  
  BEGIN_MESSAGE_MAP(CMultiColorStatic, CStatic)
  
  //{{AFX_MSG_MAP(CMultiColorStatic)
   ON_WM_PAINT() //為這個類定義WM_PAINT消息處理函數
  /
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved