程序启动流程

  • 系统shell加载程序的codedatadll并调用CreateProcess()创建了一个代表你程序的进程
  • 启动第一个线程(PCB,TCB等的创建)

MFC程序启动流程

  • _tWinMain –> AfxWinMain (WINMAIN.CPP 21)
  • AfxWinMain()负责调用CWinApp中的InitApplication(),InitInstance(),Run()等几个主要函数.

WinMain

int WINAPI WinMain(HINSTANCE hInstance/*当前进程句柄*/, HINSTANCE hPrevInstance/*win32:NULL*/, LPSTR cmd/*argv*/, int show/*maxmize...*/) {
	WNDCLASS wc = {0};
	wc.lpszClassName = "ZClass";
	wc.lpfnWndProc = (WNDPROC)ZWindowProc;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.style = CS_VERDRAW | CS_HREDRAW | CS_NOCLOSE/*禁止标题栏关闭*/;
	if (!RegisterClass(&wc)) { return 0; }

	// CreateWindow会产生WM_CREATE消息,这是窗口过程收到的第1个消息.
	HWND hWndMain = CreateWindow("ZClass", "caption", WS_OVERLAPPEDWINDOW, 0,0,8,8, NULL,NULL, hInstance, NULL);
	ShowWindow(hWndMain, SW_SHOW);
	// emit WM_PAINT, 这是窗口过程的第2个消息!
	UpdateWindow(hWndMain);

	/* 消息循环 */
	MSG msg;
	while (GetMessage(&msg, NULL, 0,0)) { // NULL:获取所有窗口消息;2个0:不过滤
		// 将WM_KEYDOWN翻译成WM_CHAR.
		TranslateMessage(&msg);
		// 将消息转发(OS)到窗口过程!
		DispatchMessage(&msg);
	}

	return 0;
}

Message Map

  • CObject实现了运行时类型识别功能: CObject::IsKindOf()
  • CCmdTarget实现了消息映射
  • CWinThread没有保存消息映射表,故不适用其父类:BEGIN_MESSAGE_MAP(CWinApp, CCmdTarget)
  • declare_message_map这些宏主要是为了使当前消息映射节点加入到消息映射的链表中去.
  • AFX_MSGMAP的类都保存了一张表,记录了"消息-处理函数"之间的对应关系

消息的种类

  • 命令消息: WM_COMAMDN,CCmdTarget; UI对象、菜单、工具栏、按钮
  • 控件消息: WM_NOTIFY,也属于命令消息的一种,是控件发送给父窗口的"通知消息";
  • 标准消息: WM_PAINT,WM_CREATE,WM_CHAR,WM_LBUTTONDOWN..除了上面两种之外的消息. CWnd不需要我们指定函数名!

AFX_MSGMAP

enum AfxSig {  // 消息映射响应函数参数及返回值的类型
	AfxSig_end = 0, // [marks end of message map]
	AfxSig_bD,      // BOOL (CDC*)
	AfxSig_bb,      // BOOL (BOOL)...
};
typedef void (CCmdTarget::*AFX_PMSG)(void);  // CCmdTarget类的void-void成员函数指针类型
struct AFX_MSGMAP_ENTRY {  // 记录(控件上的)消息所对应的"消息处理函数"----消息映射的节点
	UINT nMessage;  // WM_CREATE、WM_COMMAND、WM_MOVE、WM_SIZE、WM_PAINT...
	UINT nCode;     // 控件消息的控制码,如: BN_CLICKED、
	UINT nID;       // 控件ID:IDC_BUTTON1.., 0:标准消息
	UINT nLastID;   // 指定一定范围内的消息被映射,或两者一样
	UINT nSig;      // pfn函数类型.见上方AfxSig的定义
	AFX_PMSG pfn;   // 是一个指向CCmdTarget类非静态成员函数指针
};
struct AFX_MSGMAP { // 消息映射链表,便于从年轻的消息映射节点走访到最老的消息映射节点.
	const AFX_MSGMAP* pBaseMap;  // 记录基类的(baseClass)messageMap成员以便向上走访,基类消息映射表使子类可以走访到基类,类似于虚函数
	const AFX_MSGMAP_ENTRY* lpEntries;  // _messageEntries[]成员的地址
};

DECLARE_MESSAGE_MAP

下面都定义了_AFXDLL版本(动态连接到MFC-dll)

class XxDialog {
private:
	static const AFX_MSGMAP_ENTRY _messageEntries[];  // 不同的类具有不同的消息处理方式,故不能够被继承
protected:
	static const AFX_MSGMAP messageMap;
	static const AFX_MSGMAP*_GetBaseMessageMap ();
	virtual const AFX_MSGMAP* GetMessageMap() const;
	BEGIN_MESSAGE_MAP(theClass, baseClass) \  // 不一定需要直接父类
		const AFX_MSGMAP* theClass::_GetBaseMessageMap() { return &CDialog::messageMap; } \
		const AFX_MSGMAP* theClass::GetMessageMap() const { return &theClass::messageMap; } \
		const AFX_MSGMAP theClass::messageMap = { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \
		const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = {
			//#define ON_COMMAND(id, onClick)  { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&onClick }
			//#define ON_CONTROL(wNotifyCode, id, onClick)  { WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&onClick },
			//#define ON_BN_CLICKED(id, onClick)  ON_CONTROL(BN_CLICKED, id, onClick)
			{ 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 },
		}
	END_MESSAGE_MAP()
}

InitInstance

// 初始化全局对象,创建窗口,关联窗口过程
virtual BOOL InitInstance() {
	// 产生程序主框架类对象、创建窗口(CreateEx)、显示窗口(ShowWindow,DoModal);
	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) {
		/* 注册窗口类 */
		BOOL CWnd::PreCreateWindow(CREATESTRUCT&cs) {
			BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister) {
				WNDCLASS wndcls;
				wndcls.lpfnWndProc = DefWindowProc;  // MFC内部会使用钩子函数,提前改变窗口过程
				wndcls.hInstance = AfxGetInstanceHandle();
				::RegisterClass(&wndcls);  // 如果是控件,一般会调用WINAPI InitCommonControls
			}
		}

		/* 安装钩子函数,重定向窗口过程函数 */
		void AFXAPI AfxHookWindowCreate(CWnd* pWnd) {
			::SetWindowsHookEx(WH_CBT, _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
			// 重定向窗口过程
			LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam) {
				HWND hWnd = (HWND)wParam;
				SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)AfxWndProc);  // 替换原来的DefWinProc()
			}
		}

		/* 创建窗口 */
		::CreateWindowEx(...);
	}  // CreateEx、DoModal
}  // InitInstance

Run

WinMain执行InitInstance之后,运行消息循环

int CWinThread::Run() {  // CWinThread类虚函数 THRDCORE.CPP 463
	BOOL bIdle = TRUE;   // 是否空闲
	LONG lIdleCount = 0; // 空闲次数
	for (;;) { // acquire and dispatch messages until a WM_QUIT message is received.
		// phase1: check to see if we can do idle work,if so update UI
		while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)) {
			if (!OnIdle(lIdleCount++))
				bIdle = FALSE;  // assume "no idle" state
		}
		// 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)) { // WM_MOUSEMOVE,WM_NCMOUSEMOVE,WM_PAINT,0x0118
				bIdle = TRUE;
				lIdleCount = 0;
			}
		} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
	}
}
// CWinThread类虚函数
BOOL CWinThread::PumpMessage() {
	:: GetMessage(&m_msgCur, NULL, NULL, NULL);  // 从"线程消息队列中"取出消息
	if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) {
		::TranslateMessage(&m_msgCur);  // 将 WM-KEYDOWN 消息翻译成 WM_CHAR 消息,并投递到消息队列
		::DispatchMessage(&m_msgCur);   // 将消息派遣给窗口过程,注意此时的窗口过程已经被MFC给替换了
	}
}

AfxWndProc

LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) {
	CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
	pWnd->WindowProc(nMsg, wParam, lParam);
	LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {  // CWnd类虚函数
		LRESULT lResult = 0;
		if (!OnWndMsg(message, wParam, lParam, &lResult))  // 一个大的switch结构,针对不同的消息去调用相应的消息响应函数
			lResult = DefWindowProc(message, wParam, lParam);
		return lResult;
	}
	}

OnWndMsg

// 消息响应函数,先响应最年轻的
union MessageMapFunctions {
	AFX_PMSG pfn; //generic member function pointer
	// specific type safe variants for WM_COMMAND and WM_NOTIFY messages
	RESULT (CCmdTarget::*pfn_bb)(BOOL); //....
};
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) {  // CWnd类虚函数, WINCORE.CPP 1601
	if (message == WM_COMMAND) OnComamnd(){ ReflectLastMsg() + OnCmdMsg():virtual };
	if (message == WM_NOTIFY) OnNotify(){ ReflectLastMsg() + OnCmdMsg():virtual };
	.....
	/* 查找消息映射表 */
	AfxFindMessageEntry()--> :会配合汇编实现
	const AFX_MSGMAP* pMessageMap = GetMessageMap();  // 这是一个虚函数,所以下面会从最年轻的开始查找
	const AFX_MSGMAP_ENTRY* lpEntry;
	for (; pMessageMap!=NULL; pMessageMap=(*pMessageMap->pfnGetBaseMap)()) {  // 从最年轻的开始,一代一代往上找
		lpEntry = pMessageMap->lpEntries;
		while (lpEntry->nSig != AfxSig_end) {
			// 如果都相等则找到该消息对应的响应函数
			if (lpEntry->nMessage==message....D)
				goto LDispatch;
			lpEntry++;
		}
	}
LDispatch:
	/* 调用消息响应函数的地方 */
	union MessageMapFunctions mmf = lpEntry->pfn;
	switch (lpEntry->nSig) { // 根据不同的成员函数的类型,调用不同的响应函数
	case AfxSig_bD:
		lResult = (this->*mmf.pfn_bD)(CDC::FromHandle((HDC)wParam));  // this多数是其派生类对象的地址
		break;
	case AfxSig_bb : // AfxSig_bb, AfxSig_bw, AfxSig_bh
		lResult = (this->*mmf.pfn_bb)((BOOL)wParam);
		break;
	case AfxSig_vv :
		(this->*mmf.pfn_vv)();
		break;
	}  // switch
}  // OnWndMsg

VC6.0 迁移到 VS2010

#define _WIN32_WINNT 0×0601
const CXGAP = 1;  // vc9不支持默认int类型,加上 int
afx_msg UINT OnNcHitTest(CPoint point);  // 返回类型不一样, UINT --> LRESULT
BOOL OnPagerScroll(NMPGSCROLL* pNMPGScroll, LRESULT* pResult);  // NMPGSCROLL --> NMHDR
BOOL OnPagerCalcSize(NMPGCALCSIZE* pNMPGCalcSize, LRESULT* pResult);  // NMPGCALSIZE --> NMHDR
void OnToolbarDropDown(NMTOOLBAR* pnmtb, LRESULT* plr);  // NMTOOLBAR --> NMHDR