MFC程序运行流程
->进入入口函数_tWinMain()
程序首先进入文件AppModul.cpp,找到_tWinMain()函数运行,调用其中的AfxWinMain()函数。
由于为了支持UNICODE,C运行库对WinMain其实区分了UNICODE版和ANSI版.对UNICODE版的程序,C运行库将调用wWinMain,而对于ANSI版的应用,则调用WinMain.
文件tchar.h定义:
1 2 3 4 5 6 7 8 9 10 11 12 | ... #ifdef _UNICODE ... #define _tmain wmain #define _tWinMain wWinMain ... #else /* ndef _UNICODE */ ... #define _tmain main #define _tWinMain WinMain ... |
MFC的代码设计时是自动支持UNICODE的,所以,MFC的WinMain在APPMODUL.CPP被定义为_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow),无论用户#define _UNICODE与否,MFC的WinMain都会被调用.
1 2 3 4 5 6 7 8 | 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); } |
hInstance:表示该程序当前运行的实例句柄,它是一个数值。当程序在Windows下运行时,它唯一标识运行中的实例。
hPrevInstance:父窗口句柄。这个参数在Win32下已经不起作用了。
lpCmpLine:指定传递给应用程序的命令行参数。
nCmdShow:指定程序窗口如何显示,例如最大化、最小化、隐藏等。
->进入AfxWinMain()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | int AFXAPI AfxWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow) { ASSERT(hPrevInstance == NULL); int nReturnCode = -1; CWinThread* pThread = AfxGetThread(); //返回指向theApp的指针 CWinApp* pApp = AfxGetApp(); //返回指向theApp的指针 // AFX internal initialization if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) //<strong>初始化应用程序内部环境,设置MFC</strong> goto InitFailure; // App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) //<strong>初始化应用程序内部环境,设置MFC</strong> goto InitFailure; // Perform specific initializations if (!<strong>pThread->InitInstance</strong>()) //<strong>完成应用程序相关工作:设计窗口类、注册窗口类、创建窗口类、显示窗口类、更新窗口类</strong> { if (pThread->m_pMainWnd != NULL) { TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n" ); pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } nReturnCode = pThread->Run(); //<strong>进入消息循环,处理系统消息。</strong> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <strong> //Run()调用PumpMessage函数,通过调用GetMessage、TranslateMessage、DispatchMessage等建立了消息循环并投递消息。</strong> InitFailure: #ifdef _DEBUG // Check for missing AfxLockTempMap calls if (AfxGetModuleThreadState()->m_nTempMapLock != 0) { TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n" , AfxGetModuleThreadState()->m_nTempMapLock); } AfxLockTempMaps(); AfxUnlockTempMaps(-1); #endif AfxWinTerm(); return nReturnCode; } |
->pThread->InitInstance()
这是个虚函数,pThread指向的是theApp,调用的是子类C***App类中的InitInstance函数。
KuaiPaiClient项目中的该函数为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | BOOL CKuaiPanClientApp::InitInstance() { // InitCommonControlsEx() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof (InitCtrls); // Set this to include all the common control classes you want to use // in your application. InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinAppEx::InitInstance(); // Initialize OLE libraries if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } AfxEnableControlContainer(); EnableTaskbarInteraction(FALSE); // AfxInitRichEdit2() is required to use RichEdit control // AfxInitRichEdit2(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need // Change the registry key under which our settings are stored // TODO: You should modify this string to be something appropriate // such as the name of your company or organization SetRegistryKey(_T( "Local AppWizard-Generated Applications" )); LoadStdProfileSettings(4); // Load standard INI file options (including MRU) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | InitContextMenuManager(); InitKeyboardManager(); InitTooltipManager(); CMFCToolTipInfo ttParams; ttParams.m_bVislManagerTheme = TRUE; theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL, RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams); // Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CKuaiPanClientDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CKuaiPanClientView)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); //<strong>将Doc类、CMainFrame和CView三个类用文档模板关联到一起,装载到当前应用程序的全局对象theApp中。</strong> // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Dispatch commands specified on the command line. Will return FALSE if // app was launched with /RegServer, /Register, /Unregserver or /Unregister. if (!<strong>ProcessShellCommand</strong>(cmdInfo)) //<strong>主要完成:设计窗口类、注册窗口类、创建窗口类</strong> return FALSE; // The one and only window has been initialized, so show and update it m_pMainWnd->ShowWindow(SW_SHOW); //显示窗口 m_pMainWnd->UpdateWindow(); //更新框架窗口 // call DragAcceptFiles only if there's a suffix // In an SDI app, this should occur after ProcessShellCommand return TRUE; } |
1 | |
1 | |
->ProcessShellCommand()调用了LoadFrame()注册并创建窗口
1 | ProcessShellCommand()——>OnCmdMsg()——>AfxDispatchCmdMsg()——>pfn_Command()——>OnFileNew()——>OpenDocumentFile()——>CreateNewFrame()——>LoadFrame() |
1 | 多次调用PreCreateWindow、CreateEx、AfxEndDeferRegisterClass 注册工具栏、状态栏等. |
1 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo) { BOOL bResult = TRUE; switch (rCmdInfo.m_nShellCommand) { case CCommandLineInfo::RestartByRestartManager: // Re-register with the restart manager using the restart identifier from the command line RegisterWithRestartManager(SupportsApplicationRecovery(), rCmdInfo.m_strRestartIdentifier); // Call RestartIntance, which should reopen any previously opened document(s) and // optionally load the autosaved versions after querying the user about loading. if (RestartInstance()) break ; // If RestartInstance returns FALSE, then fall through to FileNew case, so // a new document is created--otherwise the main frame will not be created. case CCommandLineInfo::FileNew: if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)) <strong>OnFileNew</strong>(); if (m_pMainWnd == NULL) bResult = FALSE; break ; // If we've been asked to open a file, call OpenDocumentFile() case CCommandLineInfo::FileOpen: if (!OpenDocumentFile(rCmdInfo.m_strFileName)) bResult = FALSE; break ; // If the user wanted to print, hide our main window and // fire a message to ourselves to start the printing case CCommandLineInfo::FilePrintTo: case CCommandLineInfo::FilePrint: m_nCmdShow = SW_HIDE; ASSERT(m_pCmdInfo == NULL); if (OpenDocumentFile(rCmdInfo.m_strFileName)) { m_pCmdInfo = &rCmdInfo; ENSURE_VALID(m_pMainWnd); m_pMainWnd->SendMessage(WM_COMMAND, ID_FILE_PRINT_DIRECT); m_pCmdInfo = NULL; } bResult = FALSE; break ; // If we're doing DDE print or print to, start up without a new document and hide ourselves case CCommandLineInfo::FileDDENoShow: m_pCmdInfo = (CCommandLineInfo*)( UINT_PTR )m_nCmdShow; m_nCmdShow = SW_HIDE; break ; // If we're doing DDE open, start up without a new document, but don't hide ourselves -- this causes the // icon in the Windows 7 taskbar to start in the wrong position and slide into the right position. case CCommandLineInfo::FileDDE: break ; // If we've been asked to register, exit without showing UI. // Registration was already done in InitInstance(). case CCommandLineInfo::AppRegister: { Register(); bResult = FALSE; // that's all we do // If nobody is using it already, we can use it. // We'll flag that we're unregistering and not save our state // on the way out. This new object gets deleted by the // app object destructor. if (m_pCmdInfo == NULL) { m_pCmdInfo = new CCommandLineInfo; m_pCmdInfo->m_nShellCommand = CCommandLineInfo::AppUnregister; } break ; } // If we've been asked to unregister, unregister and then terminate case CCommandLineInfo::AppUnregister: { BOOL bUnregistered = Unregister(); if (!rCmdInfo.m_bRunEmbedded) { if (bUnregistered) AfxMessageBox(AFX_IDP_UNREG_DONE); else AfxMessageBox(AFX_IDP_UNREG_FAILURE); } bResult = FALSE; // that's all we do // If nobody is using it already, we can use it. // We'll flag that we're unregistering and not save our state // on the way out. This new object gets deleted by the // app object destructor. if (m_pCmdInfo == NULL) { m_pCmdInfo = new CCommandLineInfo; m_pCmdInfo->m_nShellCommand = CCommandLineInfo::AppUnregister; } } break ; } return bResult; } |
->CreateEx()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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) { ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) || AfxIsValidAtom(lpszClassName)); ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName)); // allow modification of several common create parameters CREATESTRUCT 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; if (!<strong>PreCreateWindow</strong>(cs)) //<strong>此处调用的PreCreateWindow,事实上调用的是C***App的成员函数,虚函数,可以在C***App中重写,这样可以在窗口被创建前通过结构体CREATESTRUCT更改窗口风格等 { PostNcDestroy();</strong> return FALSE; } AfxHookWindowCreate( this ); HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass, //<strong>开始创建窗口</strong> cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); #ifdef _DEBUG if (hWnd == NULL) { TRACE(traceAppMsg, 0, "Warning: Window creation failed: GetLastError returns 0x%8.8X\n" , GetLastError()); } #endif if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if CreateWindowEx fails too soon if (hWnd == NULL) return FALSE; ASSERT(hWnd == m_hWnd); // should have been set in send msg hook return TRUE; } |
->AfxEndDeferRegisterClass()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | BOOL AFXAPI AfxEndDeferRegisterClass( LONG fToRegister) { // mask off all classes that are already registered AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); fToRegister &= ~pModuleState->m_fRegisteredClasses; if (fToRegister == 0) return TRUE; LONG fRegisteredClasses = 0; // common initialization WNDCLASS wndcls; memset (&wndcls, 0, sizeof (WNDCLASS)); // start with NULL defaults wndcls.lpfnWndProc = DefWindowProc; //<strong>使窗口类的窗口过程为系统的默认窗口过程。</strong> wndcls.hInstance = AfxGetInstanceHandle(); wndcls.hCursor = afxData.hcurArrow; INITCOMMONCONTROLSEX init; init.dwSize = sizeof (init); // work to register classes as specified by fToRegister, populate fRegisteredClasses as we go if (fToRegister & AFX_WND_REG) { // Child windows - no brush, no icon, safest default class styles wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.lpszClassName = _afxWnd; if (<strong>AfxRegisterClass</strong>(&wndcls)) //<strong>注册窗口</strong> fRegisteredClasses |= AFX_WND_REG; } if (fToRegister & AFX_WNDOLECONTROL_REG) { // OLE Control windows - use parent DC for speed wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.lpszClassName = _afxWndOleControl; if (AfxRegisterClass(&wndcls)) fRegisteredClasses |= AFX_WNDOLECONTROL_REG; } if (fToRegister & AFX_WNDCONTROLBAR_REG) { // Control bar windows wndcls.style = 0; // control bars don't handle double click wndcls.lpszClassName = _afxWndControlBar; wndcls.hbrBackground = ( HBRUSH )(COLOR_BTNFACE + 1); if (AfxRegisterClass(&wndcls)) fRegisteredClasses |= AFX_WNDCONTROLBAR_REG; } if (fToRegister & AFX_WNDMDIFRAME_REG) { // MDI Frame window (also used for splitter window) wndcls.style = CS_DBLCLKS; wndcls.hbrBackground = NULL; if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame, AFX_IDI_STD_MDIFRAME)) fRegisteredClasses |= AFX_WNDMDIFRAME_REG; } if (fToRegister & AFX_WNDFRAMEORVIEW_REG) { // SDI Frame or MDI Child windows or views - normal colors wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.hbrBackground = ( HBRUSH ) (COLOR_WINDOW + 1); if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME)) fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG; } if (fToRegister & AFX_WNDCOMMCTLS_REG) { // this flag is compatible with the old InitCommonControls() API init.dwICC = ICC_WIN95_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WIN95CTLS_MASK); fToRegister &= ~AFX_WIN95CTLS_MASK; } if (fToRegister & AFX_WNDCOMMCTL_UPDOWN_REG) { init.dwICC = ICC_UPDOWN_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_UPDOWN_REG); } if (fToRegister & AFX_WNDCOMMCTL_TREEVIEW_REG) { init.dwICC = ICC_TREEVIEW_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TREEVIEW_REG); } if (fToRegister & AFX_WNDCOMMCTL_TAB_REG) { init.dwICC = ICC_TAB_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TAB_REG); } if (fToRegister & AFX_WNDCOMMCTL_PROGRESS_REG) { init.dwICC = ICC_PROGRESS_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_PROGRESS_REG); } if (fToRegister & AFX_WNDCOMMCTL_LISTVIEW_REG) { init.dwICC = ICC_LISTVIEW_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_LISTVIEW_REG); } if (fToRegister & AFX_WNDCOMMCTL_HOTKEY_REG) { init.dwICC = ICC_HOTKEY_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_HOTKEY_REG); } if (fToRegister & AFX_WNDCOMMCTL_BAR_REG) { init.dwICC = ICC_BAR_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_BAR_REG); } if (fToRegister & AFX_WNDCOMMCTL_ANIMATE_REG) { init.dwICC = ICC_ANIMATE_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_ANIMATE_REG); } if (fToRegister & AFX_WNDCOMMCTL_INTERNET_REG) { init.dwICC = ICC_INTERNET_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_INTERNET_REG); } if (fToRegister & AFX_WNDCOMMCTL_COOL_REG) { init.dwICC = ICC_COOL_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_COOL_REG); } if (fToRegister & AFX_WNDCOMMCTL_USEREX_REG) { init.dwICC = ICC_USEREX_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_USEREX_REG); } if (fToRegister & AFX_WNDCOMMCTL_DATE_REG) { init.dwICC = ICC_DATE_CLASSES; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_DATE_REG); } if (fToRegister & AFX_WNDCOMMCTL_LINK_REG) { init.dwICC = ICC_LINK_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_LINK_REG); } if (fToRegister & AFX_WNDCOMMCTL_PAGER_REG) { init.dwICC = ICC_PAGESCROLLER_CLASS; fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_PAGER_REG); } // save new state of registered controls pModuleState->m_fRegisteredClasses |= fRegisteredClasses; // special case for all common controls registered, turn on AFX_WNDCOMMCTLS_REG if ((pModuleState->m_fRegisteredClasses & AFX_WIN95CTLS_MASK) == AFX_WIN95CTLS_MASK) { pModuleState->m_fRegisteredClasses |= AFX_WNDCOMMCTLS_REG; fRegisteredClasses |= AFX_WNDCOMMCTLS_REG; } // must have registered at least as mamy classes as requested return (fToRegister & fRegisteredClasses) == fToRegister; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步