• <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規(guī)則DLL

            第4節(jié)我們對非MFC DLL進行了介紹,這一節(jié)將詳細地講述MFC規(guī)則DLL的創(chuàng)建與使用技巧。

              另外,自從本文開始連載后,收到了一些讀者的e-mail。有的讀者提出了一些問題,筆者將在本文的最后一次連載中選取其中的典型問題進行解答。由于時間的關系,對于讀者朋友的來信,筆者暫時不能一一回復,還望海涵!由于筆者的水平有限,文中難免有錯誤和紕漏,也熱誠歡迎讀者朋友不吝指正!

              5. MFC規(guī)則DLL

              5.1 概述

              MFC規(guī)則DLL的概念體現在兩方面:

             ?。?) 它是MFC的
              
              “是MFC的”意味著可以在這種DLL的內部使用MFC;

             ?。?) 它是規(guī)則的

              “是規(guī)則的”意味著它不同于MFC擴展DLL,在MFC規(guī)則DLL的內部雖然可以使用MFC,但是其與應用程序的接口不能是MFC。而MFC擴展DLL與應用程序的接口可以是MFC,可以從MFC擴展DLL中導出一個MFC類的派生類。

              Regular DLL能夠被所有支持DLL技術的語言所編寫的應用程序調用,當然也包括使用MFC的應用程序。在這種動態(tài)連接庫中,包含一個從CWinApp繼承下來的類,DllMain函數則由MFC自動提供。

              Regular DLL分為兩類:

             ?。?)靜態(tài)鏈接到MFC 的規(guī)則DLL

              靜態(tài)鏈接到MFC的規(guī)則DLL與MFC庫(包括MFC擴展 DLL)靜態(tài)鏈接,將MFC庫的代碼直接生成在.dll文件中。在調用這種DLL的接口時,MFC使用DLL的資源。因此,在靜態(tài)鏈接到MFC 的規(guī)則DLL中不需要進行模塊狀態(tài)的切換。

              使用這種方法生成的規(guī)則DLL其程序較大,也可能包含重復的代碼。

             ?。?)動態(tài)鏈接到MFC 的規(guī)則DLL

              動態(tài)鏈接到MFC 的規(guī)則DLL 可以和使用它的可執(zhí)行文件同時動態(tài)鏈接到 MFC DLL 和任何MFC擴展 DLL。在使用了MFC共享庫的時候,默認情況下,MFC使用主應用程序的資源句柄來加載資源模板。這樣,當DLL和應用程序中存在相同ID的資源時(即所謂的資源重復問題),系統(tǒng)可能不能獲得正確的資源。因此,對于共享MFC DLL的規(guī)則DLL,我們必須進行模塊切換以使得MFC能夠找到正確的資源模板。

              我們可以在Visual C++中設置MFC規(guī)則DLL是靜態(tài)鏈接到MFC DLL還是動態(tài)鏈接到MFC DLL。如圖8,依次選擇Visual C++的project -> Settings -> General菜單或選項,在Microsoft Foundation Classes中進行設置。


            圖8 設置動態(tài)/靜態(tài)鏈接MFC DLL

              5.2 MFC規(guī)則DLL的創(chuàng)建

              我們來一步步講述使用MFC向導創(chuàng)建MFC規(guī)則DLL的過程,首先新建一個project,如圖9,選擇project的類型為MFC AppWizard(dll)。點擊OK進入如圖10所示的對話框。


            圖9 MFC DLL工程的創(chuàng)建


            圖10所示對話框中的1區(qū)選擇MFC DLL的類別。

              2區(qū)選擇是否支持automation(自動化)技術, automation 允許用戶在一個應用程序中操縱另外一個應用程序或組件。例如,我們可以在應用程序中利用 Microsoft Word 或Microsoft Excel的工具,而這種使用對用戶而言是透明的。自動化技術可以大大簡化和加快應用程序的開發(fā)。

              3區(qū)選擇是否支持Windows Sockets,當選擇此項目時,應用程序能在 TCP/IP 網絡上進行通信。 CWinApp派生類的InitInstance成員函數會初始化通訊端的支持,同時工程中的StdAfx.h文件會自動include <AfxSock.h>頭文件。

              添加socket通訊支持后的InitInstance成員函數如下:

            BOOL CRegularDllSocketApp::InitInstance()
            {
             if (!AfxSocketInit())
             {
              AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
              return FALSE;
             }
             return TRUE;
            }

              4區(qū)選擇是否由MFC向導自動在源代碼中添加注釋,一般我們選擇“Yes,please”。


            圖10 MFC DLL的創(chuàng)建選項

            5.3 一個簡單的MFC規(guī)則DLL

              這個DLL的例子(屬于靜態(tài)鏈接到MFC 的規(guī)則DLL)中提供了一個如圖11所示的對話框。


            圖11 MFC規(guī)則DLL例子

              在DLL中添加對話框的方式與在MFC應用程序中是一樣的。 在圖11所示DLL中的對話框的Hello按鈕上點擊時將MessageBox一個“Hello,pconline的網友”對話框,下面是相關的文件及源代碼,其中刪除了MFC向導自動生成的絕大多數注釋(下載本工程):

              第一組文件:CWinApp繼承類的聲明與實現

            // RegularDll.h : main header file for the REGULARDLL DLL

            #if !defined(AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_)
            #define AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_

            #if _MSC_VER > 1000
            #pragma once
            #endif // _MSC_VER > 1000

            #ifndef __AFXWIN_H__
            #error include ’stdafx.h’ before including this file for PCH
            #endif
            #include "resource.h" // main symbols

            class CRegularDllApp : public CWinApp
            {
             public:
              CRegularDllApp();
              DECLARE_MESSAGE_MAP()
            };

            #endif

            // RegularDll.cpp : Defines the initialization routines for the DLL.

            #include "stdafx.h"
            #include "RegularDll.h"

            #ifdef _DEBUG
            #define new DEBUG_NEW
            #undef THIS_FILE
            static char THIS_FILE[] = __FILE__;
            #endif

            BEGIN_MESSAGE_MAP(CRegularDllApp, CWinApp)
            END_MESSAGE_MAP()

            /////////////////////////////////////////////////////////////////////////////

            // CRegularDllApp construction

            CRegularDllApp::CRegularDllApp()
            {
            }

            /////////////////////////////////////////////////////////////////////////////
            // The one and only CRegularDllApp object

            CRegularDllApp theApp;

              分析:

              在這一組文件中定義了一個繼承自CWinApp的類CRegularDllApp,并同時定義了其的一個實例theApp。乍一看,您會以為它是一個MFC應用程序,因為MFC應用程序也包含這樣的在工程名后添加“App”組成類名的類(并繼承自CWinApp類),也定義了這個類的一個全局實例theApp。

              我們知道,在MFC應用程序中CWinApp取代了SDK程序中WinMain的地位,SDK程序WinMain所完成的工作由CWinApp的三個函數完成:

            virtual BOOL InitApplication( );

            virtual BOOL InitInstance( );

            virtual BOOL Run( ); //傳說中MFC程序的“活水源頭”

              但是MFC規(guī)則DLL并不是MFC應用程序,它所繼承自CWinApp的類不包含消息循環(huán)。這是因為,MFC規(guī)則DLL不包含CWinApp::Run機制,主消息泵仍然由應用程序擁有。如果DLL 生成無模式對話框或有自己的主框架窗口,則應用程序的主消息泵必須調用從DLL 導出的函數來調用PreTranslateMessage成員函數。

              另外,MFC規(guī)則DLL與MFC 應用程序中一樣,需要將所有 DLL中元素的初始化放到InitInstance 成員函數中。

              第二組文件 自定義對話框類聲明及實現

            #if !defined(AFX_DLLDIALOG_H__CEA4C6AF_245D_48A6_B11A_A5521EAD7C4E__INCLUDED_)
            #define AFX_DLLDIALOG_H__CEA4C6AF_245D_48A6_B11A_A5521EAD7C4E__INCLUDED_

            #if _MSC_VER > 1000
            #pragma once
            #endif // _MSC_VER > 1000
            // DllDialog.h : header file
            /////////////////////////////////////////////////////////////////////////////
            // CDllDialog dialog

            class CDllDialog : public CDialog
            {
             // Construction
             public:
              CDllDialog(CWnd* pParent = NULL); // standard constructor
              enum { IDD = IDD_DLL_DIALOG };
             protected:
              virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
              // Implementation
             protected:
              afx_msg void OnHelloButton();
              DECLARE_MESSAGE_MAP()
            };
            #endif

            // DllDialog.cpp : implementation file

            #include "stdafx.h"
            #include "RegularDll.h"
            #include "DllDialog.h"
            #ifdef _DEBUG
            #define new DEBUG_NEW
            #undef THIS_FILE
            static char THIS_FILE[] = __FILE__;
            #endif

            /////////////////////////////////////////////////////////////////////////////
            // CDllDialog dialog

            CDllDialog::CDllDialog(CWnd* pParent /*=NULL*/)
            : CDialog(CDllDialog::IDD, pParent)
            {}

            void CDllDialog::DoDataExchange(CDataExchange* pDX)
            {
             CDialog::DoDataExchange(pDX);
            }

            BEGIN_MESSAGE_MAP(CDllDialog, CDialog)
             ON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)
            END_MESSAGE_MAP()

            /////////////////////////////////////////////////////////////////////////////
            // CDllDialog message handlers

            void CDllDialog::OnHelloButton()
            {
             MessageBox("Hello,pconline的網友","pconline");
            }

              分析:

              這一部分的編程與一般的應用程序根本沒有什么不同,我們照樣可以利用MFC類向導來自動為對話框上的控件添加事件。MFC類向導照樣會生成類似ON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)的消息映射宏。

              第三組文件 DLL中的資源文件

            //{{NO_DEPENDENCIES}}

            // Microsoft Developer Studio generated include file.

            // Used by RegularDll.rc

            //

            #define IDD_DLL_DIALOG 1000

            #define IDC_HELLO_BUTTON 1000

              分析:

              在MFC規(guī)則DLL中使用資源也與在MFC應用程序中使用資源沒有什么不同,我們照樣可以用Visual C++的資源編輯工具進行資源的添加、刪除和屬性的更改。

              第四組文件 MFC規(guī)則DLL接口函數

            #include "StdAfx.h"
            #include "DllDialog.h"

            extern "C" __declspec(dllexport) void ShowDlg(void)
            {
             CDllDialog dllDialog;
             dllDialog.DoModal();
            }

              分析:

              這個接口并不使用MFC,但是在其中卻可以調用MFC擴展類CdllDialog的函數,這體現了“規(guī)則”的概類。

              與非MFC DLL完全相同,我們可以使用__declspec(dllexport)聲明或在.def中引出的方式導出MFC規(guī)則DLL中的接口。
            5.4 MFC規(guī)則DLL的調用

              筆者編寫了如圖12的對話框MFC程序(下載本工程)來調用5.3節(jié)的MFC規(guī)則DLL,在這個程序的對話框上點擊“調用DLL”按鈕時彈出5.3節(jié)MFC規(guī)則DLL中的對話框。


            圖12 MFC規(guī)則DLL的調用例子

              下面是“調用DLL”按鈕單擊事件的消息處理函數:

            void CRegularDllCallDlg::OnCalldllButton()
            {
             typedef void (*lpFun)(void);
             HINSTANCE hDll; //DLL句柄
             hDll = LoadLibrary("RegularDll.dll");
             if (NULL==hDll)
             {
              MessageBox("DLL加載失敗");
             }

             lpFun addFun; //函數指針
             lpFun pShowDlg = (lpFun)GetProcAddress(hDll,"ShowDlg");
             if (NULL==pShowDlg)
             {
              MessageBox("DLL中函數尋找失敗");
             }
             pShowDlg();
            }

              上述例子中給出的是顯示調用的方式,可以看出,其調用方式與第4節(jié)中非MFC DLL的調用方式沒有什么不同。

              我們照樣可以在EXE程序中隱式調用MFC規(guī)則DLL,只需要將DLL工程生成的.lib文件和.dll文件拷入當前工程所在的目錄,并在RegularDllCallDlg.cpp文件(圖12所示對話框類的實現文件)的頂部添加:

            #pragma comment(lib,"RegularDll.lib")
            void ShowDlg(void);

              并將void CRegularDllCallDlg::OnCalldllButton() 改為:

            void CRegularDllCallDlg::OnCalldllButton()
            {
             ShowDlg();
            }

              5.5 共享MFC DLL的規(guī)則DLL的模塊切換

              應用程序進程本身及其調用的每個DLL模塊都具有一個全局唯一的HINSTANCE句柄,它們代表了DLL或EXE模塊在進程虛擬空間中的起始地址。進程本身的模塊句柄一般為0x400000,而DLL模塊的缺省句柄為0x10000000。如果程序同時加載了多個DLL,則每個DLL模塊都會有不同的HINSTANCE。應用程序在加載DLL時對其進行了重定位。

              共享MFC DLL(或MFC擴展DLL)的規(guī)則DLL涉及到HINSTANCE句柄問題,HINSTANCE句柄對于加載資源特別重要。EXE和DLL都有其自己的資源,而且這些資源的ID可能重復,應用程序需要通過資源模塊的切換來找到正確的資源。如果應用程序需要來自于DLL的資源,就應將資源模塊句柄指定為DLL的模塊句柄;如果需要EXE文件中包含的資源,就應將資源模塊句柄指定為EXE的模塊句柄。

              這次我們創(chuàng)建一個動態(tài)鏈接到MFC DLL的規(guī)則DLL(下載本工程),在其中包含如圖13的對話框。


            圖13 DLL中的對話框

              另外,在與這個DLL相同的工作區(qū)中生成一個基于對話框的MFC程序,其對話框與圖12完全一樣。但是在此工程中我們另外添加了一個如圖14的對話框。


            圖14 EXE中的對話框

              圖13和圖14中的對話框除了caption不同(以示區(qū)別)以外,其它的都相同。

              尤其值得特別注意,在DLL和EXE中我們對圖13和圖14的對話框使用了相同的資源ID=2000,在DLL和EXE工程的resource.h中分別有如下的宏:

            //DLL中對話框的ID

            #define IDD_DLL_DIALOG 2000

            //EXE中對話框的ID

            #define IDD_EXE_DIALOG 2000

              與5.3節(jié)靜態(tài)鏈接MFC DLL的規(guī)則DLL相同,我們還是在規(guī)則DLL中定義接口函數ShowDlg,原型如下:

            #include "StdAfx.h"
            #include "SharedDll.h"

            void ShowDlg(void)
            {
             CDialog dlg(IDD_DLL_DIALOG); //打開ID為2000的對話框
             dlg.DoModal();
            }

              而為應用工程主對話框的“調用DLL”的單擊事件添加如下消息處理函數:

            void CSharedDllCallDlg::OnCalldllButton()
            {
             ShowDlg();
            }

              我們以為單擊“調用DLL”會彈出如圖13所示DLL中的對話框,可是可怕的事情發(fā)生了,我們看到是圖14所示EXE中的對話框!
            驚訝?

              產生這個問題的根源在于應用程序與MFC規(guī)則DLL共享MFC DLL(或MFC擴展DLL)的程序總是默認使用EXE的資源,我們必須進行資源模塊句柄的切換,其實現方法有三:

              方法一 在DLL接口函數中使用:

            AFX_MANAGE_STATE(AfxGetStaticModuleState());

              我們將DLL中的接口函數ShowDlg改為:

            void ShowDlg(void)
            {
             //方法1:在函數開始處變更,在函數結束時恢復
             //將AFX_MANAGE_STATE(AfxGetStaticModuleState());作為接口函數的第一//條語句進行模塊狀態(tài)切換

             AFX_MANAGE_STATE(AfxGetStaticModuleState());
             CDialog dlg(IDD_DLL_DIALOG);//打開ID為2000的對話框
             dlg.DoModal();
            }

              這次我們再點擊EXE程序中的“調用DLL”按鈕,彈出的是DLL中的如圖13的對話框!嘿嘿,彈出了正確的對話框資源。

              AfxGetStaticModuleState是一個函數,其原型為:

            AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );

              該函數的功能是在棧上(這意味著其作用域是局部的)創(chuàng)建一個AFX_MODULE_STATE類(模塊全局數據也就是模塊狀態(tài))的實例,對其進行設置,并將其指針pModuleState返回。

              AFX_MODULE_STATE類的原型如下:

            // AFX_MODULE_STATE (global data for a module)

            class AFX_MODULE_STATE : public CNoTrackObject
            {
             public:
              #ifdef _AFXDLL
               AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion);
               AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion,BOOL bSystem);
              #else
               AFX_MODULE_STATE(BOOL bDLL);
              #endif
              ~AFX_MODULE_STATE();

              CWinApp* m_pCurrentWinApp;
              HINSTANCE m_hCurrentInstanceHandle;
              HINSTANCE m_hCurrentResourceHandle;
              LPCTSTR m_lpszCurrentAppName;

              … //省略后面的部分
            }

              AFX_MODULE_STATE類利用其構造函數和析構函數進行存儲模塊狀態(tài)現場及恢復現場的工作,類似匯編中call指令對pc指針和sp寄存器的保存與恢復、中斷服務程序的中斷現場壓棧與恢復以及操作系統(tǒng)線程調度的任務控制塊保存與恢復。

              許多看似不著邊際的知識點居然有驚人的相似!

              AFX_MANAGE_STATE是一個宏,其原型為:

            AFX_MANAGE_STATE( AFX_MODULE_STATE* pModuleState )

              該宏用于將pModuleState設置為當前的有效模塊狀態(tài)。當離開該宏的作用域時(也就離開了pModuleState所指向棧上對象的作用域),先前的模塊狀態(tài)將由AFX_MODULE_STATE的析構函數恢復。

              方法二 在DLL接口函數中使用:

            AfxGetResourceHandle();

            AfxSetResourceHandle(HINSTANCE xxx);

              AfxGetResourceHandle用于獲取當前資源模塊句柄,而AfxSetResourceHandle則用于設置程序目前要使用的資源模塊句柄。

              我們將DLL中的接口函數ShowDlg改為:

            void ShowDlg(void)
            {
             //方法2的狀態(tài)變更
             HINSTANCE save_hInstance = AfxGetResourceHandle();
             AfxSetResourceHandle(theApp.m_hInstance);
             CDialog dlg(IDD_DLL_DIALOG);//打開ID為2000的對話框
             dlg.DoModal();

             //方法2的狀態(tài)還原
             AfxSetResourceHandle(save_hInstance);
            }

              通過AfxGetResourceHandle和AfxSetResourceHandle的合理變更,我們能夠靈活地設置程序的資源模塊句柄,而方法一則只能在DLL接口函數退出的時候才會恢復模塊句柄。方法二則不同,如果將ShowDlg改為:

            extern CSharedDllApp theApp; //需要聲明theApp外部全局變量

            void ShowDlg(void)
            {
             //方法2的狀態(tài)變更
             HINSTANCE save_hInstance = AfxGetResourceHandle();
             AfxSetResourceHandle(theApp.m_hInstance);

             CDialog dlg(IDD_DLL_DIALOG);//打開ID為2000的對話框
             dlg.DoModal();

             //方法2的狀態(tài)還原

             AfxSetResourceHandle(save_hInstance);

             //使用方法2后在此處再進行操作針對的將是應用程序的資源

             CDialog dlg1(IDD_DLL_DIALOG); //打開ID為2000的對話框
             dlg1.DoModal();
            }

              在應用程序主對話框的“調用DLL”按鈕上點擊,將看到兩個對話框,相繼為DLL中的對話框(圖13)和EXE中的對話框(圖14)。

              方法三 由應用程序自身切換

              資源模塊的切換除了可以由DLL接口函數完成以外,由應用程序自身也能完成(下載本工程)。

              現在我們把DLL中的接口函數改為最簡單的:

            void ShowDlg(void)
            {
             CDialog dlg(IDD_DLL_DIALOG); //打開ID為2000的對話框
             dlg.DoModal();
            }

              而將應用程序的OnCalldllButton函數改為:

            void CSharedDllCallDlg::OnCalldllButton()
            {
             //方法3:由應用程序本身進行狀態(tài)切換
             //獲取EXE模塊句柄

             HINSTANCE exe_hInstance = GetModuleHandle(NULL);

             //或者HINSTANCE exe_hInstance = AfxGetResourceHandle();
             //獲取DLL模塊句柄

             HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll");
             AfxSetResourceHandle(dll_hInstance); //切換狀態(tài)
             ShowDlg(); //此時顯示的是DLL的對話框
             AfxSetResourceHandle(exe_hInstance); //恢復狀態(tài)

             //資源模塊恢復后再調用ShowDlg
             ShowDlg(); //此時顯示的是EXE的對話框
            }

              方法三中的Win32函數GetModuleHandle可以根據DLL的文件名獲取DLL的模塊句柄。如果需要得到EXE模塊的句柄,則應調用帶有Null參數的GetModuleHandle。

              方法三與方法二的不同在于方法三是在應用程序中利用AfxGetResourceHandle和AfxSetResourceHandle進行資源模塊句柄切換的。同樣地,在應用程序主對話框的“調用DLL”按鈕上點擊,也將看到兩個對話框,相繼為DLL中的對話框(圖13)和EXE中的對話框(圖14)。

              在下一節(jié)我們將對MFC擴展DLL進行詳細分析和實例講解,歡迎您繼續(xù)關注本系列連載。

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

            導航

            <2009年6月>
            31123456
            78910111213
            14151617181920
            21222324252627
            2829301234
            567891011

            統(tǒng)計

            常用鏈接

            留言簿(19)

            隨筆檔案

            文章檔案

            收藏夾

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            狠狠精品久久久无码中文字幕| 伊人 久久 精品| 欧美亚洲另类久久综合| 色综合色天天久久婷婷基地| 亚洲伊人久久成综合人影院 | 久久青青草原亚洲av无码app | 亚洲中文久久精品无码ww16| 精品少妇人妻av无码久久| 久久人人爽人人爽AV片| 色婷婷综合久久久久中文| 99久久www免费人成精品| 久久国产AVJUST麻豆| 777米奇久久最新地址| 一级做a爰片久久毛片免费陪| 99久久中文字幕| 亚洲午夜久久久久久久久电影网| 久久免费线看线看| 久久亚洲精品成人AV| 天天影视色香欲综合久久| 亚洲国产成人久久精品动漫| 久久久久人妻一区二区三区| 日本久久久久久中文字幕| 亚洲国产另类久久久精品| 久久久久久久综合日本| 久久―日本道色综合久久| 人妻精品久久无码专区精东影业| 亚洲国产成人久久一区久久| 91亚洲国产成人久久精品| 99久久久精品免费观看国产 | 国产精品女同久久久久电影院| 久久精品中文字幕大胸| 欧美精品丝袜久久久中文字幕| 久久精品国产99国产精偷| AAA级久久久精品无码片| 亚洲av伊人久久综合密臀性色| 久久精品国产亚洲av麻豆蜜芽| 久久亚洲国产精品123区| 国产福利电影一区二区三区,免费久久久久久久精| 综合网日日天干夜夜久久| 久久久久亚洲AV无码专区首JN| 99久久国产亚洲综合精品|