• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            VC++動態(tài)鏈接庫編程之MFC擴展 DLL

            前文我們對非MFC DLL和MFC規(guī)則DLL進行了介紹,現(xiàn)在開始詳細分析DLL的最后一種類型――MFC擴展DLL。

              6.1概論

              MFC擴展DLL與MFC規(guī)則DLL的相同點在于在兩種DLL的內(nèi)部都可以使用MFC類庫,其不同點在于MFC擴展DLL與應用程序的接口可以是MFC的。MFC擴展DLL的含義在于它是MFC的擴展,其主要功能是實現(xiàn)從現(xiàn)有MFC庫類中派生出可重用的類。MFC擴展DLL使用MFC 動態(tài)鏈接庫版本,因此只有用共享MFC 版本生成的MFC 可執(zhí)行文件(應用程序或規(guī)則DLL)才能使用MFC擴展DLL。

              從前文可知,MFC規(guī)則DLL被MFC向?qū)ё詣犹砑恿艘粋€CWinApp的對象,而MFC擴展DLL則不包含該對象,它只是被自動添加了DllMain 函數(shù)。對于MFC擴展DLL,開發(fā)人員必須在DLL的DllMain函數(shù)中添加初始化和結(jié)束代碼。

              從下表我們可以看出三種DLL對DllMain入口函數(shù)的不同處理方式:

            DLL類型 入口函數(shù)
            非 MFC DLL 編程者提供DllMain函數(shù)
            MFC規(guī)則 DLL CWinApp對象的InitInstance 和 ExitInstance
            MFC擴展 DLL MFC DLL向?qū)蒁llMain 函數(shù)

              對于MFC擴展DLL,系統(tǒng)會自動在工程中添加如下表所示的宏,這些宏為DLL和應用程序的編寫提供了方便。像AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA這樣的宏,在DLL和應用程序中將具有不同的定義,這取決于_AFXEXT宏是否被定義。這使得在DLL和應用程序中,使用統(tǒng)一的一個宏就可以表示出輸出和輸入的不同意思。在DLL中,表示輸出(因為_AFXEXT被定義,通常是在編譯器的標識參數(shù)中指定/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派生類

              在這個例子中,我們將產(chǎn)生一個名為“ExtDll”的MFC擴展DLL工程,在這個DLL中導出一個對話框類,這個對話框類派生自MFC類CDialog。

              使用MFC向?qū)蒑FC擴展DLL時,系統(tǒng)會自動添加如下代碼:

            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是一個被系統(tǒng)所保留的參數(shù),對于隱式鏈接是一個非零值,對于顯式鏈接值是零

             if (dwReason == DLL_PROCESS_ATTACH)
             {
              TRACE0( "EXTDLL.DLL Initializing!\n" );
              // 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!\n" );
              // Terminate the library before destructors are called
              AfxTermExtensionModule( ExtDllDLL );
             }
             return 1; // ok
            }

              這一段代碼含義晦澀,我們需要對其進行解讀:

              (1)上述代碼完成MFC擴展DLL的初始化和終止處理;

              (2)初始化期間所創(chuàng)建的 CDynLinkLibrary 對象使MFC擴展 DLL 可以將 DLL中的CRuntimeClass 對象或資源導出到應用程序;

              (3)AfxInitExtensionModule函數(shù)捕獲模塊的CRuntimeClass 結(jié)構和在創(chuàng)建 CDynLinkLibrary 對象時使用的對象工廠(COleObjectFactory 對象);

              (4)AfxTermExtensionModule函數(shù)使 MFC 得以在每個進程與擴展 DLL 分離時(進程退出或使用AfxFreeLibrary卸載DLL時)清除擴展 DLL;

              (5)第一條語句static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };定義了一個AFX_EXTENSION_MODULE類的靜態(tài)全局對象,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類向?qū)槠涮砑右粋€對應的類CExtDialog,系統(tǒng)自動添加了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工程所在的工作區(qū)中添加一個LoadExtDllDlg工程,用于演示MFC擴展DLL的加載。在LoadExtDllDlg工程中添加一個如圖16所示的對話框,這個對話框上包括一個“調(diào)用DLL”按鈕。


            圖16 MFC擴展DLL調(diào)用工程中的對話框

              在與圖16對應對話框類實現(xiàn)文件的頭部添加:

            // LoadExtDllDlg.cpp : implementation file
            //

            #include "..\ExtDialog.h"
            #pragma comment( lib, "ExtDll.lib" )

            而“調(diào)用DLL”按鈕的單擊事件的消息處理函數(shù)為:

            void CLoadExtDllDlg::OnDllcallButton()
            {
             CExtDialog extDialog;
             extDialog.DoModal();
            }

              當我們單擊“調(diào)用DLL”的時候,彈出了如圖15的對話框。

              為提供給用戶隱式加載(MFC擴展DLL一般使用隱式加載,具體原因見下節(jié)),MFC擴展DLL需要提供三個文件:

              (1)描述DLL中擴展類的頭文件;

              (2)與動態(tài)鏈接庫對應的.LIB文件;

              (3)動態(tài)鏈接庫.DLL文件本身。

              有了這三個文件,應用程序的開發(fā)者才可充分利用MFC擴展DLL。

              6.3.2 顯示加載

              顯示加載MFC擴展DLL應使用MFC全局函數(shù)AfxLoadLibrary而不是WIN32 API中的LoadLibrary。AfxLoadLibrary 最終也調(diào)用了 LoadLibrary這個API,但是在調(diào)用之前進行了線程同步的處理。

              AfxLoadLibrary 的函數(shù)原型與 LoadLibrary完全相同,為:

            HINSTANCE AFXAPI AfxLoadLibrary( LPCTSTR lpszModuleName );

              與之相對應的是,MFC 應用程序應使用AfxFreeLibrary 而非FreeLibrary 卸載MFC擴展DLL。AfxFreeLibrary的函數(shù)原型也與 FreeLibrary完全相同,為:

            BOOL AFXAPI AfxFreeLibrary( HINSTANCE hInstLib );

              如果我們把上例中的“調(diào)用DLL”按鈕單擊事件的消息處理函數(shù)改為:

            void CLoadExtDllDlg::OnDllcallButton()
            {
             HINSTANCE hDll = AfxLoadLibrary( "ExtDll.dll" );
             if(NULL == hDll)
             {
              AfxMessageBox( "MFC擴展DLL動態(tài)加載失敗" );
              return;
             }

             CExtDialog extDialog;
             extDialog.DoModal();
             AfxFreeLibrary(hDll);
            }

              則工程會出現(xiàn)link錯誤:

            LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall CExtDialog::~CExtDialog(void)" (__imp_??1CExtDialog@@UAE@XZ)

            LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall CExtDialog::CExtDialog(class CWnd *)" (__imp_??0CExtDialog@@QAE@PAVCWnd@@@Z)

              提示CExtDialog的構造函數(shù)和析構函數(shù)均無法找到!是的,對于派生MFC類的MFC擴展DLL,當我們要在應用程序中使用DLL中定義的派生類時,我們不宜使用動態(tài)加載DLL的方法。

              6.4 MFC擴展DLL加載MFC擴展DLL

              我們可以在MFC擴展DLL中再次使用MFC擴展DLL,但是,由于在兩個DLL中對于AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA宏的定義都是輸出,這會導致調(diào)用的時候出現(xiàn)問題。

              我們將會在調(diào)用MFC擴展DLL的DLL中看到link錯誤:

            error LNK2001: unresolved external symbol ….......

              因此,在調(diào)用MFC擴展DLL的MFC擴展DLL中,在包含被調(diào)用DLL的頭文件之前,需要臨時重新定義AFX_EXT_CLASS的值。下面的例子顯示了如何實現(xiàn):

            //臨時改變宏的含義“輸出”為“輸入”

            #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

            //包含被調(diào)用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導出函數(shù)和變量

              MFC擴展DLL導出函數(shù)和變量的方法也十分簡單,下面我們給出一個簡單的例子。

              我們在MFC向?qū)傻腗FC擴展DLL工程中添加gobal.h和global.cpp兩個文件:

            //global.h:MFC擴展DLL導出變量和函數(shù)的聲明

            extern "C"
            {
             int AFX_EXT_DATA total; //導出變量
             int AFX_EXT_API add( int x, int y ); //導出函數(shù)
            }

            //global.cpp:MFC擴展DLL導出變量和函數(shù)定義

            #include "StdAfx.h"
            #include "global.h"

            extern "C" int total;
            int add(int x,int y)
            {
             total = x + y;
             return total;
            }

              編寫一個簡單的控制臺程序來調(diào)用這個MFC擴展DLL:

            #include <iostream.h>
            #include <afxver_.h>

            //AFX_EXT_DATA、AFX_EXT_API宏的定義在afxver_.h頭文件中

            #pragma comment ( lib, "ExtDll.lib" )
            #include "..\global.h"

            int main(int argc, char* argv[])
            {
             cout << add(2,3) << endl;
             cout << total;
             return 0;
            }

              運行程序,在控制臺上看到:

              5

              5

              另外,在Visual C++下建立MFC擴展DLL時,MFC DLL向?qū)詣由?def文件。因此,對于函數(shù)和變量,我們除了可以利用AFX_EXT_DATA、AFX_EXT_API宏導出以外,在.def文件中定義導出也是一個很好的辦法。與之相比,在.def文件中導出類卻較麻煩。通常需要從工程生成的.map文件中獲得類的所有成員函數(shù)被C++編譯器更改過的標識符,并且在.def文件中導出這些“奇怪”的標識符。因此,MFC擴展DLL通常以AFX_EXT_CLASS宏直接聲明導出類。

              6.6 MFC擴展DLL的應用

              上述各小節(jié)所舉MFC擴展DLL的例子均只是為了說明某方面的問題,沒有真實地體現(xiàn)“MFC擴展” 的內(nèi)涵,譬如6.2派生自CDialog的類也不具備比CDialog更強的功能。MFC擴展DLL的真實內(nèi)涵體現(xiàn)在它提供的類雖然派生自MFC類,但是提供了比MFC類更強大的功能、更豐富的接口。下面我們來看一個具體的例子(單擊此處下載本工程)。

              我們知道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()
            };

              在這個類的實現(xiàn)文件中,我們需要為它提供WM_PAINT消息的處理函數(shù)(這是因為顏色的設置依賴于WM_PAINT消息):

            BEGIN_MESSAGE_MAP(CMultiColorStatic, CStatic)

            //{{AFX_MSG_MAP(CMultiColorStatic)
             ON_WM_PAINT() //為這個類定義WM_PAINT消息處理函數(shù)
            //}}AFX_MSG_MAP
            END_MESSAGE_MAP()

              下面是這個類中的重要成員函數(shù):

            //為CMultiColorStatic類添加“設置文本顏色”接口

            void CMultiColorStatic::SetTextColor( COLORREF TextColor )
            {
             m_TextColor = TextColor; //設置文字顏色
            }

            //為CMultiColorStatic類添加“設置背景顏色”接口

            void CMultiColorStatic::SetBackColor( COLORREF BackColor )
            {
             m_BackColor = BackColor; //設置背景顏色
            }

            //為CMultiColorStatic類添加“設置標題”接口

            void CMultiColorStatic::SetCaption( CString strCaption )
            {
             m_strCaption = strCaption;
            }

            //重畫Static,顏色和標題的設置都依賴于這個函數(shù)

            void CMultiColorStatic::OnPaint()
            {
             CPaintDC dc(this); // device context for painting
             CRect rect;
             GetClientRect( &rect );
             dc.SetBkColor( m_BackColor );
             dc.SetBkMode( TRANSPARENT );
             CFont *pFont = GetParent()->GetFont();//得到父窗體的字體
             CFont *pOldFont;
             pOldFont = dc.SelectObject( pFont );//選用父窗體的字體
             dc.SetTextColor( m_TextColor );//設置文本顏色
             dc.DrawText( m_strCaption, &rect, DT_CENTER );//文本在Static中央
             dc.SelectObject( pOldFont );
            }

              為了驗證CMultiColorStatic類,我們制作一個基于對話框的應用程序,它包含一個如圖17所示的對話框。該對話框上包括一個static控件和三個按鈕,這三個按鈕可分別把static控件設置為“紅色”、“藍色”和“綠色”。


            圖17 擴展的CStatic類調(diào)用演示

              下面看看應如何編寫與這個對話框?qū)念悺?br>
              包含這種Static的對話框類的聲明如下:

            #include "..\MultiColorStatic.h"
            #pragma comment ( lib, "ColorStatic.lib" )

            // CCallDllDlg dialog

            class CCallDllDlg : public CDialog
            {
             public:
              CCallDllDlg(CWnd* pParent = NULL); // standard constructor
              enum { IDD = IDD_CALLDLL_DIALOG };
              CMultiColorStatic m_colorstatic; //包含一個CMultiColorStatic的實例
             protected:
              virtual void DoDataExchange(CDataExchange* pDX);//DDX/DDV support
              HICON m_hIcon;

             // Generated message map functions
             //{{AFX_MSG(CCallDllDlg)

             virtual BOOL OnInitDialog();
             afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
             afx_msg void OnPaint();
             afx_msg HCURSOR OnQueryDragIcon();
             afx_msg void OnRedButton();
             afx_msg void OnBlueButton();
             afx_msg void OnGreenButton();
            //}}AFX_MSG
            DECLARE_MESSAGE_MAP()
            };

              下面是這個類中與使用CMultiColorStatic相關的主要成員函數(shù):

            void CCallDllDlg::DoDataExchange(CDataExchange* pDX)
            {
             CDialog::DoDataExchange(pDX);
             //{{AFX_DATA_MAP(CCallDllDlg)
              DDX_Control(pDX, IDC_COLOR_STATIC, m_colorstatic);
             //使m_colorstatic與IDC_COLOR_STATIC控件關聯(lián)
             //}}AFX_DATA_MAP
            }

            BOOL CCallDllDlg::OnInitDialog()
            {
             …
             // TODO: Add extra initialization here

             // 初始static控件的顯示

             m_colorstatic.SetCaption("最開始為黑色");
             m_colorstatic.SetTextColor(RGB(0,0,0));
             return TRUE; // return TRUE unless you set the focus to a control
            }

            //設置static控件文本顏色為紅色

            void CCallDllDlg::OnRedButton()
            {
             m_colorstatic.SetCaption( "改變?yōu)榧t色" );
             m_colorstatic.SetTextColor( RGB( 255, 0, 0 ) );
             Invalidate( TRUE ); //導致發(fā)出WM_PAINT消息
            }

            //設置static控件文本顏色為藍色

            void CCallDllDlg::OnBlueButton()
            {
             m_colorstatic.SetCaption( "改變?yōu)樗{色" );
             m_colorstatic.SetTextColor( RGB( 0, 0, 255 ) );
             Invalidate( TRUE ); //導致發(fā)出WM_PAINT消息
            }

            //設置static控件文本顏色為綠色

            void CCallDllDlg::OnGreenButton()
            {
             m_colorstatic.SetCaption( "改變?yōu)榫G色" );
             m_colorstatic.SetTextColor( RGB(0,255,0) );
             Invalidate( TRUE ); //導致發(fā)出WM_PAINT消息
            }

              至此,我們已經(jīng)講解完成了所有類型的動態(tài)鏈接庫,即非MFC DLL、MFC規(guī)則DLL和MFC擴展DLL。下一節(jié)將給出DLL的三個工程實例,與讀者朋友們共同體會DLL的應用范圍和使用方法。

            posted on 2009-06-09 21:17 wrh 閱讀(753) 評論(0)  編輯 收藏 引用

            導航

            <2009年10月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計

            常用鏈接

            留言簿(19)

            隨筆檔案

            文章檔案

            收藏夾

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            国产精品一区二区久久精品涩爱| 亚洲欧美一级久久精品| 九九精品99久久久香蕉| 久久亚洲国产精品一区二区| 免费观看久久精彩视频| 久久精品二区| 波多野结衣中文字幕久久| 99久久夜色精品国产网站| 亚洲日韩欧美一区久久久久我| 少妇人妻88久久中文字幕| 99久久亚洲综合精品成人| 亚洲精品无码成人片久久| 久久国产成人午夜aⅴ影院| 精品久久久久久中文字幕大豆网| 久久线看观看精品香蕉国产| 久久久亚洲裙底偷窥综合| 国产高潮国产高潮久久久91 | 久久精品国产亚洲网站| 久久99热这里只频精品6| 久久国产亚洲精品麻豆| 久久久无码精品亚洲日韩京东传媒 | 久久久精品人妻无码专区不卡| 国内精品久久久久影院薰衣草| 久久精品国产亚洲AV不卡| 国产韩国精品一区二区三区久久| 久久99久久99精品免视看动漫 | 久久久久亚洲精品天堂| 久久久久久久波多野结衣高潮| 久久久久一本毛久久久| 99久久免费只有精品国产| 久久精品亚洲日本波多野结衣| 精品国产乱码久久久久久呢| 久久露脸国产精品| 久久这里有精品视频| 久久精品国产精品亜洲毛片| 久久男人中文字幕资源站| 日韩十八禁一区二区久久| 亚洲精品乱码久久久久久不卡| 亚洲精品午夜国产va久久| 性高湖久久久久久久久AAAAA | 精品无码久久久久久午夜|