luqingfei@C++

为中华之崛起而崛起!
兼听则明,偏听则暗。

Win32汇编--窗口间的消息互发

 

窗口间的消息互发

 

在不同应用程序之间的窗口中可以互发消息,方法是通过SendMessage或者PostMessage函数,它们的用法如下:

       invoke PostMessage, hWnd, Msg, wParam, lParam

       invoke SendMessage, hWnd, Msg, wParam, lParam

 

对于不同的MsgwParamlParam的含义是不同的,如对于WM_SETTEXT是:

       wParam = 0;                                             //未定义,必须为0

       lParam = (LPARAM)(LPCTSTR)lpsz;         //要设置的字符串地址

 

 

想一想就会发现一个问题:Windows中不同应用程序的地址空间是隔离的,全市程序1要用SendMessage调用程序2所属窗口的窗口过程,但程序2窗口过程的代码并不在程序1的地址空间中,那么SendMessage如何调用它呢?其实很简单,当程序1调用SendMessage函数的时候,Windows会先保存wParamlParam参数并等待,等轮到程序2的时间片的时候再去调用它的窗口过程,并把保存的wParamlParam参数发给它,等窗口过程返回的时候,Windows记下返回值并等待程序1,这样程序1看上去就像自己直接在调用程序2的窗口过程一样。

 

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

 

写一个源程序实验一下,用一个程序向另一个程序的窗口发送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 Messag’,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表示输出字符串等。

 

%符号和表示格式的dxs等字母间可以用数字来指定输出时占用的位长,这时输出的位长不够时函数会用空格填齐。另外,表示位长的数字前可以加0来表示填齐时用“0而非空格,如%08x表示输出为8位前面用0填齐的16进制数。

 

wsprintfWin32 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 (?)

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

                .const

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 no found!',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

                .code

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发送给接收窗口。

 

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

       Receive WM_SETTEXT message

       param: 0012fflc

       text: “Text send to other windows”

 

可见字符串是正确地传了过来,但地址却不是发送程序的00402072h,这是为何?

答案是Window做“红娘”做到底,它拷贝了WM_SETTEXT消息的lParam参数指向的字符串,并在接收程序的地址空间中开了一块内存放上这个字符串,然后把新的地址值当做lParam传给接收程序,毕竟在WM_SETTEXT消息中,lParam的数值是多少并不重要,重要的是它指向的字符串是否正确。

 

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

 

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

 

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

 

posted on 2010-08-18 13:04 luqingfei 阅读(897) 评论(0)  编辑 收藏 引用 所属分类: Win32汇编程语言序设计


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2010年8月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

留言簿(6)

随笔分类(109)

随笔档案(105)

Blogers

Game

Life

NodeJs

Python

Useful Webs

大牛

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜