1、一个单线程的“问题程序”
现假设编写一个计数程序,程序的要求如下:
●用对话框做主界面,对话框中放置主“计数”按钮和“暂停/回复”按钮,并有一个编辑框用来显示计数结果。
●开始计数前,“暂停/恢复”按钮处于灰化状态,当开始计数后,该按钮被激活,用户按动一次这个按钮,则暂停计数,再按一次,则继续计数。
●开始计数后,“计数”按钮上的文字将被改为“停止计数”,如果按下“停止计数”按钮,程序将恢复到初始状态——第一个按钮变回“计数”按钮,同时灰化“暂停/恢复”按钮.
本程序的设计采用Win32汇编,采用调用Win32 API函数的方式实现Windows程序的功能,先设计一个资源文件(Counter.rc),在资源文件中定义了如图1所示的对话框。再编写汇编源文件,编译后与资源文件进行连接。
资源文件就是按标准的格式(与C语言的资源文件相同),所以这里主要讨论汇编源文件的设计。汇编源文件写起来似乎很容易,只要在WM_COMMAND消息中对“计数”和“暂停/恢复”按钮的动作进行处理就可以了。在“计数”按钮中,可以调用一个计数子程序不停地进行加法运算并将结果在编辑框中显示出来。为了能够随时停止计数或暂停,可以设置一个标志位,按动“停止计数”或者“暂停/恢复”按钮时设置不同的标志,计数子程序在循环中通过测试这个标志位来决定是否暂停或退出。
在汇编源程序中我们定义了一个32位的双字变量dwOption,它的第0、1和2位分别表示暂停标志、停止标志和计算中标志。在计数子程序中只需通过一定的逻辑运算测试相应的位就可以知道用户按动了什么按钮以及执行相应的动作。
这个程序很简单,其逻辑结构如下:当用户按下“计数”按钮的时候,窗口发送一个WM_COMMAND消息,WM_COMMAND消息处理代码调用_Counter子程序进行计数,子程序会将IDOK按钮(“计数”按钮)上的文字通过SetWindowText函数改为“停止计数”,并且使用EnableWindow函数激活“暂停/恢复”按钮,然后进入计数循环。
在循环中,程序通过dwOption变量中的第1位(预定义为F_STOP,停止位)来判断是否停止,通过第0位(预定义为F_PAUSE,暂停位)来决定是否暂停计数。这些标志位的状态以后会在按下“停止计数”或“暂停/恢复”按钮时在WM_COMMAND消息中设置。在程序的开头F_COUNTING、F_STOP和F_PAUSE分别被预定义为0004h、0002h和0001h,与dwOption进行与运算就可测试出停止标志或暂停标志是否被置位。
这个程序粗看起来好象很合逻辑,似乎天衣无缝。现在通过实践来检验:运行后,“计数”按钮被正确地改为“停止计数”,“暂停/恢复”按钮也正确地被激活了。但是接下来就不对了,编辑框中并没有显示计数值,更槽糕的是接下来所有的按钮都无法按动,对话框也无法移动和无法关闭,总之,程序停止了响应,现在只能通过“结束任务”来强制杀掉该进程(当该程序运行后,即成为一个进程)了。
以下是问题程序中处理WM_COMMAND消息的汇编源代码片段:
;************************************
.if eax = = WM_COMMAND
mov eax,wParam
.if ax = = IDOK
.if dwOption & F_COUNTING
or dwOption,F_STOP
.else
call _Counter
.endif
.elseif ax == IDC_PAUSE
xor dwOption,F_PAUSE
.endif
;************************************
在程序中设置了专门的计数子程序_Counter,在处理WM_COMMAND消息的代码中,设置了dwOption的相应的标志位后,就调用_Counter子程序进行计数。