WIN32汇编语言教程:第04章 第一个窗口程序 · 4.4 实验(1)

在这一节中,将通过不同的实验,进一步介绍窗口的运行。首先构造一个程序,在程序中将收到的消息查表翻译成文本以“WM_XXX”格式显示出来,并且将调用各个API函数的过程也显示出来,这样可以分析窗口的各种行为和消息之间的关系。

4.4.1 MsgWindow程序

为了把需要的内容显示出来,可以选择在客户区显示文本,但这样会引入新的消息,干扰实验,所以,这里选择了一种新的方法,就是先打开Windows附件中自带的Notepad记事本程序,然后在程序中将要显示的内容通过SendMessage发送到记事本中,可以通过查看记事本中的内容来了解MsgWindow的运行情况。

同样,先拷贝一份FirstWindow程序进行修改,共增加3个部分。第一部分是将消息查表转换为字符串,首先在 .const段中增加两个表:16进制的消息编号列表dwMsgTable和字符串列表szStringTable,两表中的项目一一对应,代码如下:

                .const
dwMsgTable      dd      WM_NULL
                dd      WM_CREATE
                dd      WM_DESTROY
                dd      WM_MOVE
                ...
                dd      WM_EXITSIZEMOVE
MSG_TABLE_LEN   equ     ($ - dwMsgTable)/sizeof dword
 
MSG_STRING_LEN  equ     sizeof szStringTable
szStringTable   db      'WM_NULL               ',0
                         db'WM_CREATE           ',0
                db      'WM_DESTROY            ',0
                db      'WM_MOVE               ',0
                ...
                db      'WM_EXITSIZEMOVE       ',0
szFormat        db      'WndProc: [%04x]%s %08x %08x',0dh,0

MSG_TABLE_LEN定义了表的项数,MSG_STRING_LEN定义了字符串表中每一项的长度。sizeof操作符取的是szStringTable这一行中的数据长度,而非包括下面全部的字符串行。为了简化处理,全部字符串的长度保持相等。由于篇幅所限,这里没有列出全部的消息列表,完整的源代码可以在本书附带光盘的Chapter04\MsgWindow01目录中找到。程序在窗口过程的入口处调用_ShowMessage子程序来翻译消息并传给记事本:

_ProcWinMain        proc   uses ebx edi esi,hWnd,uMsg,wParam,lParam
 
                    invoke _ShowMessage,uMsg,wParam,lParam
                    mov    eax,uMsg
                    .if    eax == WM_XXX
                    ...
_ShowMessage子程序用来将消息查表翻译成字符串,源程序如下:
_ShowMessage        proc      _uMsg,_wParam,_lParam
                    local  @szBuffer[128]:byte
 
                    pushad
;********************************************************************
; 查找消息的说明字符串
;********************************************************************
                    mov       eax,_uMsg
                    mov       edi,offset dwMsgTable
                    mov       ecx,MSG_TABLE_LEN
                    cld
                    repnz  scasd
                    .if       ZERO?
                           sub    edi,offset dwMsgTable + sizeof dword
                           shr    edi,2
                           mov    eax,edi
                           mov    ecx,MSG_STRING_LEN
                           mul    ecx
                           add    eax,offset szStringTable
;********************************************************************
; 翻译格式并发送到 Notepad 窗口
;********************************************************************
                           invoke wsprintf,addr @szBuffer,addr szFormat,\
                                   _uMsg,eax,_wParam,_lParam
                           invoke _SendtoNotepad,addr @szBuffer
                    .endif
                    popad
                    ret
 
_ShowMessage        endp

在这里要用到repnz scasd指令,scasd指令是把eax中的值从[edi]开始的内存中按双字比较,同时将edi加4,如果相等,则ZR标志置位,否则为NZ,repnz表示如果标志为NZ,则以ecx为重复次数重复搜索,直到相等或ecx为零为止。

将ecx赋值为消息表的项数MSG_TABLE_LEN,将edi赋值为消息表的开始地址offset dwMsgTable,然后开始查找,停止后可以查看标志Zero位,如果是非ZERO,表示查完全部都没有找到,如果是ZERO,则表示找到表项。

当标志为ZERO时,edi指向找到项目的后一项,将edi减去一项的长度(sizeof dword)以及表的基址,再除以表项的长度(sizeof dword等于4,除以4等于右移两位,所以程序中用shr edi,2),就是消息在表中的索引了,接下来算出消息字符串的位置,位置等于:索引×字符串长+字符串表基址,代码如下:

   mov    ecx,MSG_STRING_LEN
   mul    ecx
   add    eax,offset szStringTable

这样,eax中就是字符串的地址了。最后将消息编号、名称和参数用wsprintf函数格式化成可以发送的字符串存放到@szBuffer中,并用_SendtoNotepad子程序将@szBuffer中的内容发送到记事本去。

程序增加的第二部分就是下面这个_SendtoNotepad子程序:

szDestClass     db       'Notepad',0
_SendtoNotepad  proc     _lpsz
                local    @hWinNotepad
 
                pushad
                invoke   FindWindow,addr szDestClass,NULL
                .if      eax
                          mov     ecx,eax
                          invoke  ChildWindowFromPoint,ecx,20,20
                .endif
                .if      eax
                          mov     @hWinNotepad,eax
                          mov     esi,_lpsz
                          @@:
                          lodsb
                          or      al,al
                          jz      @F
                          movzx   eax,al
                          invoke  PostMessage,@hWinNotepad,WM_CHAR,eax,1
                          jmp     @B
                          @@:
                .endif
                popad
                ret
 
_SendtoNotepad  endp

上页:第04章 第一个窗口程序 · 4.3 窗口间的消息互发 下页:第04章 第一个窗口程序 · 4.4 实验(2)

第04章 第一个窗口程序

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号