文章目录
程序启动流程
- 系统
shell
加载程序的code
、data
、dll
并调用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