【MFC】MFC应用程序框架详解
00. 目錄
文章目錄
- 00. 目錄
- 01. Win32應用程序
- 02. MFC應用程序
- 03. MFC應用程序框架總結
- 04. 附錄
01. Win32應用程序
程序運行都要有入口函數,在之前的C++教程中都是main函數,而Windows應用程序的入口函數是WinMain函數,MFC程序也是從WinMain函數開始的。下面就給出用Windows SDK寫的“Test”程序,與應用程序框架進行對比,這樣能更好的了解框架是怎樣運行的。Windows SDK開發程序就是不使用MFC類庫,直接用Windows API函數進行軟件開發。我們不是要講解SDK開發,只是為了對比而簡單介紹,至于SDK開發一般來說有簡單了解就可以了。
#include <windows.h> LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { const static TCHAR appName[] = TEXT("Test"); WNDCLASSEX myWin; myWin.cbSize = sizeof(myWin); myWin.style = CS_HREDRAW | CS_VREDRAW; myWin.lpfnWndProc = myWndProc; myWin.cbClsExtra = 0; myWin.cbWndExtra = 0; myWin.hInstance = hInstance; myWin.hIcon = 0; myWin.hIconSm = 0; myWin.hCursor = 0; myWin.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); myWin.lpszMenuName = 0; myWin.lpszClassName = appName; //Register if (!RegisterClassEx(&myWin)) return 0; const HWND hWindow = CreateWindow( appName, appName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0); ShowWindow(hWindow,iCmdShow); UpdateWindow(hWindow); { MSG msg; while(GetMessage(&msg,0,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } } LRESULT CALLBACK myWndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg==WM_PAINT) { PAINTSTRUCT ps; const HDC hDC = BeginPaint(hWindow,&ps); RECT rect; GetClientRect(hWindow,&rect); DrawText(hDC,TEXT("Test"),-1,&rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hWindow,&ps); return 0; } else if (msg==WM_DESTROY) { PostQuitMessage(0); return 0; } return DefWindowProc(hWindow,msg,wParam,lParam); }上面的程序運行的流程是:進入WinMain函數->初始化WNDCLASSEX,調用RegisterClassEx函數注冊窗口類->調用ShowWindow和UpdateWindow函數顯示并更新窗口->進入消息循環。關于消息循環再簡單說下,Windows應用程序是消息驅動的,系統或用戶讓應用程序進行某項操作或完成某個任務時會發送消息,進入程序的消息隊列,然后消息循環會將消息隊列中的消息取出,交給相應的窗口過程處理,此程序的窗口過程函數就是myWndProc函數,窗口過程函數處理完消息就完成了某項操作或任務。本例是要顯示“Test”字符串,UpdateWindow函數會發送WM_PAINT消息,但是此消息不經過消息隊列而是直接送到窗口過程處理,在窗口過程函數中最終繪制了“Test”字符串。
02. MFC應用程序
首先在Test.cpp中定義全局對象theApp:CTestApp theApp;。調用CWinApp和CTestApp的構造函數后,進入WinMain函數(位于appmodul.cpp中)。
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow) #pragma warning(suppress: 4985) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); }在TCHAR.h中,有此定義:#define _tWinMain WinMain,所以這里的_tWinMain就是WinMain函數。它調用了AfxWinMain函數(位于WinMain.cpp中)。
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow) { //.............略 // App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) goto InitFailure; if (!pThread->InitInstance()) { // .........略 } // Run函數位于THRDCORE.cpp中,由此函數進入消息循環 nReturnCode = pThread->Run(); //..............略 return nReturnCode; }InitInstance函數的代碼如下:
BOOL CTestApp::InitInstance() { //.............略 CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTestDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CTestView)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); //ProcessShellCommand位于AppUI2.cpp中,注冊并創建窗口 if (!ProcessShellCommand(cmdInfo)) return FALSE; m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; }InitInstance中的ProcessShellCommand函數又調用了CMainFrame的LoadFrame函數注冊并創建了窗口,執行完ProcessShellCommand函數以后,調用了m_pMainWnd的ShowWindow和UpdateWindow函數顯示并更新框架窗口。
接下來該是消息循環了,上面的AfxWinMain函數中調用了pThread的Run函數(位于THRDCORE.cpp中),在Run中包含了消息循環。Run函數的代碼如下:
int CWinThread::Run() { //.............略 // phase2: pump messages while available do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); //..............略 } BOOL CWinThread::PumpMessage() { return AfxInternalPumpMessage(); } BOOL AFXAPI AfxInternalPumpMessage() { _AFX_THREAD_STATE *pState = AfxGetThreadState(); if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL)) { // .............略 } //...............略 if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur))) { ::TranslateMessage(&(pState->m_msgCur)); ::DispatchMessage(&(pState->m_msgCur)); } return TRUE; }PumpMessage中通過調用GetMessage、TranslateMessage、DispatchMessage等建立了消息循環并投遞消息。
窗口過程函數AfxWinProc形式如下:
LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam) { //…… CWnd*pWnd=CWnd::FromHandlePermanent(hWnd); ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam); }MFC應用程序的運行流程與SDK程序是類似的,都是先進行一些初始化過程,再注冊并創建窗口,然后顯示、更新窗口,最后進入消息循環,消息都由窗口過程函數處理。
03. MFC應用程序框架總結
CMainFrame是視圖CTestView的父窗口,視圖CTestView就顯示在CMainFrame的客戶區中。視圖類CTestView用來顯示文檔類CTestDoc中的數據,并根據對視圖類的操作修改文檔類的數據。一個視圖類只能跟一個文檔類相聯系,而一個文檔類可以跟多個視圖類相聯系。
04. 附錄
總結
以上是生活随笔為你收集整理的【MFC】MFC应用程序框架详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【MFC】MFC工程文件详解
- 下一篇: 【MFC】类的层次结构图