爱爱小说网 > 其他电子书 > windows环境下32位汇编语言程序设计 >

第13章

windows环境下32位汇编语言程序设计-第13章

小说: windows环境下32位汇编语言程序设计 字数: 每页3500字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!



等轮到程序1的时间片的时候把返回值当做SendMessage的返回值传给程序1,这样程序1看上去就像自己直接在调用程序2的窗口过程一样。

但又一个问题出现了:Windows在做“牵线红娘”的时候传递了wParam和lParam以及返回值,如果参数指向一个字符串呢,比如说上面的WM_SETTEXT消息中的lParam指向一个字符串,假设程序1中lParam指向字符串的地址为xxxxxxxx,把这个地址传给程序2的时候,程序2不可能访问到程序1的地址空间,在程序2中xxxxxxxx指向的可能是其他内容,也可能是不可访问的,这又该如何处理呢?

写一个源程序实验一下,用一个程序向另一个程序的窗口发送WM_SETTEXT消息,然后在另一个程序中将接收到的WM_SETTEXT消息的参数显示出来。先来打造接收程序,首先拷贝一份FirstWindows的代码,然后在窗口过程的分支中加上以下代码:

。elseif   eax  WM_SETTEXT

          invoke wsprintf;addr szBuffer;addr szReceive;lParam;lParam

          invoke MessageBox;hWnd;offset szBuffer;addr szCaptionMain;MB_OK

同时在数据段中加上下列定义:

szCaptionMain     db       'Receive Message';0

szReceive         db       'Receive WM_SETTEXT message';0dh;0ah

                  db       'param: %08x';0dh;0ah

                  db       'text: 〃%s〃';0dh;0ah;0

在这里,要提及Win32 API中一个很常用的函数wsprintf,这是一个字符串格式化函数,可以将数值按指定格式翻译成字符串,类似于C语言中的printf函数,它的原型是这样的:

int wsprintf(

    LPTSTR lpOut;        // 输出缓冲区地址

    LPCTSTR lpFmt;       // 格式化串地址

    。。。                  // 变量列表

   );

变量列表的数目由格式化字符串规定,wsprintf处理格式化字符串,遇到普通的字符则直接拷贝到输出,遇到%字符则代表有一个变量,%后面不同的字母表示不同的输出格式,如%d表示输出为整数,%x表示输出为16进制,%s表示输出字符串等。

%符号和表示格式的d,x和s等字母间可以用数字来指定输出时占用的位长,这时输出的位长不够时函数会用空格填齐。另外,表示位长的数字前可以加0来表示填齐时用“0”而非空格,如%08x表示输出为8位前面用0填齐的16进制数。wsprintf是Win32 API中惟一一个参数数量不定的函数,使用wsprintf函数的时候,参数的数量取决于格式化字符串中用%号指定的数量,变量列表的数目和格式化串中的%格式一定要一一对应。这里szReceive中有两个%号定义,那么后面就要额外跟两个参数:

    invoke  wsprintf;addr szBuffer;addr szReceive;lParam;lParam

这条语句将lParam的数值以及lParam的字符串按照szReceive格式化串定义的格式转换,并将结果存放到szBuffer中,然后程序将szBuffer中的内容在一个消息框中显示出来:

    invoke  MessageBox;hWnd;offset szBuffer;addr szCaptionMain;MB_OK

接收程序写好了,现在来写一个发送程序,如下所示:

               。386

               。model flat;stdcall

               option casemap:none

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

include        windows。inc

include        user32。inc

includelib     user32。lib

include        kernel32。inc

includelib     kernel32。lib

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

               。data

hWnd           dd     ?

szBuffer       db     256 dup (?)

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

               nst

szCaption      db     'SendMessage';0

szStart        db     'Press OK to start SendMessage; param: %08x!';0

szReturn       db     'SendMessage returned!';0

szDestClass    db     'MyClass';0

szText         db     'Text send to other windows';0

szNotFound     db     'Receive Message Window not found!';0

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

               de

start:

               invoke FindWindow;addr szDestClass;NULL

               。if    eax

                      mov     hWnd;eax

                      invoke  wsprintf;addr szBuffer;addr szStart;addr szText

                      invoke  MessageBox;NULL;offset szBuffer;

                              offset szCaption;MB_OK

                      invoke  SendMessage;hWnd;WM_SETTEXT;0;addr szText

                      invoke  MessageBox;NULL;offset szReturn;

                              offset szCaption;MB_OK

               。else

                      invoke  MessageBox;NULL;offset szNotFound;

                      offset  szCaption;MB_OK

               。endif

               invoke ExitProcess;NULL

;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

               end    start

在这个程序中首先用FindWindow函数找到接收窗口的窗口句柄,FindWindow函数的使用方法是:

    invoke  FindWindow;lpClassName;lpWindowName

    。if     eax

            mov hWin;eax

    。endif

两个参数都指向字符串,lpClassName指向需要寻找的窗口的窗口类,lpWindowName指向需要寻找窗口的窗口标题,如果目标窗口存在的话,函数的返回值是找到的窗口句柄,否则函数返回0。

用接收窗口的窗口类当做参数寻找窗口,如果没有找到则显示“Receive Message Window not found”,找到的话则把“Text send to other windows”字符串的地址当做WM_SETTEXT消息的参数用SendMessage发送给接收窗口。两个程序的源代码可以在所附带光盘的Chapter04SendMessage目录中找到。

好!现在发送开始,首先执行Receive。exe,窗口出来了,然后执行Send。exe,屏幕上出现一个对话框:Press OK to start SendMessage; param: 00402072,表示在Send程序中字符串的地址是00402072h,现在单击“确定”按钮执行SendMessage函数,单击后对话框消失,但接收程序显示出了一个对话框,内容为:

Receive WM_SETTEXT message

param: 0012ff1c

text: 〃Text send to other windows〃

可见字符串是正确地传了过来,但地址却不是发送程序的00402072h,这是怎么回事呢?答案是Windows做“红娘”做到底,它拷贝了WM_SETTEXT消息lParam指向的字符串,并在接收程序的地址空间中开了一块内存放上这个字符串,然后把新的地址值当做lParam传给接收程序,毕竟在WM_GETTEXT消息中,lParam的数值是多少并不重要,重要的是它指向的字符串是否正确。

最后,单击Receive 程序中的“确定”按钮,Send程序马上会弹出一个消息框并显示:SendMessage returned,这是SendMessage函数告诉我们:我回来了!

其实Windows在处理SendMessage和PostMessage的时候要检查消息的类型,并对不同的消息做不同的处理,当消息的参数是一个指针的时候,Windows要把指针指向的内容复制到缓冲区,以便在两个程序的地址空间中传递。

 在用户自定义的消息中(WM_USER等)不要在消息参数中传递指针,这只会引发非法访问内存,因为Windows不知道用户的意图,它只会把lParam和wParam当两个普通的数值传递,而不会帮用户把指针指向的内容复制到一个缓冲区中。



 
来源:电子工业出版社 作者:罗云彬 上一页         回书目         下一页          
上一页         回书目         下一页          
  


第4章 第一个窗口程序


4。4 实 验(1)

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

4。4。1  MsgWindow程序

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

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

                 nst

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这一行中的数据长度,而非包括下面全部的字符串行。为了简化处理,全部字符串的长度保持相等。由于篇幅所限,这里没有列出全部的消息列表,完整的源代码可以在本书附带光盘的Chapter04MsgWindow01目录中找到。程序在窗口过程的入口处调用_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

       

返回目录 上一页 下一页 回到顶部 2 2

你可能喜欢的