资料介绍
Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础
本人对Windows系统、MFC谈不上有深入的了解,但对MFC本身包装API的机制很有兴趣,特别是读了候老师的《深入浅出MFC》后,感觉到Visual C++的Application FrameWork十分精制。在以前,我对SDI结构处理消息有一定的认识,但对于模式对话框的消息机制不了解,读了《深入》一书也没能得到解决,近日,通过在网友的帮助和查阅MSDN,自认为已经了解。一时兴起,写下这些文字,没有其它目的,只是希望让后来者少走弯路,也希望和我一样 喜欢“钻牛角尖”的人共同讨论、学习。如果你是牛人,那么你现在要慎重考虑有没有充足的时间读这些幼稚文字。
正文:
Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础。如何理解?
举了例子,当你CLICK Windows “开始”BUTTON时,为什么就会弹出一个菜单呢?
当你单击鼠标左键时,操作系统中与MOUSE相关的驱动程序在第一时间内得到这个信号[LBUTTONDOWN],然后它通知操作系统―――“嗨,鼠标左键被单击了!”,操作系统得到这一信号后,马上要判断――用户单击鼠标左键,这是针对哪个窗口呢?如何判断?这很简单!当前状态中,具有焦点的窗口[或控件]就是了[这里当然是“开始”BUTTON了]。然后操作系统马上向这个窗口发送一条消息到这个窗口所在进程的消息队列,消息内容应是消息本身的代号、附加参数、窗口句柄…等等了。那么,只有操作系统才有资格发送消息至某一窗口的消息队列吗?不然,其它程序也有资格。你可以在你的程序中调用:SendMessage、PostMessage。这样,被单击的窗口得到了一条由操作系统发送的包含CLICK的消息,操作系统已经暂时不再管窗口的任何事,因为它还要忙于处理其它事务。你的程序得到一条消息后如何做呢?Windows对于你在“开始”BUTTON上的单击事件做出如下反映:弹出一菜单。可是,得到消息到做出反映这一过程是如何实现的呢?这就是本文讨论的主要内容[当然只是针对MFC了]。
我首先简要谈一下SDI,然后会花更多文字描述模式对话框。
对于SDI窗口,你的应用程序类的InitInstance()大约如下:
BOOL CEx06aApp::InitInstance()
{
……………
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CEx06aDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CEx06aView));
AddDocTemplate(pDocTemplate);
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (!ProcessShellCommand(cmdInfo))
return FALSE;
m_pMainWnd-》ShowWindow(SW_SHOW);
m_pMainWnd-》UpdateWindow();
return TRUE;
}
完成一些如动态生成相关文档、视,显示主框架窗口、处理参数行信息等工作。这些都是显示在你工程中的“明码”。我们现在把断点设置到return TRUE;一句,跟入MFC源码中,看看到底MFC内部做了什么。
程序进入SRCWinMain.cpp,下一个大动作应是:
nReturnCode = pThread-》Run();
注意了,重点来了。F11进入
int CWinApp::Run()
{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
{
// Not launched /Embedding or /Automation, but has no main window!
TRACE0(“Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application. ”);
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
再次F11进入:
int CWinThread::Run()
{
ASSERT_VALID(this);
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
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))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
BOOL CWinThread::IsIdleMessage(MSG* pMsg)
{
// Return FALSE if the message just dispatched should _not_
// cause OnIdle to be run. Messages which do not usually
// affect the state of the user interface and happen very
// often are checked for.
// redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE
if (pMsg-》message == WM_MOUSEMOVE || pMsg-》message == WM_NCMOUSEMOVE)
{
// mouse move at same position as last mouse move?
if (m_ptCursorLast == pMsg-》pt && pMsg-》message == m_nMsgLast)
return FALSE;
m_ptCursorLast = pMsg-》pt; // remember for next time
m_nMsgLast = pMsg-》message;
return TRUE;
}
// WM_PAINT and WM_SYSTIMER (caret blink)
return pMsg-》message != WM_PAINT && pMsg-》message != 0x0118;
}
这是SDI处理消息的中心机构,但请注意,它觉对不是核心!
分析一下,在无限循环FOR内部又出现一个WHILE循环
while (bIdle &&
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume “no idle” state
}
这段代码是当你程序进程的消息队列中没有消息时,会调用OnIdle做一些后备工作,临时对象在这里被删除。当然它是虚函数。其中的PeekMessage,是查看消息队列,如果有消息返回TRUE,如果没有消息返回FALSE,这里指定PM_NOREMOVE,是指查看过后不移走消息队列中刚刚被查看
到的消息,也就是说这里的PeekMessage只起到一个检测作用,显然返回FALSE时[即没有消息],才会进入循环内部,执行OnIdle,当然了,你的OnIdle返回FLASE,会让程序不再执行OnIdle。你可能要问:
当bidle=0或消息队例中有消息时,程序又执行到哪了呢?
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));
看啊,又进入一个循环!
其中有个重要的函数,PumpMessage,内容如下:
BOOL CWinThread::PumpMessage()
{
ASSERT_VALID(this);
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
{
#ifdef _DEBUG
if (afxTraceFlags & traceAppMsg)
TRACE0(“CWinThread::PumpMessage - Received WM_QUIT. ”);
m_nDisablePumpCount++; // application must die
// Note: prevents calling message loop things in ’ExitInstance’
// will never be decremented
#endif
return FALSE;
}
#ifdef _DEBUG
if (m_nDisablePumpCount != 0)
{
TRACE0(“Error: CWinThread::PumpMessage called when not permitted. ”);
ASSERT(FALSE);
}
#endif
#ifdef _DEBUG
if (afxTraceFlags & traceAppMsg)
_AfxTraceMsg(_T(“PumpMessage”), &m_msgCur);
#endif
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
如你所想,这才是MFC消息处理的核心基地[也是我个人认为的]。
GetMessage不同于PeekMessae,它是不得到消息不罢体,PeekMessage如果发现消息队列中没有消息会返回0,而GetMessage如果发现没有消息,等,直到有了消息,而且,GetMessage不同于PeekMessage,它会将消息移走[当然,PeekMessage也可以做到这点]。我想当你读了这个函数后,
你应明白PreTranslateMessage函数的用法了吧[我比较喜欢在程序中充分利用这个函数]。
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
将消息发送到窗口的处理函数[它是由窗口类指定的],之后的动作一直到你的程序做出反映的过程,你可以在《深入》一书中得到完美的解释。我们还是通过reurn
TRUE;回到CWinThread::Run()中的Do{}while;循环。然后还是对IDLE的处理,即便刚才你的ONIDLE返回了FALSE,在这里你看到,你的程序还是有机会执行它的。然后又是利用PeekMessage检测消息队列:
如果有消息[这个消息不被移动的原因是因为它要为PumpMessage内的GetMessage所利用。]再次进入PumpMessage[叫它“消息泵”吧]。
如果没有消息,退出DO循环,但它还在FOR内部,所以又执行第一个While循环。
本人对Windows系统、MFC谈不上有深入的了解,但对MFC本身包装API的机制很有兴趣,特别是读了候老师的《深入浅出MFC》后,感觉到Visual C++的Application FrameWork十分精制。在以前,我对SDI结构处理消息有一定的认识,但对于模式对话框的消息机制不了解,读了《深入》一书也没能得到解决,近日,通过在网友的帮助和查阅MSDN,自认为已经了解。一时兴起,写下这些文字,没有其它目的,只是希望让后来者少走弯路,也希望和我一样 喜欢“钻牛角尖”的人共同讨论、学习。如果你是牛人,那么你现在要慎重考虑有没有充足的时间读这些幼稚文字。
正文:
Windows程序和DOS程序的主要不同点之一是:Windows程序是以事件为驱动、消息机制为基础。如何理解?
举了例子,当你CLICK Windows “开始”BUTTON时,为什么就会弹出一个菜单呢?
当你单击鼠标左键时,操作系统中与MOUSE相关的驱动程序在第一时间内得到这个信号[LBUTTONDOWN],然后它通知操作系统―――“嗨,鼠标左键被单击了!”,操作系统得到这一信号后,马上要判断――用户单击鼠标左键,这是针对哪个窗口呢?如何判断?这很简单!当前状态中,具有焦点的窗口[或控件]就是了[这里当然是“开始”BUTTON了]。然后操作系统马上向这个窗口发送一条消息到这个窗口所在进程的消息队列,消息内容应是消息本身的代号、附加参数、窗口句柄…等等了。那么,只有操作系统才有资格发送消息至某一窗口的消息队列吗?不然,其它程序也有资格。你可以在你的程序中调用:SendMessage、PostMessage。这样,被单击的窗口得到了一条由操作系统发送的包含CLICK的消息,操作系统已经暂时不再管窗口的任何事,因为它还要忙于处理其它事务。你的程序得到一条消息后如何做呢?Windows对于你在“开始”BUTTON上的单击事件做出如下反映:弹出一菜单。可是,得到消息到做出反映这一过程是如何实现的呢?这就是本文讨论的主要内容[当然只是针对MFC了]。
我首先简要谈一下SDI,然后会花更多文字描述模式对话框。
对于SDI窗口,你的应用程序类的InitInstance()大约如下:
BOOL CEx06aApp::InitInstance()
{
……………
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CEx06aDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CEx06aView));
AddDocTemplate(pDocTemplate);
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (!ProcessShellCommand(cmdInfo))
return FALSE;
m_pMainWnd-》ShowWindow(SW_SHOW);
m_pMainWnd-》UpdateWindow();
return TRUE;
}
完成一些如动态生成相关文档、视,显示主框架窗口、处理参数行信息等工作。这些都是显示在你工程中的“明码”。我们现在把断点设置到return TRUE;一句,跟入MFC源码中,看看到底MFC内部做了什么。
程序进入SRCWinMain.cpp,下一个大动作应是:
nReturnCode = pThread-》Run();
注意了,重点来了。F11进入
int CWinApp::Run()
{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
{
// Not launched /Embedding or /Automation, but has no main window!
TRACE0(“Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application. ”);
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
再次F11进入:
int CWinThread::Run()
{
ASSERT_VALID(this);
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
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))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
BOOL CWinThread::IsIdleMessage(MSG* pMsg)
{
// Return FALSE if the message just dispatched should _not_
// cause OnIdle to be run. Messages which do not usually
// affect the state of the user interface and happen very
// often are checked for.
// redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE
if (pMsg-》message == WM_MOUSEMOVE || pMsg-》message == WM_NCMOUSEMOVE)
{
// mouse move at same position as last mouse move?
if (m_ptCursorLast == pMsg-》pt && pMsg-》message == m_nMsgLast)
return FALSE;
m_ptCursorLast = pMsg-》pt; // remember for next time
m_nMsgLast = pMsg-》message;
return TRUE;
}
// WM_PAINT and WM_SYSTIMER (caret blink)
return pMsg-》message != WM_PAINT && pMsg-》message != 0x0118;
}
这是SDI处理消息的中心机构,但请注意,它觉对不是核心!
分析一下,在无限循环FOR内部又出现一个WHILE循环
while (bIdle &&
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume “no idle” state
}
这段代码是当你程序进程的消息队列中没有消息时,会调用OnIdle做一些后备工作,临时对象在这里被删除。当然它是虚函数。其中的PeekMessage,是查看消息队列,如果有消息返回TRUE,如果没有消息返回FALSE,这里指定PM_NOREMOVE,是指查看过后不移走消息队列中刚刚被查看
到的消息,也就是说这里的PeekMessage只起到一个检测作用,显然返回FALSE时[即没有消息],才会进入循环内部,执行OnIdle,当然了,你的OnIdle返回FLASE,会让程序不再执行OnIdle。你可能要问:
当bidle=0或消息队例中有消息时,程序又执行到哪了呢?
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));
看啊,又进入一个循环!
其中有个重要的函数,PumpMessage,内容如下:
BOOL CWinThread::PumpMessage()
{
ASSERT_VALID(this);
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
{
#ifdef _DEBUG
if (afxTraceFlags & traceAppMsg)
TRACE0(“CWinThread::PumpMessage - Received WM_QUIT. ”);
m_nDisablePumpCount++; // application must die
// Note: prevents calling message loop things in ’ExitInstance’
// will never be decremented
#endif
return FALSE;
}
#ifdef _DEBUG
if (m_nDisablePumpCount != 0)
{
TRACE0(“Error: CWinThread::PumpMessage called when not permitted. ”);
ASSERT(FALSE);
}
#endif
#ifdef _DEBUG
if (afxTraceFlags & traceAppMsg)
_AfxTraceMsg(_T(“PumpMessage”), &m_msgCur);
#endif
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
如你所想,这才是MFC消息处理的核心基地[也是我个人认为的]。
GetMessage不同于PeekMessae,它是不得到消息不罢体,PeekMessage如果发现消息队列中没有消息会返回0,而GetMessage如果发现没有消息,等,直到有了消息,而且,GetMessage不同于PeekMessage,它会将消息移走[当然,PeekMessage也可以做到这点]。我想当你读了这个函数后,
你应明白PreTranslateMessage函数的用法了吧[我比较喜欢在程序中充分利用这个函数]。
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
将消息发送到窗口的处理函数[它是由窗口类指定的],之后的动作一直到你的程序做出反映的过程,你可以在《深入》一书中得到完美的解释。我们还是通过reurn
TRUE;回到CWinThread::Run()中的Do{}while;循环。然后还是对IDLE的处理,即便刚才你的ONIDLE返回了FALSE,在这里你看到,你的程序还是有机会执行它的。然后又是利用PeekMessage检测消息队列:
如果有消息[这个消息不被移动的原因是因为它要为PumpMessage内的GetMessage所利用。]再次进入PumpMessage[叫它“消息泵”吧]。
如果没有消息,退出DO循环,但它还在FOR内部,所以又执行第一个While循环。
下载该资料的人也在下载
下载该资料的人还在阅读
更多 >
- TI C64x+ DSP内核异常处理机制的应用
- 基于ARM核的嵌入式系统异常处理机制的设计
- 深度解析Asp.Net2.0中的Callback机制
- 总结:ARM的异常处理机制分析资料下载
- 解析操作系统的概念、结构和机制 8次下载
- ZigBee CC2530串口的数据接收处理机制详细资料讲解
- MFC原理与方法 8次下载
- 基于MFC编程的图像处理在核医学中的应用_付兴建 0次下载
- 基于C++的Windows CE文件IO处理技术 0次下载
- TTNT数据链收发机消息处理机制的MATLAB仿真 0次下载
- VxWorks下MPC860的中断处理机制及其应用
- iFix组态软件中基于队列的命令处理机制研究
- VxWorks下MPC860的中断处理机制及其应用
- VxWorks下MPC860的中断处理机制及其应用
- VxWorks下FMPC860的中断处理机制及其应用
- 介绍C语言中错误处理和异常处理的一些常用的方法和策略 465次阅读
- C语言中的错误处理机制解析 394次阅读
- 西门子博图:错误处理机制概览 2228次阅读
- S32K1xx的ECC错误处理机制 5601次阅读
- C++程序异常处理机制是什么 744次阅读
- ZWave中的消息队列机制是什么 656次阅读
- 一文详解FIFO处理机制 3542次阅读
- 嵌入式开发软件中回调函数的使用 2120次阅读
- 嵌入式Linux中进程调度怎样来解析 666次阅读
- PCIe扫盲—PCIe错误检测机制的详细资料概述 1.4w次阅读
- mfc程序执行流程小结,MFC程序的执行顺序 9403次阅读
- mfc多线程编程实例及代码,mfc多线程间通信介绍 1.7w次阅读
- mfc基础入门教程 6150次阅读
- mfc71.dll是什么 2301次阅读
- 微处理机控制的数字温度计电路图 2544次阅读
下载排行
本周
- 1TC358743XBG评估板参考手册
- 1.36 MB | 330次下载 | 免费
- 2开关电源基础知识
- 5.73 MB | 6次下载 | 免费
- 3100W短波放大电路图
- 0.05 MB | 4次下载 | 3 积分
- 4嵌入式linux-聊天程序设计
- 0.60 MB | 3次下载 | 免费
- 5基于FPGA的光纤通信系统的设计与实现
- 0.61 MB | 2次下载 | 免费
- 6基于FPGA的C8051F单片机开发板设计
- 0.70 MB | 2次下载 | 免费
- 751单片机窗帘控制器仿真程序
- 1.93 MB | 2次下载 | 免费
- 8基于51单片机的RGB调色灯程序仿真
- 0.86 MB | 2次下载 | 免费
本月
- 1OrCAD10.5下载OrCAD10.5中文版软件
- 0.00 MB | 234315次下载 | 免费
- 2555集成电路应用800例(新编版)
- 0.00 MB | 33564次下载 | 免费
- 3接口电路图大全
- 未知 | 30323次下载 | 免费
- 4开关电源设计实例指南
- 未知 | 21548次下载 | 免费
- 5电气工程师手册免费下载(新编第二版pdf电子书)
- 0.00 MB | 15349次下载 | 免费
- 6数字电路基础pdf(下载)
- 未知 | 13750次下载 | 免费
- 7电子制作实例集锦 下载
- 未知 | 8113次下载 | 免费
- 8《LED驱动电路设计》 温德尔著
- 0.00 MB | 6653次下载 | 免费
总榜
- 1matlab软件下载入口
- 未知 | 935054次下载 | 免费
- 2protel99se软件下载(可英文版转中文版)
- 78.1 MB | 537796次下载 | 免费
- 3MATLAB 7.1 下载 (含软件介绍)
- 未知 | 420026次下载 | 免费
- 4OrCAD10.5下载OrCAD10.5中文版软件
- 0.00 MB | 234315次下载 | 免费
- 5Altium DXP2002下载入口
- 未知 | 233046次下载 | 免费
- 6电路仿真软件multisim 10.0免费下载
- 340992 | 191185次下载 | 免费
- 7十天学会AVR单片机与C语言视频教程 下载
- 158M | 183278次下载 | 免费
- 8proe5.0野火版下载(中文版免费下载)
- 未知 | 138040次下载 | 免费
评论
查看更多