zoukankan      html  css  js  c++  java
  • MFC程序运行流程

    ->进入入口函数_tWinMain()

    程序首先进入文件AppModul.cpp,找到_tWinMain()函数运行,调用其中的AfxWinMain()函数。

    由于为了支持UNICODE,C运行库对WinMain其实区分了UNICODE版和ANSI版.对UNICODE版的程序,C运行库将调用wWinMain,而对于ANSI版的应用,则调用WinMain.

    文件tchar.h定义:

    ...
    #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都会被调用.

    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()

    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))//初始化应用程序内部环境,设置MFC
    		goto InitFailure;
    
    	// App global initializations (rare)
    	if (pApp != NULL && !pApp->InitApplication())//初始化应用程序内部环境,设置MFC
    		goto InitFailure;
    
    	// Perform specific initializations
    	if (!pThread->InitInstance())//完成应用程序相关工作:设计窗口类、注册窗口类、创建窗口类、显示窗口类、更新窗口类
    	{
    		if (pThread->m_pMainWnd != NULL)
    		{
    			TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd
    ");
    			pThread->m_pMainWnd->DestroyWindow();
    		}
    		nReturnCode = pThread->ExitInstance();
    		goto InitFailure;
    	}
    	nReturnCode = pThread->Run();//进入消息循环,处理系统消息。
    //Run()调用PumpMessage函数,通过调用GetMessage、TranslateMessage、DispatchMessage等建立了消息循环并投递消息。
    
    InitFailure:
    #ifdef _DEBUG
    	// Check for missing AfxLockTempMap calls
    	if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
    	{
    		TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).
    ",
    			AfxGetModuleThreadState()->m_nTempMapLock);
    	}
    	AfxLockTempMaps();
    	AfxUnlockTempMaps(-1);
    #endif
    
    	AfxWinTerm();
    	return nReturnCode;
    }

    ->pThread->InitInstance()

    这是个虚函数,pThread指向的是theApp,调用的是子类C***App类中的InitInstance函数。

    KuaiPaiClient项目中的该函数为:

    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)
    	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);//将Doc类、CMainFrame和CView三个类用文档模板关联到一起,装载到当前应用程序的全局对象theApp中。
    
    	// 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 (!ProcessShellCommand(cmdInfo))//主要完成:设计窗口类、注册窗口类、创建窗口类
    		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;
    }
     
     

    ->ProcessShellCommand()调用了LoadFrame()注册并创建窗口

    ProcessShellCommand()——>OnCmdMsg()——>AfxDispatchCmdMsg()——>pfn_Command()——>OnFileNew()——>OpenDocumentFile()——>CreateNewFrame()——>LoadFrame()
    多次调用PreCreateWindow、CreateEx、AfxEndDeferRegisterClass 注册工具栏、状态栏等.
     
    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))
    			OnFileNew();
    		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()

    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 (!PreCreateWindow(cs))//此处调用的PreCreateWindow,事实上调用的是C***App的成员函数,虚函数,可以在C***App中重写,这样可以在窗口被创建前通过结构体CREATESTRUCT更改窗口风格等
    	{
    		PostNcDestroy();
    		return FALSE;
    	}
    
    	AfxHookWindowCreate(this);
    	HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,//开始创建窗口
    			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
    ",
    			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()

    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;//使窗口类的窗口过程为系统的默认窗口过程。
    	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 (AfxRegisterClass(&wndcls))//注册窗口
    			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;
    }
    作者:Lucas Hsueh
    文章部分是自己的学习体会、代码等,还有收集和整理其他技术牛人的文章。
  • 相关阅读:
    Object.keys
    数组内容深拷贝的应用
    CSS如何让页脚固定在页面底部
    vue eslint开发 关掉 tab错误提示
    input框,需要隐式显示的时候,不让它自动填充的办法
    关于BFC
    File协议与HTTP协议 以及区别
    关于缓存
    深拷贝浅拷贝 遇到了bug
    聚餐学习
  • 原文地址:https://www.cnblogs.com/lucas-hsueh/p/3713563.html
Copyright © 2011-2022 走看看