【MFC】MFC应用程序框架详解
发布日期:2021-06-29 20:50:24 浏览次数:2 分类:技术文章

本文共 6625 字,大约阅读时间需要 22 分钟。

00. 目录

文章目录

01. Win32应用程序

程序运行都要有入口函数,在之前的C++教程中都是main函数,而Windows应用程序的入口函数是WinMain函数,MFC程序也是从WinMain函数开始的。下面就给出用Windows SDK写的“Test”程序,与应用程序框架进行对比,这样能更好的了解框架是怎样运行的。Windows SDK开发程序就是不使用MFC类库,直接用Windows API函数进行软件开发。我们不是要讲解SDK开发,只是为了对比而简单介绍,至于SDK开发一般来说有简单了解就可以了。

#include 
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. 附录

转载地址:https://dengjin.blog.csdn.net/article/details/113420431 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【MFC】类的层次结构图
下一篇:【MFC】MFC工程文件详解

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月20日 05时31分08秒