第五章 走得太远,别忘了回家的路(3) ——《箴言》第三章 Windows运行机理之读书笔记之一

三、Window Procedure 与“Reentrant”及鱼死网破
[四处蔓延、泛滥的重入性问题,鱼死网破、漏网之鱼]

其实就是在一个GUI线程内处理消息也是有点很不简单的。先就看看窗口过程的重入性吧。

先看看Petzold的说法:
[《Programming Windows》第2版(1990年)的中译版本:“”]


《Programming Windows》(1998年)第五版 第三章 Windows and Messages:“Although Windows programs can have multiple threads of execution, each thread's message queue handles messages for only the windows whose window procedures are executed in that thread. In other words, the message loop and the window procedure do not run concurrently. When a message loop retrieves a message from its message queue and calls DispatchMessage to send the message off to the window procedure, DispatchMessage does not return until the window procedure has returned control back to Windows.

However, the window procedure could call a function that sends the window procedure another message, in which case the window procedure must finish processing the second message before the function call returns, at which time the window procedure proceeds with the original message. For example, when a window procedure calls UpdateWindow, Windows calls the window procedure with a WM_PAINT message. When the window procedure finishes processing the WM_PAINT message, the UpdateWindow call will return controls back to the window procedure.

This means that window procedures must be reentrant. In most cases, this doesn't cause problems, but you should be aware of it. For example, suppose you set a static variable in the window procedure while processing a message and then you call a Windows function. Upon return from that function, can you be assured that the variable is still the same? Not necessarily—not if the particular Windows function you call generated another message and the window procedure changes the variable while processing that second message. This is one of the reasons why certain forms of compiler optimization must be turned off when compiling Windows programs.

In many cases, the window procedure must retain information it obtains in one message and use it while processing another message. This information must be saved in variables defined as static in the window procedure, or saved in global variables.
Of course, you'll get a much better feel for all of this in later chapters as the window procedures are expanded to process more messages. ”

Petzold认为“window procedures must be reentrant”。请原谅我蹩脚的英语水平,我不知道他眼下之“In most cases”与“In many cases”竟能如此和谐、彼此兼容。【《Programming Windows》第2版(1990年)的中译版本(《Windows编程》)中分别译为“多数情况下”与“在许多情况下”;《Programming Windows程序开发设计指南》第五版(2000.6),余孟学先生分别译为“在大多数情况下”与“在许多情况下”;它们大致相同。】[相对比较与数目多少][小时候与大一点的小孩争分东西时,大一点的要抢着先分,并貌似公平地说:“你要多半,我要大半——”,待他拿了大半之后实际只剩下小半了、哪里还会有我的多半呢?!——那时尽管小也还是知道的,所以这一招也早就不灵了:等我先拿了多半,你再拿你的大半!我想现在这儿时的法子或许还可一用,那就是:先拿多半,即先“In many cases……”。]在许多情况下我们都将遭遇消息之间通过静态或全局变量的通讯,要想做到窗口过程的重入性不得不谨慎从事,尤其当窗口过程被扩展来处理更多的消息时。

其实,就以MS推荐的GUI程序基本模型来说吧,如果把Windows GUI程序的主窗口/过程当作一张渔网顶部的网绳,那么由此而带出的大大小小的窗口/窗口过程就像网结一样,布满了整张网,其中有它的许许多多的经线(纲),还有它的许许多多的纬线(目),要想在这张网上做到窗口过程的重入性,绝非易事(暂不考虑Incoming nonqueue messages对SendMessage函数的“拯救”机制所带来的复杂性)!其中的危险性、复杂性、艰巨性,MS也没有“和盘托出”给我们,只是拿线程之间的SendMessage()来搪塞(却不提引入SendMessage的拯救机制所带来的后果)。而Petzold用近乎“大半”与“多半”的方式给我们一再强调线程内处理消息的难以避免、需要小心的“那些事儿”。

【局部可重入性、Weak Reentrant。路径可重入性、条件可重入性、状态可重入性。我们赖以苟延残喘的东西。(相似物 :STL 之Weakly comparable,Weak Ordering)】

平常我们并没有[常]碰到这种严重的重入性问题。一般情形下,我们只是做到了不那么严格的局部重入性,但这也就是一般情形下较低的要求。什么是局部重入性(或Weak Reentrant)?简言之,就是,一个模块在某种(执行)状态下,这种状态的条件、路线是不可重入性的,但其他的条件、路线是可重入性的,此中情形我们称此模块是局部可重入性(或Weak Reentrant)的。例如,平常的Windows GUI程序,我们由主窗口菜单进入About对话框,在About对话框显示期间,主要窗口仍能刷新(因对话框内置消息循环并路由主窗口刷新消息之故),但不会再次进入主窗口过程的显示About对话框的消息处理过程(部分)(有意或恶意下可以chk),这就是一种局部可重入性。Windows窗口系统对此是没有保(证)障的,“”。要保障这点主要是程序员的责任。Modal对话框的天然屏障,即进入一个Modal对话框后不会再象之前那样的方式进入这个Modal对话框(除非对话框自身引发、其他线程有意或恶意地引发),还有变灰禁用、层级隔离等局部化措施(状态变量控制等),这是这些常规的局部化措施所实现的局部重入性,给了我们苟延残喘的机会。当这些作为保护网的天然屏障被有意或恶意践踏之后,我们该如何【防范、】应对?网破之时鱼将为之奈何?是坐以待毙呢,还是未雨绸缪……

如果考虑到Incoming nonqueue messages对SendMessage函数的“拯救”机制的影响的话,其中的危险性、复杂性、艰巨性将与日俱增,这种机制可以扰乱正常的消息序列(MS的说辞),而且由于MS对此也无有效的识别机制、选择机制,也没有有效的抵御措施。MS不是提供了InSendMessage()函数来区分线程外与线程内发的消息么?不过,Windows的窗口系统所发的消息中有的是以线程外Incoming nonque messages来发送的,有的又以线程内消息来发送的。如以Windows XP sp2为例:

Windows系统所发消息   InSendMessage()测试结果
WM_ACTIVATE                   FALSE
WM_SETFOCUS                 FALSE
WM_SETCURSOR              FALSE


WM_GETICON                   TRUE
WM_SYNCPAINT               TRUE
WM_NCPAINT                    TRUE
WM_ERASEBKGND           TRUE
WM_CTLCOLORDLG         TRUE

[测试程序 略]
对此,MS并没有相应的有效措施。

无论如何,对我们自己可控制其格式的消息、可控制【收】发各方的消息等可控制的消息,我们可以采取措施加以防范。对这些消息格式不在自己掌握之中、又不能阻止的消息,除了一一识别【】之外,我们也还没有有效措施来识别哪是系统发的,哪是其他进程(中的线程)发的,哪是期望发送消息的线程发的,哪是恶意线程发的。即使在Windows NT系列操作系统下,由于Window对象不在Windows NT安全机制控制之下,这对本来就危险的机制无异于雪上加霜。一个以非管理员身份运行的进程可以无需安全检查就能驱使管理员身份运行的GUI进程,而你想有效防范有时还难以奈何——似乎只好让它一旁歇着了!【Windows GUI程序的脆弱性。】


四、标准的事件驱动模型与牛鼻子

发布了48 篇原创文章 · 获赞 7 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章