日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

由浅入深MFC学习摘记--第三部分

發布時間:2024/1/8 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 由浅入深MFC学习摘记--第三部分 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

由淺入深MFC摘要

  • 工欲善其事必先利其器
    • debug工具
  • 5章 Application Framework
      • General Purpose classes
      • Windows API classes
      • Application framework classes
      • High level abstractions
      • Afx 全局函數
      • MFC 宏
      • MFC 數據類型
  • 6章 MFC 程序的生命周期
    • 層次結構
    • 所需函數庫
    • 所需頭文件
    • MFC簡易程序
    • MFC 程序的來龍去脈
      • CWinApp 和 CFrameWnd
      • CWinApp
      • CFrameWnd
      • Application object
      • WinMain
      • 第一步 AfxWinInit
      • 第二步 CWinApp::InitApplication
      • 第三部 CMyWinApp::InitInstance
      • 第四步 CFrameWnd::Create
      • 窗口類名稱
      • 窗口顯示與更新
      • 第五步 CWinApp::Run
      • Message Map 機制
      • 小結
    • CallBack 函數
    • 空閑時間(idle time)的處理:OnIdle
    • Dialog 與 Control
    • 通用對話框(Common Dialogs)
    • 章節總結
  • 7章 MFC主程序
    • 牢記 MFC 類層級架構
    • UI接口
    • Document、View
    • 文檔模板
    • view
    • 主窗口
    • 工具欄、狀態欄
    • 鼠標拖放
    • 消息映射
    • 標準菜單
    • 關于對話框
    • CEditView

讀書筆記,記錄要點

工欲善其事必先利其器

mfc開發流程:

debug工具

debug版本的 mfc 程序,可以使用 trace 輸出調試信息。
或者使用afxDump

try catch的使用,catch有兩個參數,第一個為錯誤類型,第二個是錯誤對象。

5章 Application Framework

簡而言之就是通過使用應用框架(Application Framework)快速開發。

General Purpose classes

CObject:

  • RTTI(執行時期型別鑒識)
  • Persistence(對象保存)
  • Dynamic Creation(動態生成)
  • Diagnostic(錯誤診斷)

數據處理類別(collection classes)
理解為一些容器類

其他類

  • CRect
  • CSize
  • CPoint
  • CTime 絕對時間
  • CTimeSpan 以秒數表示時間
  • CString

異常處理類

Windows API classes

CWinThread:MFC 程序中的一個執行線程

CWinApp:整個MFC 應用程序。派生于CWinThread

CWnd: 所有窗口,不論是主框窗口、子框窗口、對話框、控制組件、view 視窗,都有一個對應的C++ 類。這些C++ 類統統派生于CWnd,也就是說,派生于CWnd 的類才能收到WM_ 窗口消息(WM_COMMAND 除外)。CWnd 對象有一個成員變量m_hWnd,就放著對應的窗口handle。所以,只要有一個CWnd 對象或CWnd 對象指針,就可以輕易獲得其窗口handle:

HWND hWnd = pWnd->m_hWnd;

CCmdTarget:- CWnd 的父類。繼承它的類才能夠處理命令消息WM_COMMAND
GDI 類
DC 類
Menu 類

Application framework classes

Document:文檔
View:界面

High level abstractions

視圖UI 對象,例如工具欄CToolBar、狀態欄CStatusBar、對話框CDialogBar。

Afx 全局函數

函數功能
AfxWinInit被WinMain調用的一個函數,用做MFC GUI程序初始化的一部份
AfxBeginThread開始一個線程
AfxEndThread結束一個線程
AfxFormatString1類似printf 將字符串格式化
AfxFormatString2類似printf 將字符串格式化
AfxMessageBox類似Windows API 函數MessageBox
AfxOutputDebugString把調試信息輸出到編譯器的輸出窗口
AfxGetApp取得application object(CWinApp 衍生對象)的指針
AfxGetMainWnd取得程序主窗口的指針。
AfxGetInstance取得程序的instance handle。
AfxRegisterClass以自定的WNDCLASS 注冊窗口類別;用來注冊窗口類的函數,用已注冊的窗口類可以創建一個窗口

MFC 宏

RTTI:(Run-Time Type Identification,運行時類型識別)
Serialization:序列化
Dynamic object creation:動態的對象生成

宏名稱提供機能
DECLARE_DYNAMIC執行時期類別信息
IMPLEMENT_DYNAMIC執行時期類別信息
DECLARE_DYNCREATE動態生成
IMPLEMENT_DYNCREATE動態生成
DECLARE_SERIAL對象內容的文件讀寫
IMPLEMENT_SERIAL對象內容的文件讀寫
DECLARE_OLECREATE OLE對象的動態生成
IMPLEMENT_OLECREATE OLE對象的動態生成

Message Mapping:消息映射
Command Routing: 命令傳遞

宏名稱提供機能
DECLARE_MESSAGE_MAP聲明消息映射表數據結構
BEGIN_MESSAGE_MAP開始消息映射表的建立
ON_COMMAND增加消息映射表中的項目
ON_CONTROL增加消息映射表中的項目
ON_MESSAGE增加消息映射表中的項目
ON_OLECMD增加消息映射表中的項目
ON_REGISTERED_MESSAGE增加消息映射表中的項目
ON_REGISTERED_THREAD_MESSAGE增加消息映射表中的項目
ON_THREAD_MESSAGE增加消息映射表中的項目
ON_UPDATE_COMMAND_UI增加消息映射表中的項目
END_MESSAGE_MAP結束消息映射表的建立

MFC 數據類型

和Win32 程序(SDK 程序)共同使用的數據類型

數據類型含義
BOOLBoolean 值(布爾值,不是TRUE 就是FALSE)
BSTR32-bit 字符指針(LPWSTR)
BYTE8-bit 整數,無符號
COLORREF32-bit 數值,代表一個顏色值
DWORD32-bit 整數,無符號
LONG32-bit 整數,帶符號
LPARAM32-bit 數值,做為窗口函數或callback 函數的一個參數
LPCSTR32-bit 指針,指向一個常數字符串
LPSTR32-bit 指針,指向一個字符串
LPCTSTR32-bit 指針,指向一個常數字符串。此字符串可移植到Unicode 和DBCS(雙字節字集)
LPTSTR32-bit 指針,指向一個字符串。此字符串可移植到Unicode 和DBCS(雙位組字集)
LPVOID32-bit 指針,指向一個未指定類型的資料
LPRESULT32-bit 數值,做為窗口函數或callback 函數的回返值
UINT在Win16 中是一個16-bit 無符號整數,在Win32 中是一個32-bit 無符號整數。
WNDPROC32-bit 指針,指向一個窗口函數
WORD16-bit 整數,無符號
WPARAM窗口函數的callback 函數的一個參數。在Win16 中是16 bits,在Win32中是32 bits。

下面這些是MFC 獨特的數據類型:

數據類型含義
POSITION一個數值,代表collection 對象(例如數組或串行)中的元素位置。常使用于MFC collection classes。
LPCRECT32-bit 指針,指向一個不變的RECT 結構。

常用的宏定義:

#define NULL 0 #define far //Win32 不再有far 或near memory model,而是使用所謂的flat model。pascall 函數調用習慣 #define near //也被stdcall 函數調用習慣取而代之。 #define pascal __stdcall //在Windows programming演化過程中曾經出現的PASCAL、CALLBACK、WINAPI、APIENTRY //現在都代表相同的意義,就是stdcall函數調用習慣。 #define cdecl _cdecl #define CDECL _cdecl #define CALLBACK __stdcall // #define WINAPI __stdcall // #define WINAPIV __cdecl // #define APIENTRY WINAPI // #define APIPRIVATE __stdcall #define PASCAL __stdcall #define FAR far #define NEAR near #define CONST const typedef unsigned long DWORD; typedef int BOOL; typedef unsigned char BYTE; typedef unsigned short WORD; typedef float FLOAT; typedef FLOAT *PFLOAT; typedef BOOL near *PBOOL; typedef BOOL far *LPBOOL; typedef BYTE near *PBYTE; typedef BYTE far *LPBYTE; typedef int near *PINT; typedef int far *LPINT; typedef WORD near *PWORD; typedef WORD far *LPWORD; typedef long far *LPLONG; typedef DWORD near *PDWORD; typedef DWORD far *LPDWORD; typedef void far *LPVOID; typedef CONST void far *LPCVOID; typedef int INT; typedef unsigned int UINT; typedef unsigned int *PUINT; /* Types use for passing & returning polymorphic values */ typedef UINT WPARAM; typedef LONG LPARAM; typedef LONG LRESULT; typedef DWORD COLORREF; typedef DWORD *LPCOLORREF; typedef struct tagRECT {LONG left;LONG top;LONG right;LONG bottom; } RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT; typedef const RECT FAR* LPCRECT; typedef struct tagPOINT {LONG x;LONG y; } POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT; typedef struct tagSIZE {LONG cx;LONG cy; } SIZE, *PSIZE, *LPSIZE;

6章 MFC 程序的生命周期

層次結構

所需函數庫

Windows C Runtime 函數庫
(runtime 是一個通用抽象的術語,指的是計算機程序運行的時候所需要的一切代碼庫,框架,平臺等)

文件名稱說明
LIBC.LIBC Runtime 函數庫的靜態聯結版本
MSVCRT.LIBC Runtime 函數庫的動態聯結版本
MSVCRTD.LIB‘D’ 表示使用于Debug 模式

DLL Import 函數庫

文件名稱說明
GDI32.LIBfor GDI32.DLL
USER32.LIBfor USER32.DLL
KERNEL32.LIBfor KERNEL32 DLL

MFC 函數庫 (AFX 函數庫)

所需頭文件

WINDOWS.H:大合集。
STDAFX.H:預編譯頭文件,用于包含其他頭文件
AFXWIN.H:MFC程序必須包含的頭文件,內含所有MFC類。(其中包含了AFX.H,AFX.H又包含了AFXVER_.H,AFXVER_.H又包含了AFXV_W32.H,AFXV_W32.H又包含了WINDOWS.H)
AFXEXT.H:使用工具欄、狀態欄需要包含的頭文件
AFXDLGS.H:對話框
AFXCMN.H:win95通用組件
AFXCOLL.H:Collections Classes
AFXDLLX.H:extension DLL
AFXRES.H:MFC標準資源

預編譯頭 就是將.H 文件第一次編譯后的結果貯存起來,第二次再編譯時就可以直接從磁盤中取出來用。有效減少大型應用程序的編譯時間。

MFC簡易程序

新建空項目,增加如下文件:
resource.h
需要在最后面增加一個回車

#define IDM_ABOUT 100

hello.rc

#include "resource.h" #include "afxres.h"JJHouRIcon ICON DISCARDABLE "JJHOUR.ICO" AFX_IDI_STD_FRAME ICON DISCARDABLE "JJHOUR.ICO"MainMenu MENU DISCARDABLE {POPUP "&Help"{MENUITEM "&About HelloMFC...", IDM_ABOUT} }AboutBox DIALOG DISCARDABLE 34, 22, 147, 55 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Hello" {ICON "JJHouRIcon", IDC_STATIC, 11, 17, 18, 20LTEXT "Hello MFC 4.0", IDC_STATIC, 40, 10, 52, 8LTEXT "Copyright 1996 Top Studio", IDC_STATIC, 40, 25, 100, 8LTEXT "J.J.Hou", IDC_STATIC, 40, 40, 100, 8DEFPUSHBUTTON "OK", IDOK, 105, 7, 32, 14, WS_GROUP }

stdafx.h

// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, // but are changed infrequently#include <afxwin.h> // MFC core and standard components

stdafx.cpp

// stdafx.cpp : source file that includes just the standard includes // Hello.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information#include "stdafx.h"

hello.h

class CMyWinApp : public CWinApp { public:BOOL InitInstance(); // };//--------------------------------------------------------------- class CMyFrameWnd : public CFrameWnd { public:CMyFrameWnd(); // constructorafx_msg void OnPaint(); // for WM_PAINTafx_msg void OnAbout(); // for WM_COMMAND (IDM_ABOUT)private:DECLARE_MESSAGE_MAP() // Declare Message Mapstatic VOID CALLBACK LineDDACallback(int, int, LPARAM);//注意: callback 函數必須是"static",才能去除隱藏的'this' 指針。};

hello.cpp

#include "stdafx.h" #include "hello.h" #include "resource.h"CMyWinApp theApp; // application object//--------------------------------------------------------------- // CMyWinApp's member //--------------------------------------------------------------- BOOL CMyWinApp::InitInstance() {m_pMainWnd = new CMyFrameWnd();m_pMainWnd->ShowWindow(m_nCmdShow);m_pMainWnd->UpdateWindow();return TRUE; } //--------------------------------------------------------------- // CMyFrameWnd's member //--------------------------------------------------------------- CMyFrameWnd::CMyFrameWnd() {Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, "MainMenu"); // "MainMenu" 定義于 RC 檔 } //--------------------------------------------------------------- BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)ON_COMMAND(IDM_ABOUT, OnAbout)ON_WM_PAINT() END_MESSAGE_MAP() //--------------------------------------------------------------- void CMyFrameWnd::OnPaint() {CPaintDC dc(this);CRect rect;GetClientRect(rect);dc.SetTextAlign(TA_BOTTOM | TA_CENTER);//沿著定義的起始點與結束點組成的直線,重復執行指定的LINEDDAPROC回調函數。在線條的每個坐標點(結束點除外)上執行一次回調函數。::LineDDA(rect.right / 2, 0, rect.right / 2, rect.bottom / 2,(LINEDDAPROC)LineDDACallback, (LPARAM)(LPVOID)&dc); } //--------------------------------------------------------------- VOID CALLBACK CMyFrameWnd::LineDDACallback(int x, int y, LPARAM lpdc) {static char szText[] = "Hello, MFC,textdraw";((CDC*)lpdc)->TextOut(x, y, szText, sizeof(szText) - 1);for (int i = 1; i < 5000000; i++); // 純粹是為了延遲下降速度,以利觀察 } //--------------------------------------------------------------- void CMyFrameWnd::OnAbout() {CDialog about("AboutBox", this); //"AboutBox" 定義于RC 文檔about.DoModal(); }

需要在工程目錄中增加一個圖標文件JJHOUR.ICO。
工程配置修改如下:

預處理器的內容不增加也可以編譯通過

其中說明:

#define CALLBACK __stdcall // 一種函數調用習慣 #define afx_msg // intentional placeholder故意安排的一個空位置。也許以后版本會用到。

MFC 程序的來龍去脈

CWinApp 和 CFrameWnd

CWinApp 代表程序本體,包含了WinMain。
CFrameWnd代表一個主框窗口(Frame Window),包含了WndProc。

CWinApp

CWinApp部分成員:

class CWinApp : public CWinThread { // Attributes// Startup args (do not change)HINSTANCE m_hInstance;HINSTANCE m_hPrevInstance;LPTSTR m_lpCmdLine;int m_nCmdShow;// Running args (can be changed in InitInstance)LPCTSTR m_pszAppName; // human readable nameLPCTSTR m_pszRegistryKey; // used for registry entries public: // set in constructor to override defaultLPCTSTR m_pszExeName; // executable name (no spaces)LPCTSTR m_pszHelpFilePath; // default based on module pathLPCTSTR m_pszProfileName; // default based on app name public:// hooks for your initialization codevirtual BOOL InitApplication();// overrides for implementationvirtual BOOL InitInstance();virtual int ExitInstance();virtual int Run();virtual BOOL OnIdle(LONG lCount); };

傳統上SDK 程序的WinMain(windows程序入口) 所完成的工作現在由CWinApp的三個函數完成:

virtual BOOL InitApplication(); virtual BOOL InitInstance(); virtual int Run();

CWinThread部分成員

class CWinThread : public CCmdTarget { // AttributesCWnd* m_pMainWnd; // main window (usually same AfxGetApp()->m_pMainWnd)CWnd* m_pActiveWnd; // active main window (may not be m_pMainWnd)// only valid while runningHANDLE m_hThread; // this thread's HANDLEDWORD m_nThreadID; // this thread's IDint GetThreadPriority();BOOL SetThreadPriority(int nPriority); // OperationsDWORD SuspendThread();//掛起線程DWORD ResumeThread();//喚醒線程 // Overridables// thread initializationvirtual BOOL InitInstance();// running and idle processingvirtual int Run();virtual BOOL PreTranslateMessage(MSG* pMsg);virtual BOOL PumpMessage(); // low level message pumpvirtual BOOL OnIdle(LONG lCount); // return TRUE if more idle processing public:// valid after constructionAFX_THREADPROC m_pfnThreadProc; ... };

CFrameWnd

CFrameWnd實現窗口過程的函數如下:

class CMyFrameWnd : public CFrameWnd { public:CMyFrameWnd();afx_msg void OnPaint();//處理 WM_PAINTafx_msg void OnAbout();//處理 WM_COMMAND 的 IDM_ABOUTDECLARE_MESSAGE_MAP() };

通過宏DECLARE_MESSAGE_MAP、 BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)、
END_MESSAGE_MAP()實現消息與對應函數的映射。

Application object

首先查看mfc的代碼WinMain.cpp

int AFXAPI AfxWinMain (...) {CWinApp* pApp = AfxGetApp();AfxWinInit(...);pApp->InitApplication();pApp->InitInstance();nReturnCode = pApp->Run();AfxWinTerm(); }

然后是我們模擬的例子hello.cpp

CMyWinApp theApp; // application object BOOL CMyWinApp::InitInstance() {m_pMainWnd = new CMyFrameWnd();m_pMainWnd->ShowWindow(m_nCmdShow);m_pMainWnd->UpdateWindow();return TRUE; } CMyFrameWnd::CMyFrameWnd() {Create(NULL, "Hello MFC", ...,"MainMenu"); } void CMyFrameWnd::OnPaint() { ... } void CMyFrameWnd::OnAbout() { ... } BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)ON_COMMAND(IDM_ABOUT, OnAbout)ON_WM_PAINT() END_MESSAGE_MAP()

theApp就是一個應用程序對象(Application object)。每一個MFC 應用程序都有一個,而且也只有這么一個。當執行hello.cpp時,這個全局對象被創建,執行其構造函數,此構造函數在父類CWinApp中:

CWinApp::CWinApp(LPCTSTR lpszAppName) {m_pszAppName = lpszAppName;// initialize CWinThread stateAFX_MODULE_THREAD_STATE* pThreadState = AfxGetModuleThreadState();pThreadState->m_pCurrentWinThread = this;m_hThread = ::GetCurrentThread();m_nThreadID = ::GetCurrentThreadId();// initialize CWinApp stateAFX_MODULE_STATE* pModuleState = AfxGetModuleState();pModuleState->m_pCurrentWinApp = this;// in non-running state until WinMainm_hInstance = NULL;m_pszHelpFilePath = NULL;m_pszProfileName = NULL;m_pszRegistryKey = NULL;m_pszExeName = NULL;m_lpCmdLine = NULL;m_pCmdInfo = NULL;... }

WinMain

theApp創建后就會調用WinMain函數。WinMain函數實際上是通過調用AfxWinMain函數來完成它的功能的。
_tWinMain 調用 AfxWinMain(APPMODUL.CPP)。

int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {ASSERT(hPrevInstance == NULL);int nReturnCode = -1;CWinApp* pApp = AfxGetApp();// AFX internal initializationif (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))goto InitFailure;// App global initializations (rare)ASSERT_VALID(pApp);if (!pApp->InitApplication())goto InitFailure;ASSERT_VALID(pApp);// Perform specific initializationsif (!pApp->InitInstance()){if (pApp->m_pMainWnd != NULL){TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");pApp->m_pMainWnd->DestroyWindow();}nReturnCode = pApp->ExitInstance();goto InitFailure;}ASSERT_VALID(pApp);nReturnCode = pApp->Run();ASSERT_VALID(pApp);InitFailure:AfxWinTerm();return nReturnCode; }

以上代碼簡化下就是:

int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow) {int nReturnCode = -1;CWinApp* pApp = AfxGetApp();AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);pApp->InitApplication();pApp->InitInstance();nReturnCode = pApp->Run();AfxWinTerm();return nReturnCode; }

AfxGetApp 是一個全局函數,定義于(afxwin1.inl)中

_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp(){ return afxCurrentWinApp; }

afxCurrentWinApp 定義于(afxwin.h*)中

#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp

AfxGetApp 其實就是取得CMyWinApp 對象指針即theApp
所以afxwinmain就相當于:

CWinApp* pApp = AfxGetApp(); pApp->InitApplication(); pApp->InitInstance(); nReturnCode = pApp->Run();

也就是順序調用:

CMyWinApp::InitApplication(); CMyWinApp::InitInstance(); CMyWinApp::Run();

考慮重寫就是:

CWinApp::InitApplication(); //因為 CMyWinApp 并沒有改寫InitApplication CMyWinApp::InitInstance(); //因為 CMyWinApp 改寫了 InitInstance CWinApp::Run(); //因為 CMyWinApp 并沒有改寫Run

第一步 AfxWinInit

AfxWinInit 是winmain中首先調用的,代碼如下

BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow) {ASSERT(hPrevInstance == NULL);// set resource handlesAFX_MODULE_STATE* pState = AfxGetModuleState();pState->m_hCurrentInstanceHandle = hInstance;pState->m_hCurrentResourceHandle = hInstance;// fill in the initial state for the applicationCWinApp* pApp = AfxGetApp();if (pApp != NULL){// Windows specific initialization (not done if no CWinApp)pApp->m_hInstance = hInstance;pApp->m_hPrevInstance = hPrevInstance;pApp->m_lpCmdLine = lpCmdLine;pApp->m_nCmdShow = nCmdShow;pApp->SetCurrentHandles();}// initialize thread specific data (for main thread)if (!afxContextIsDLL)AfxInitThread();return TRUE; }

其中 AfxInitThread:

void AFXAPI AfxInitThread() {if (!afxContextIsDLL){// attempt to make the message queue biggerfor (int cMsg = 96; !SetMessageQueue(cMsg) && (cMsg -= 8); );// set message filter proc_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();ASSERT(pThreadState->m_hHookOldMsgFilter == NULL);pThreadState->m_hHookOldMsgFilter = ::SetWindowsHookEx(WH_MSGFILTER,_AfxMsgFilterHook, NULL, ::GetCurrentThreadId());// intialize CTL3D for this thread_AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState;if (pCtl3dState->m_pfnAutoSubclass != NULL)(*pCtl3dState->m_pfnAutoSubclass)(AfxGetInstanceHandle());// allocate thread local _AFX_CTL3D_THREAD just for automatic termination_AFX_CTL3D_THREAD* pTemp = _afxCtl3dThread;} }

第二步 CWinApp::InitApplication

BOOL CWinApp::InitApplication() {if (CDocManager::pStaticDocManager != NULL){if (m_pDocManager == NULL)m_pDocManager = CDocManager::pStaticDocManager;CDocManager::pStaticDocManager = NULL;}if (m_pDocManager != NULL)m_pDocManager->AddDocTemplate(NULL);elseCDocManager::bStaticInit = FALSE;return TRUE; }

第三部 CMyWinApp::InitInstance

即調用hello.cpp中重寫的 InitInstance, 在該處創建主窗口。

應用程序一定要改寫虛擬函數InitInstance,因為它在CWinApp 中只是個空函數。

第四步 CFrameWnd::Create


new CMyFrameWnd 對象時,會進入構造函數:

CMyFrameWnd::CMyFrameWnd {//調用據CFrameWnd::CreateCreate(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL,"MainMenu"); }

CFrameWnd::Create 的參數如下

BOOL Create( LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle = WS_OVERLAPPEDWINDOW,const RECT& rect = rectDefault,CWnd* pParentWnd = NULL,LPCTSTR lpszMenuName = NULL,DWORD dwExStyle = 0,CCreateContext* pContext = NULL );

參數說明:

  • lpszClassName:指定 WNDCLASS 窗口類型,可以為NULL。
  • lpszWindowName: 指定窗口標題。
  • dwStyle: 指定窗口風格。
  • #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION |WS_SYSMENU | WS_THICKFRAME |WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
  • rect:指定窗口的位置與大小,如:
  • Create(NULL,"Hello MFC",WS_OVERLAPPEDWINDOW,CRect(40, 60, 240, 460), // 起始位置 (40,60),寬 200,高 400)NULL,"MainMenu");
  • dwExStyle:擴充風格。
  • pParentWnd: 指定父窗口。對于一個頂級 窗口而言,此值應為NULL,表示沒有父窗口(其實是有的,父窗口就是desktop 窗口)。
  • lpszMenuName: 指定菜單。
  • pContext: 是一個指向CCreateContext 結構的指針,用來指定在具備Document/View 架構的程序中初始化外框窗口的樣式。
  • 其簡略實現如下:

    //WINFRM.cpp BOOL CFrameWnd::Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,const RECT& rect,CWnd* pParentWnd,LPCTSTR lpszMenuName,DWORD dwExStyle,CCreateContext* pContext) {HMENU hMenu = NULL;if (lpszMenuName != NULL){// load in a menu that will get destroyed when window gets destroyedHINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);hMenu = ::LoadMenu(hInst, lpszMenuName);}m_strTitle = lpszWindowName; // save title for laterCreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext);return TRUE; }

    CFrameWnd 沒有 CreateEx 函數,所以調用繼承來的 CWnd::CreateEx;

    //WINCORE.CPP BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,int x, int y, int nWidth, int nHeight,HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam) {// allow modification of several common create parametersCREATESTRUCT cs;cs.dwExStyle = dwExStyle;cs.lpszClass = lpszClassName;cs.lpszName = lpszWindowName;cs.style = dwStyle;cs.x = x;cs.y = y;cs.cx = nWidth;cs.cy = nHeight;cs.hwndParent = hWndParent;cs.hMenu = nIDorHMenu;cs.hInstance = AfxGetInstanceHandle();cs.lpCreateParams = lpParam;PreCreateWindow(cs);AfxHookWindowCreate(this); //HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);... }

    PreCreateWindow 是虛函數,CWnd 和CFrameWnd 之中都有定義,這里調用是CFrameWnd::PreCreateWindow

    //WINFRM.CPP // CFrameWnd second phase creation BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) {if (cs.lpszClass == NULL){AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background}... }

    宏中AfxDeferRegisterClass定義如下:

    //AFXIMPL.H #define AfxDeferRegisterClass(fClass) \((afxRegisteredClasses & fClass) ? TRUE : AfxEndDeferRegisterClass(fClass))

    如果變量afxRegisteredClasses 的值顯示系統已經注冊了fClass 這種窗口類別,MFC 就啥也不做;否則就調用AfxEndDeferRegisterClass(fClass)注冊。

    // in AFXWIN.H #define afxRegisteredClasses AfxGetModuleState()->m_fRegisteredClasses

    注冊函數如下:

    BOOL AFXAPI AfxEndDeferRegisterClass(short fClass) {BOOL bResult = FALSE;// common initializationWNDCLASS wndcls;memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaultswndcls.lpfnWndProc = DefWindowProc;wndcls.hInstance = AfxGetInstanceHandle();wndcls.hCursor = afxData.hcurArrow;AFX_MODULE_STATE* pModuleState = AfxGetModuleState();if (fClass & AFX_WND_REG){// Child windows - no brush, no icon, safest default class styleswndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.lpszClassName = _afxWnd;bResult = AfxRegisterClass(&wndcls);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WND_REG;}else if (fClass & AFX_WNDOLECONTROL_REG){// OLE Control windows - use parent DC for speedwndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.lpszClassName = _afxWndOleControl;bResult = AfxRegisterClass(&wndcls);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WNDOLECONTROL_REG;}else if (fClass & AFX_WNDCONTROLBAR_REG){// Control bar windowswndcls.style = 0; // control bars don't handle double clickwndcls.lpszClassName = _afxWndControlBar;wndcls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);bResult = AfxRegisterClass(&wndcls);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WNDCONTROLBAR_REG;}else if (fClass & AFX_WNDMDIFRAME_REG){// MDI Frame window (also used for splitter window)wndcls.style = CS_DBLCLKS;wndcls.hbrBackground = NULL;bResult = RegisterWithIcon(&wndcls, _afxWndMDIFrame,AFX_IDI_STD_MDIFRAME);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WNDMDIFRAME_REG;}else if (fClass & AFX_WNDFRAMEORVIEW_REG){// SDI Frame or MDI Child windows or views - normal colorswndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);bResult = RegisterWithIcon(&wndcls, _afxWndFrameOrView,AFX_IDI_STD_FRAME);if (bResult)pModuleState->m_fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;}else if (fClass & AFX_WNDCOMMCTLS_REG){InitCommonControls();bResult = TRUE;pModuleState->m_fRegisteredClasses |= AFX_WNDCOMMCTLS_REG;}return bResult; }

    其中的的常數定義:

    #define AFX_WNDCLASS(s) \ _T("Afx") _T(s) _T("42") _STATIC_SUFFIX _UNICODE_SUFFIX _DEBUG_SUFFIX #define AFX_WND AFX_WNDCLASS("Wnd") #define AFX_WNDCONTROLBAR AFX_WNDCLASS("ControlBar") #define AFX_WNDMDIFRAME AFX_WNDCLASS("MDIFrame") #define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView") #define AFX_WNDOLECONTROL AFX_WNDCLASS("OleControl")

    所以debug編譯時這5個窗口的名稱是

    "AfxWnd42d" "AfxControlBar42d" "AfxMDIFrame42d" "AfxFrameOrView42d" "AfxOleControl42d"

    AfxEndDeferRegisterClass注冊中使用的函數:

    static BOOL AFXAPI RegisterWithIcon(WNDCLASS* pWndCls,LPCTSTR lpszClassName, UINT nIDIcon) {pWndCls->lpszClassName = lpszClassName;HINSTANCE hInst = AfxFindResourceHandle(MAKEINTRESOURCE(nIDIcon), RT_GROUP_ICON);if ((pWndCls->hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(nIDIcon))) == NULL){// use default iconpWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION);}return AfxRegisterClass(pWndCls); }BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass) {WNDCLASS wndcls;if (GetClassInfo(lpWndClass->hInstance,lpWndClass->lpszClassName, &wndcls)){// class already registeredreturn TRUE;}::RegisterClass(lpWndClass);...return TRUE; }

    不同類的PreCreateWindow 成員函數都是在窗口產生之前一刻被調用,準備用來注冊窗口類。如果我們指定的窗口類是NULL,那么就使用系統默認類。

    // in WINCORE.CPP BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs) {{AfxDeferRegisterClass(AFX_WND_REG);...cs.lpszClass = _afxWnd; //(這表示CWnd 使用的窗口類別是_afxWnd)}return TRUE; }// in WINFRM.CPP BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) {if (cs.lpszClass == NULL){AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);...cs.lpszClass = _afxWndFrameOrView; //(這表示CFrameWnd 使用的窗口類別是_afxWndFrameOrView)}     ... }// in WINMDI.CPP BOOL CMDIFrameWnd::PreCreateWindow(CREATESTRUCT& cs) {if (cs.lpszClass == NULL){AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG);//(這表示CMDIFrameWnd 使用的窗口類別是_afxWndMDIFrame)...cs.lpszClass = _afxWndMDIFrame; }     return TRUE; }// in WINMDI.CPP BOOL CMDIChildWnd::PreCreateWindow(CREATESTRUCT& cs) {...return CFrameWnd::PreCreateWindow(cs); //(這表示CMDIChildWnd 使用的窗口類別是_afxWndFrameOrView) }  // in VIEWCORE.CPP BOOL CView::PreCreateWindow(CREATESTRUCT & cs) {if (cs.lpszClass == NULL){AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);//(這表示CView 使用的窗口類別是_afxWndFrameOrView)...     cs.lpszClass = _afxWndFrameOrView; } ... }

    窗口類名稱

    選擇vs自帶安裝工具:

    選中并拖動 查找工具 圖標到指定窗口上,可以查看窗口的信息

    但是這里沒有看到示例的Afx:x:y:z:w 格式窗口名稱。
    此格式中:

    x: 窗口風格(window style)的hex y: 窗口鼠標光標的hex 值 值 z: 窗口背景顏色的hex 值 w: 窗口圖標(icon)的hex 值

    這種情況如果修改窗口名稱,需要在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)中修改。

    窗口顯示與更新


    調用ShowWindow 顯示窗口
    調用UpdateWindow向新窗口發送WM_PAINT消息

    第五步 CWinApp::Run

    窗口注冊→窗口產生并顯示→調用UpdateWindow→產生WM_PAINT 消息,等待被處理。

    此時該執行pApp->run(),由于沒有重寫該方法(一般也不用重寫),所以相當于調用CMyWinApp::Run()

    //APPCORE.CPP int CWinApp::Run() {if (m_pMainWnd == NULL && AfxOleGetUserCtrl()){// Not launched /Embedding or /Automation, but has no main window!TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quittingapplication.\n");AfxPostQuitMessage(0);}return CWinThread::Run(); } // THRDCORE.CPP int CWinThread::Run() {// for tracking the idle time stateBOOL bIdle = TRUE;LONG lIdleCount = 0;// acquire and dispatch messages until a WM_QUIT message is received.for (;;){// phase1: check to see if we can do idle workwhile (bIdle &&!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)){// call OnIdle while in bIdle stateif (!OnIdle(lIdleCount++))bIdle = FALSE; // assume "no idle" state}// phase2: pump messages while availabledo{// pump message, but quit on WM_QUITif (!PumpMessage())return ExitInstance();// reset "no idle" state after pumping "normal" messageif (IsIdleMessage(&m_msgCur)){bIdle = TRUE;lIdleCount = 0;}} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));}ASSERT(FALSE); // not reachable }BOOL CWinThread::PumpMessage() {if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)){return FALSE;}// process this messageif (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)){::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur);}return TRUE; }

    獲得的消息后SDK 程序的作法是調用DispatchMessage,把消息丟給窗口函數。而MFC在AfxEndDeferRegisterClass 源代碼中注冊四種窗口類之前已經指定窗口函數為:
    wndcls.lpfnWndProc = DefWindowProc;

    雖然窗口函數被指定為DefWindowProc 成員函數,但事實上消息是一個名為AfxWndProc 的全局函數處理。

    WinMain 由MFC 提供,窗口類由MFC 注冊完成,窗口函數也由MFC提供。

    Message Map 機制


    mfc的消息映射步驟:

  • 在頭文件的CMyFrameWnd 加上DECLARE_MESSAGE_MAP:
  • class CMyFrameWnd : public CFrameWnd { public:CMyFrameWnd();afx_msg void OnPaint();afx_msg void OnAbout();DECLARE_MESSAGE_MAP() };
  • 在cpp文件任何位置(當然不能在函數之內)使用宏:
  • BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)ON_WM_PAINT()ON_COMMAND(IDM_ABOUT, OnAbout) END_MESSAGE_MAP()

    如上,就把WM_PAINT 導到OnPaint 函數, 把WM_COMMAND(IDM_ABOUT)導到OnAbout 函數去了。

    Message Map 機制中對于消息與函數間的映射關系也規定以下三種:

    • 標準Windows 消息(WM_xxx)的映射規則:
    宏名稱映射的消息消息處理函數(名稱已由系統定好)
    ON_WM_CHARWM_CHAROnChar
    ON_WM_CLOSEWM_CLOSEOnClose
    ON_WM_CREATEWM_CREATEOnCreate
    ON_WM_DESTROYWM_DESTROYOnDestroy
    ON_WM_LBUTTONDOWNWM_LBUTTONDOWNOnLButtonDown
    ON_WM_LBUTTONUPWM_LBUTTONUPOnLButtonUp
    ON_WM_MOUSEMOVEWM_MOUSEMOVEOnMouseMove
    ON_WM_PAINTWM_PAINTOnPaint
    • 命令消息(WM_COMMAND)的一般映射規則:
      ON_COMMAND(<id>,<memberFxn>)
      例如:
      ON_COMMAND(IDM_ABOUT, OnAbout)
      ON_COMMAND(IDM_FILENEW, OnFileNew)
      ON_COMMAND(IDM_FILEOPEN, OnFileOpen)
      ON_COMMAND(IDM_FILESAVE, OnFileSave)

    • Notification 消息(由控件產生,例如BN_xxx)的映射機制的宏分為幾種(因為控件本就分為幾種):

    控件宏名稱消息處理函數
    ButtonON_BN_CLICKED(,)memberFxn
    ComboBoxON_CBN_DBLCLK(,)memberFxn
    EditON_EN_SETFOCUS(,)memberFxn
    ListBoxON_LBN_DBLCLK(,)memberFxn

    如果沒有指定消息映射函數,消息會在基類中處理,這個消息在基類流竄動作稱為「Message Routing」

    小結

  • 程序的誕生
    • 創建application object (即CMyWinApp theApp)
    • 執行程序入口函數,調用Afx WinMain ,調用 AfxWinInit,又調用AfxInitThread
    • Afx WinMain 調用 InitApplication。這是CWinApp 的虛函數,我們通常不改寫它。
    • AfxWinMain 調用 InitInstance。這是CWinApp 的虛函數,我們必須改寫它。
    • CMyWinApp::InitInstance new 一個 CMyFrameWnd 對象。
    • CMyFrameWnd 構造中調用Create,產生主窗口。我們在Create 參數中指定的窗口類別是NULL, 于是MFC 根據窗口種類, 自行為我們注冊一個名為"AfxFrameOrView42d" 的窗口類。
    • 回到 InitInstance 中繼續執行ShowWindow,顯示窗口
    • 執行UpdateWindow,于是發出 WM_PAINT。
    • 回到AfxWinMain,執行Run,進入消息循環
  • 程序的運行
    • 程序獲得WM_PAINT 消息(通過CWinApp::Run 中的::GetMessage 循環)
    • WM_PAINT 經由::DispatchMessage 送到窗口函數CWnd::DefWindowProc 中。
    • CWnd::DefWindowProc 將消息發送到消息映射表格(Message Map)
    • 調用映射中對應的函數。此函數是應用程序利用BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP 之間的宏映射的
    • 標準消息的處理函數有標準命名,例如WM_PAINT 必然由OnPaint 處理。
  • 程序的死亡
    • 使用者選按【File/Close】,于是發出 WM_CLOSE。
    • CMyFrameWnd 并沒有設置 WM_CLOSE 處理函數,于是交給默認處理函數
    • 默認函數對于WM_CLOSE 的處理方式是調用::DestroyWindow, 并因而發出 WM_DESTROY
    • 默認 WM_DESTROY 處理方式是調用::PostQuitMessage,因此發出WM_QUIT。
    • CWinApp::Run 收到 WM_QUIT 后會結束其內部之消息循環, 然后調用ExitInstance,這是CWinApp 的一個虛函數。
    • 如果 CMyWinApp 改寫了 ExitInstance , 那么 CWinApp::Run 所調用的就是CMyWinApp::ExitInstance,否則就是 CWinApp::ExitInstance。
    • 最后回到 AfxWinMain,執行 AfxWinTerm,結束程序

    CallBack 函數

    LineDDA函數:
    void WINAPI LineDDA(int, int, int, int, LINEDDAPROC, LPARAM);
    利用前四個參數指定屏幕上任意兩點的(x,y) 座標,此函數將以Bresenham 算法計算出通過兩點之直線中的每一個屏幕圖素座標;每計算出一個坐標,就通知由LineDDA 第五個參數所指定的callback 函數。
    LineDDA 的第六個(最后一個)參數可以視應用程序的需要傳遞一個32 位指針.

    LineDDA 的 callback 函數:
    typedef void (CALLBACK* LINEDDAPROC)(int, int, LPARAM);

    LineDDA 并不屬于任何一個MFC 類別,因此調用它必須使用C++ 的"scope operator"(也就是::)
    ::LineDDA(rect.right/2, 0, rect.right/2, rect.bottom/2,(LINEDDAPROC) LineDDACallback, (LPARAM) (LPVOID) &dc);

    其中LineDDACallback 是我們準備的callback 函數,必須在類中先有聲明:

    class CMyFrameWnd : public CFrameWnd { ... private:static VOID CALLBACK LineDDACallback(int,int,LPARAM); };

    類的成員函數是一個callback 函數, 你必須聲明它為"static",才能把C++ 編譯器加到函數的一個隱藏參數this 去掉

    空閑時間(idle time)的處理:OnIdle

    CWinThreadd::OnIdle函數:
    virtual BOOL OnIdle(LONG lCount);
    lCount 是系統傳進來的一個值,表示自從上次有消息進來,到現在,OnIdle 已經被調用了多少次。
    lCount 會持續累增,直到CWinThread::Run 的消息循環又獲得了一個消息,此值才重置為0

    測試重寫onIdle
    重寫app的OnIdle函數:

    窗口類增加IdleTimeHandler函數:

    class CMyFrameWnd : public CFrameWnd { public:CMyFrameWnd(); // constructorafx_msg void OnPaint(); // for WM_PAINTafx_msg void OnAbout(); // for WM_COMMAND (IDM_ABOUT)void IdleTimeHandler(LONG lCount); // we want it call by CMyWinApp::OnIdle... };

    app OnIdle函數的定義:

    BOOL ChelloworldApp::OnIdle(LONG lCount) {// TODO: 在此添加專用代碼和/或調用基類ChelloworldDlg* pWnd = (ChelloworldDlg*)m_pMainWnd;pWnd->IdleTimeHandler(lCount);return TRUE;return CWinApp::OnIdle(lCount); }

    窗口類函數CMyFrameWnd::IdleTimeHandler的實現

    void CMyFrameWnd::IdleTimeHandler(LONG lCount) {CString str;CRect rect(10,10,200,30);CDC* pDC = new CClientDC(this);str.Format(_T("%010d"), lCount);pDC->DrawText(str, &rect, DT_LEFT | DT_TOP); }

    運行CWinThread::Run,則需要pThread->InitInstance返回才行,但模態對話框程序有點特殊,會直接阻塞在DoMoDal函數中,然后運行自己的消息循環,CXXXApp::InitInstace在程序沒結束時,不會返回,故OnIdle不會得到運行;我用的是基于文檔的mfc向導。

    Dialog 與 Control

    “關于”對話框的創建:

    當使用者按下【File/About 】菜單, 根據Message Map 的設定,WM_COMMAND(IDM_ABOUT)被送到OnAbout 函數去。我們首先在OnAbout 中產生一個CDialog 對象,名為about。接下來直接調用CDialog::DoModal,對話框就開始運行。

    通用對話框(Common Dialogs)

    類描述
    CCommonDialog以下各類別的父類別
    CFileDialogFile 對話框(Open 或Save As)
    CPrintDialogPrint 對話框
    CFindReplaceDialogFind and Replace 對話框
    CColorDialogColor 對話框
    CFontDialogFont 對話框
    CPageSetupDialogPage Setup 對話框(MFC 4.0 新增)
    COleDialogOle 相關對話框


    如打開文件的操作:

    char szFileters[] = "Text fiels (*.txt)|*.txt|All files (*.*)|*.*||" CFileDialog opendlg (TRUE, "txt", "*.txt", OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters, this); if (opendlg.DoModal() == IDOK){filename = opendlg.GetPathName(); }

    第五個參數szFilters 指定使用者可以選擇的文件型態,
    最后一個參數是父窗口。

    當DoModal 回返,我們可以利用CFileDialog 的成員函數
    GetPathName 取得完整的文件路徑
    也可以使用另一個成員函數GetFileName 取其不含路徑的文件名稱
    或GetFileTitle 取得既不含路徑亦不含擴展名的文件名稱。

    章節總結

    一開始是一個派生自CWinApp 的全局對象application object,然后是一個隱藏的WinMain 函數,調用application object 的InitInstance 函數,將程序初始化。初始化動作包括構造一個窗口對象(CFrameWnd 對象),而其構造函數又調用CFrameWnd::Create 產生真正的窗口(并在產生之前要求MFC注冊窗口類別)。窗口產生后WinMain 又調用Run 激活消息循環,將WM_COMMAND(IDM_ABOUT)和WM_PAINT 分別交給成員函數OnAbout 和OnPaint 處理。

    7章 MFC主程序

    牢記 MFC 類層級架構

    UI接口

    document窗口:如word、excel可以操作多份文件,每一個文件作為一個 document。
    文件窗口
    關于窗口
    打印及預覽窗口

    Document、View

    Document 是數據
    View 是數據的表現、視圖

    文檔模板

    View 本身雖然已經是一個窗口,其外部卻必須再包裝一個外框窗口做為舞臺。這樣是為了讓View 可以獨立地放置于「MDI Document Frame 窗口」或「SDI Document Frame 窗口」或「OLE Document Frame 窗口」等各種應用之中。也可以說,Document Frame 窗口是View 窗口的一個容器。

    文檔內容、文檔視圖、文檔的窗口是一個組合,程序打開一個資料,就會產生對應的三個對象:

  • Document 對象
  • View 對象
  • CMDIChildWnd 對象(做為外框窗口)
    三個對象由 Document Template 對象來管理。
  • BOOL CScribbleApp::InitInstance() {...CMultiDocTemplate* pDocTemplate;pDocTemplate = new CMultiDocTemplate(IDR_SCRIBTYPE,RUNTIME_CLASS(CScribbleDoc),RUNTIME_CLASS(CChildFrame),RUNTIME_CLASS(CScribbleView));AddDocTemplate(pDocTemplate);... }

    其中,構造函數:

    CMultiDocTemplate::CMultiDocTemplate(UINT nIDResource,CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass );

    中,參數:
    nIDResource:這是一個資源ID,表示此一文件類型(文件格式)所使用的資源。
    pDocClass: 這是一個指針,指向Document 類
    pFrameClass: 這是一個指針,指向Child Frame 類
    pViewClass : 這是一個指針,指向View 類

    可以獲取CDocTemplate文件類型用到的七個常量,如 filterExt:

    CDocTemplate *pDoc1 = this->m_pDocument->GetDocTemplate(); CString strDefExt; pDoc1->GetDocString(strDefExt, CDocTemplate::filterExt);

    view

    view中常用的相應函數:
    OnDraw:畫面重繪
    OnLButtonDown:鼠標左鍵按下(作為鼠標事件的舉例)

    void CmultiDocTempView::OnLButtonDown(UINT nFlags, CPoint point) {// TODO: 在此添加消息處理程序代碼和/或調用默認值//在類視圖中,CmultiDocTempView類右鍵屬性中,找到消息,選擇需要重寫的消息CView::OnLButtonDown(nFlags, point);MessageBox(_T("hello lbutton down")); }

    主窗口

    創建過程:

    由 CMainFrame 的 LoadFrame 到 OnCreate。

    工具欄、狀態欄

    工具欄相關 主要動作和設置:

  • 產生一個隸屬于this 的工具欄
  • //簡單形式 m_wndToolBar.Create(this) //向導產生的 m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC)
  • 將RC 文件中的工具欄資源導入。IDR_MAINFRAME 在RC 中代表與工具欄有關的圖標、位置等資源。
  • m_wndToolBar.LoadToolBar(IDR_MAINFRAME) //或者 m_wndToolBar.LoadToolBar(theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME)

    LoadToolBar 知道如何把BITMAP 資源和TOOLBAR 資源搭配起來,完成工具欄的設定。如果不是使用資源工具來編輯工具欄,BITMAP 資源和TOOLBAR 資源就可能格數不符,那是不被允許的。

    狀態欄相關的動作:

  • 產生一個隸屬于this 對象的狀態欄
  • m_wndStatusBar.Create(this)
  • 狀態欄設置最右側的「指示窗口」,如圖
  • m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));

    鼠標拖放

    主要講述在initInstance中的應用的三個函數:

    BOOL CScribbleApp::InitInstance() {// Enable drag/drop openm_pMainWnd->DragAcceptFiles();// Enable DDE Execute openEnableShellOpen();RegisterShellFileTypes(TRUE); }

    CWnd::DragAcceptFile(BOOL bAccept=TRUE); 參數TRUE 表示你的主窗口以及每一個子窗口(文件窗口)都愿意接受來自Shell 的拖放文件。CFrameWnd 內有一個OnDropFiles 成員函數,負責對WM_DROPFIELS 消息做出反應,它會通知 application 對象的OnOpenDocument,并夾帶被拖放的文件的名稱。

    CWinApp::EnableShellOpen(); 當使用者在Shell 中對著本程序的文件文件快按兩下時,本程序能夠打開文件并讀內容。通常此函數后面跟隨著RegisterShellFileTypes。(我理解為文件默認打開的應用程序注冊。)

    CWinApp::RegisterShellFileTypes(); 此函數將向Shell 注冊本程序的文件類型。有了這樣的注冊動作,使用者在Shell 的雙擊動作才有著力點。這個函數搜尋Document Template 串行中的每一種文件類型,然后把它加到系統所維護的registry中。

    消息映射

    每一個派生自CCmdTarget 的類別都可以有自己的Message Map 以處理消息。
    需要在頭文件中聲明DECLARE_MESSAGE_MAP 宏,
    定義中BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP 兩個宏 之間,聲明消息和對應的處理函數。

    標準菜單

    菜單消息及其映射函數

    菜單內容命令項ID預設的處理函數
    File
    NewID_FILE_NEWCWinApp::OnFileNew
    OpenID_FILE_OPENCWinApp::OnFileOpen
    CloseID_FILE_CLOSECDocument::OnFileClose
    SaveID_FILE_SAVECDocument::OnFileSave
    Save AsID_FILE_SAVEASCDocument::OnFileSaveAs
    PrintID_FILE_PRINTCView::OnFilePrint
    PrintPre&viewID_FILE_PRINT_PREVIEW CView::OnFilePrintPreview
    PrintSetupID_FILE_PRINT_SETUP CWinApp::OnFilePrintSetup
    “Recent File Name”ID_FILE_MRU_FILE1~4CWinApp::OnOpenRecentFile
    ExitID_APP_EXITCWinApp::OnFileExit
    Edit
    UndoID_EDIT_UNDONone
    CutID_EDIT_CUTNone
    CopyID_EDIT_COPYNone
    PasteID_EDIT_PASTENone
    View
    ToolbarID_VIEW_TOOLBARFrameWnd::OnBarCheck Yes
    StatusBar ID_VIEW_STATUS_BARFrameWnd::OnBarCheck Yes
    Window(MDI only)
    NewWindow ID_WINDOW_NEWMDIFrameWnd::OnWindowNew Yes
    CascadeID_WINDOW_CASCADEMDIFrameWnd::OnWindowCmd Yes
    TileID_WINDOW_TILE_HORZMDIFrameWnd::OnWindowCmd Yes
    Arrange IconsID_WINDOW_ARRANGEMDIFrameWnd::OnWindowCmd Yes
    Help
    About AppNameID_APP_ABOUTNone

    關于對話框

    響應函數

    BEGIN_MESSAGE_MAP(CScribbleApp, CWinApp)ON_COMMAND(ID_APP_ABOUT, OnAppAbout) END_MESSAGE_MAP()

    CAboutDlg 是CDialog 的派生類

    CEditView

    替換基類從CView 改為CEditView


    界面變為可以輸入。

    總結

    以上是生活随笔為你收集整理的由浅入深MFC学习摘记--第三部分的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。