轉(zhuǎn)帖|使用教程|編輯:鮑佳佳|2021-01-04 11:17:32.777|閱讀 2405 次
概述:上一講講的是VS2010應(yīng)用程序工程中文件的組成結(jié)構(gòu),可能大家對工程的運(yùn)行原理還是很模糊,理不出頭緒,畢竟跟C++編程入門系列中的例程差別太大。這一節(jié)就為大家分析下MFC應(yīng)用程序框架的運(yùn)行流程。再為大家推薦一個MFC開發(fā)庫Xtreme ToolKit Pro。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關(guān)鏈接:
上一講講的是VS2010應(yīng)用程序工程中文件的組成結(jié)構(gòu),可能大家對工程的運(yùn)行原理還是很模糊,理不出頭緒,畢竟跟C++編程入門系列中的例程差別太大。這一節(jié)就為大家分析下MFC應(yīng)用程序框架的運(yùn)行流程。再為大家推薦一個MFC開發(fā)庫Xtreme ToolKit Pro。
Xtreme Toolkit Pro是MFC開發(fā)中最全面界面控件套包,它提供了Windows開發(fā)所需要的11種主流的Visual C++ MFC控件,包括Command Bars、Controls、Chart Pro、Calendar、Docking Pane、Property Grid、Report Control、Shortcut Bar、Syntax Edit、Skin Framework 和Task Panel。可點(diǎn)擊下載Xtreme Toolkit Pro最新試用版! 或者點(diǎn)擊獲取更多免費(fèi)Xtreme Toolkit Pro教程、視頻、示例!
一.SDK應(yīng)用程序與MFC應(yīng)用程序運(yùn)行過程的對比
程序運(yùn)行都要有入口函數(shù),在之前的C++教程中都是main函數(shù),而Windows應(yīng)用程序的入口函數(shù)是WinMain函數(shù),MFC程序也是從WinMain函數(shù)開始的。下面給出用Windows SDK寫的“HelloWorld”程序,與應(yīng)用程序框架進(jìn)行對比,這樣能更好的了解框架是怎樣運(yùn)行的。Windows SDK開發(fā)程序就是不使用MFC類庫,直接用Windows API函數(shù)進(jìn)行軟件開發(fā)。這并不是要講解SDK開發(fā),只是為了對比而簡單介紹,至于SDK開發(fā)可以在大家學(xué)完MFC以后選擇是否要研究,一般來說有簡單了解就可以了。
SDK應(yīng)用程序
首先,給出Windows SDK應(yīng)用程序“HelloWorld”的源碼:
C++代碼
#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("Hello world");
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("HELLO WORLD"),-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);
}
上面的程序運(yùn)行的流程是:進(jìn)入WinMain函數(shù)->初始化WNDCLASSEX,調(diào)用RegisterClassEx函數(shù)注冊窗口類->調(diào)用ShowWindow和UpdateWindow函數(shù)顯示并更新窗口->進(jìn)入消息循環(huán)。關(guān)于消息循環(huán)再簡單說下,Windows應(yīng)用程序是消息驅(qū)動的,系統(tǒng)或用戶讓應(yīng)用程序進(jìn)行某項(xiàng)操作或完成某個任務(wù)時會發(fā)送消息,進(jìn)入程序的消息隊(duì)列,然后消息循環(huán)會將消息隊(duì)列中的消息取出,交予相應(yīng)的窗口過程處理,此程序的窗口過程函數(shù)就是myWndProc函數(shù),窗口過程函數(shù)處理完消息就完成了某項(xiàng)操作或任務(wù)。本例是要顯示“HELLO WORLD”字符串,UpdateWindow函數(shù)會發(fā)送WM_PAINT消息,但是此消息不經(jīng)過消息隊(duì)列而是直接送到窗口過程處理,在窗口過程函數(shù)中最終繪制了“HELLO WORLD”字符串。
MFC應(yīng)用程序
下面是MFC應(yīng)用程序的運(yùn)行流程,通過MFC庫中代碼進(jìn)行分析:
首先在HelloWorld.cpp中定義全局對象theApp:CHelloWorldApp theApp;。調(diào)用CWinApp和CHelloWorldApp的構(gòu)造函數(shù)后,進(jìn)入WinMain函數(shù)(位于appmodul.cpp中)。
	
C++代碼
	
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函數(shù)。它調(diào)用了AfxWinMain函數(shù)(位于WinMain.cpp中)。
	
C++代碼
	
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函數(shù)位于THRDCORE.cpp中,由此函數(shù)進(jìn)入消息循環(huán)
nReturnCode = pThread->Run();
..............略
return nReturnCode;
}
	
	
上面InitInstance函數(shù)的代碼如下:
		
	
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中,注冊并創(chuàng)建窗口
if (!ProcessShellCommand(cmdInfo))
return FALSE;
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
	
		
	
InitInstance中的ProcessShellCommand函數(shù)又調(diào)用了CMainFrame的LoadFrame函數(shù)注冊并創(chuàng)建了窗口,執(zhí)行完P(guān)rocessShellCommand函數(shù)以后,調(diào)用了m_pMainWnd的ShowWindow和UpdateWindow函數(shù)顯示并更新框架窗口。這些是不是與上面的SDK程序十分類似?
接下來該是消息循環(huán)了,上面的AfxWinMain函數(shù)中調(diào)用了pThread的Run函數(shù)(位于THRDCORE.cpp中),在Run中包含了消息循環(huán)。Run函數(shù)的代碼如下:
	
C++
	
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中通過調(diào)用GetMessage、TranslateMessage、DispatchMessage等建立了消息循環(huán)并投遞消息。
窗口過程函數(shù)AfxWinProc形式如下:
	
C++代碼
	
LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam)
{
……
CWnd*pWnd=CWnd::FromHandlePermanent(hWnd);
ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);
}
	
	
兩者運(yùn)行過程對比
到此,通過對比可以發(fā)現(xiàn),MFC應(yīng)用程序的運(yùn)行流程與SDK程序是類似的,都是先進(jìn)行一些初始化過程,再注冊并創(chuàng)建窗口,然后顯示、更新窗口,最后進(jìn)入消息循環(huán),消息都由窗口過程函數(shù)處理?,F(xiàn)在大家是不是覺得有些頭緒了?在運(yùn)行流程上有基本的掌握即可。
二.MFC應(yīng)用程序框架主要類之間的關(guān)系
在第二講中,給大家演示了如何利用應(yīng)用程序向?qū)蓡挝臋n應(yīng)用程序框架,可以看到程序的基本框架和必要的代碼都自動生成了,上一講又講解了文件組成結(jié)構(gòu),實(shí)際上在前面自動生成的框架中比較重要的類包括以下幾個:CHelloWorldApp、CMainFrame、CHelloWorldDoc和CHelloWorldView,至于其他的類比如CClassView、CFileView等都是在框架窗口(CMainFrame)上創(chuàng)建的面板等,不是必要的。
現(xiàn)在就四個主要類的關(guān)系簡單講下,CHelloWorldApp類處理消息,將收到的消息分發(fā)給相應(yīng)的對象。CMainFrame是視圖CHelloWorldView的父窗口,視圖CHelloWorldView就顯示在CMainFrame的客戶區(qū)中。視圖類CHelloWorldView用來顯示文檔類CHelloWorldDoc中的數(shù)據(jù),并根據(jù)對視圖類的操作修改文檔類的數(shù)據(jù)。一個視圖類只能跟一個文檔類相聯(lián)系,而一個文檔類可以跟多個視圖類相聯(lián)系。關(guān)于視圖類和文檔類的關(guān)系后面會詳細(xì)講解。
本節(jié)VC++/MFC編程入門教程內(nèi)容比較多,主要是讓大家對MFC應(yīng)用程序的運(yùn)行原理有大概的了解。對于以后的MFC開發(fā)有很多好處。感謝您的閱讀,希望這篇文章能帶給你一定的幫助!如果這篇文章沒能滿足你的需求、點(diǎn)擊獲取更多文章教程!
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@ke049m.cn