分享

为什么只能用SendMessage发送消息WM

 quasiceo 2014-08-12
2007-01-04 09:49 4076人阅读 评论(2) 收藏 举报

为什么只能用SendMessage发送消息WM_CopyData的分析.

Writter: SkyJacker
Date:2006-12-28
Http://blog.csdn.net/skyjacker

注:能力实在有限,最终并未得出为什么只能用SendMessage发送消息WM_CopyData。
更没有弄清SendMessage的工作原理。
文中引用的资料或观点也许并不准确,只是希望有兴趣者可以继续分析或者给予指导以解惑。谢谢!

一、先从网上搜罗以下基本知识:

资料1:WM_COPYDATA是用内存映射机制实现的。  
   简单思路如下:  
   发送进程为A,目标进程为B。  
   当A进程用SendMessage(WM_COPYDATA,wParam,lParam)进行发送的时候,它先调用  
   HANDLE   hMap   =   CreateFileMapping(0xFFFFFFFF,NULL,PAGE_READWRITE,NULL,dwSize,"MSName")  
   其中dwSize为lParam信息得到的长度(也就是COPYDATASTRUCT结构得到的信息)  
   MSName可以是MS自己定义的名字,这样就把内容复制到共享内存中。然后等待B进程处理;  
   当B进程处理WM_COPYDATA消息时,它先用OpenFileMapping()和MapViewOfFile()得到共享内存地址,
   然后再把地址值保存到lParam里面使B进程处理消息过程能处理这些数据,处理好后进行A与B再  
   用CloseHandle()等API进行清除操作就OK了。

   SkyJacker注:按照这种说法,那么进程B,如何知道映射文件名是"MSName"呢?
 还是lParam本身就表示映射文件名了?
资料2:
   SendMessage是同步函数,会等到窗口处理完才返回
   PostMessage是异步函数,只是将消息挂到消息队列中就立即返回,由于无法同步可能导致无法响应,
       一般情况下窗口消息是不会被丢弃的。
   这是因为系统必须管理用以传递数据的缓冲区的生命期,如果你使用PostMessage,数据缓冲区会在接受端有机会处理该数据之前被系统摧毁掉!
   看看候杰翻译的"多线程程序设计" 里面有详细解释,总而言之 只能用SendMessage。

   SkyJacker注:话虽然是这么说,但是异步也可以接收数据吧。socket函数connect,recv,send等也运行正常啊。
       因此,只能是SendMessage函数的设计使然,而不是因为同步或异步的原因。

资料3:请问如何在2个进程间(一个是多线程数据采集程序,一个是画面显示程序)传递大量数据?
   采集程序中存在数量不定的线程,其中每一个线程都可以采集数据,并将数据发送到显示程序进行显示。
   显示程序就是一个简单的UI程序,将接收到的采集数据显示出来。
   在一定时间段中采集的数据量比较大:每秒几条数据。每条数据大概2-3K(来自不同线程)

   原来我使用SendMessage(WM_COPYDATA,…) 在2个进程间传递数据。但最近发现一个BUG:
   有时在显示数据过程中切换UI的窗口,会引起系统deadlock! 后来查阅资料,发现用SendMessage传递数据时,
   在某些情况下的确可以引起deadlock,所以按照书中的建议用SendMessageTimeout(WM_COPYDATA,…) 进行自我保护方式数据传输。
   deadlock问题倒是解决了,但又出现数据丢失问题,原因是SendMessageTimeout经常返回timout错误 !

   我的问题是除了SendMessage(WM_COPYDATA,…) 方法,还有没有别的方法实现本文中所说的采集程序和画面显示程序间大量数据的传递?
   PostMessage不支持 WM_COPYDATA.
   SendMessageCallback, SendNotifyMessage也不支持WM_COPYDATA.

   我查过国外的很多网站,大概的结论是:

   1)WM_COPYDATA是在两个进程间传递大量数据最方便,最好使的方法.
   2)但只有SendMessage,SendMessageTimeout支持WM_COPYDATA
   3)SendMessage(WM_COPYDATA,…)内部实现是通过File mapping实现的, 系统自动完成同步工作.
   4)SendMessage是阻塞的(block), 在某些情况下会引起deadlock. 所以更安全的方法是使用SendMessageTimeout

   SkyJacker注:还是没有点明本质。

二、通过反汇编试图熟悉SendMessage过程
1、发送过程:
procudure SendAlmData(const SendText: string);
var
  DS: TCopyDataStruct;
begin
  DS.dwData := 0;
  DS.cbData := Length(SendText);   //SendText表示要发送的数据     
  DS.lpData := @SendText[1];
  SendMessage(FaceHandle, WM_COPYDATA, 0, LongWord(@DS)); //FaceHandle表示接收数据的主界面
  //PostMessage(FaceHandle, WM_COPYDATA, 0, LongWord(@DS));
  //此处必须用SendMessage,用PostMessage主界面接收不到消息
end;
2、SendMessage声明
SendMessage(FaceHandle, WM_COPYDATA, 0, LongWord(@DS)); //WM_COPYDATA = $004A;
LRESULT SendMessage(
    HWND hWnd,         // handle of destination window
    UINT Msg,         // message to send
    WPARAM wParam, // first message parameter  短参数
    LPARAM lParam  // second message parameter 长参数
   );

3、通过WM_CopyData传输数据
通过TCopyDataStruct结构存储要传输的数据.
只有DS: TCopyDataStruct一个局部变量的情况。
TCopyDataStruct结构:
  tagCOPYDATASTRUCT = packed record
    dwData: DWORD;  
    cbData: DWORD;
    lpData: Pointer;
  end;

4、Delphi编译器调试过程

Begin
004AD36C 53               push ebx //使用ebx存放要传输数据的地址
004AD36D 83C4F4           add esp,-$0c  //分配局部变量空间。TCopyDataStructk结构是12个字节大小 

004AD370 8BDA             mov ebx,edx   //edx指向要传输数据的地址(形参SendText)
004AD372 33C0             xor eax,eax   //ESP=$12F278
004AD374 890424           mov [esp],eax //dwData=0
004AD377 8BC3             mov eax,ebx   
004AD379 E85676F5FF       call @LStrLen
004AD37E 89442404         mov [esp+$04],eax //cbData := Length(Str)
004AD382 8BC3             mov eax,ebx
004AD384 E84378F5FF       call @LStrToPChar //PChar(Str)
004AD389 89442408         mov [esp+$08],eax //lpData := @Str[1] $E44EBC
004AD38D 8BC4             mov eax,esp       //eax存放TCopyDataStruct的起始地址
004AD38F 50               push eax          //stdcall方式从右向左 LPARAM=@CDS
004AD390 6A00             push $00          //WPARAM
004AD392 6A4A             push $4a          //WM_COPYDATA = $004A;
004AD394 A1F0DF4B00       mov eax,[$004bdff0] //[$004bdff0]处,存放全局变量的地址
004AD399 8B00             mov eax,[eax]     //获得全局变量 对方窗口句柄 FaceHandle
004AD39B 50               push eax
004AD39C E857A5F5FF       call SendMessage
004AD3A1 83C40C           add esp,$0c       //恢复堆栈
004AD3A4 5B               pop ebx           //恢复ebx
004AD3A5 C3               ret               //pop EIP
004AD3A6 8BC0             mov eax,eax
004AD3A8 55               push ebp
004AD3A9 8BEC             mov ebp,esp
004AD3AB 33C0             xor eax,eax
004AD3AD 55               push ebp
004AD3AE 68CDD34A00       push $004ad3cd
004AD3B3 64FF30           push dword ptr fs:[eax]
004AD3B6 648920           mov fs:[eax],esp


I=Length(Str)  //eax执行字符串的首地址
004049D4 85C0             test eax,eax   //如果字符串地址为空,则跳$3F
004049D6 7403             jz +$03
004049D8 8B40FC           mov eax,[eax-$04] //取字符串的长度
004049DB C3               ret
004049DC 85D2             test edx,edx
004049DE 743F             jz +$3f


call @LStrToPChar
00404BCC 85C0             test eax,eax  //判断是否为空
00404BCE 7402             jz +$02
00404BD0 C3               ret
00404BD1 00B8D14B4000     add [eax+$00404bd1],bh //冗余垃圾代码
00404BD7 C3               ret


call SendMessage //跳转到导入表
004078F0 FF254C054C00     jmp dword ptr [$004c054c]  //ScrollWindow
004078F6 8BC0             mov eax,eax
004078F8 FF2548054C00     jmp dword ptr [$004c0548]  //SendMessage
004078FE 8BC0             mov eax,eax
00407900 FF2544054C00     jmp dword ptr [$004c0544]  //SetActiveWindow
00407906 8BC0             mov eax,eax
00407908 FF2540054C00     jmp dword ptr [$004c0540]
......

SendMessage内部:
77D2F39A 8BFF             mov edi,edi
77D2F39C 55               push ebp
77D2F39D 8BEC             mov ebp,esp
77D2F39F 56               push esi
77D2F3A0 8B750C           mov esi,[ebp+$0c]  //首先判断消息WM_CopyData
77D2F3A3 F7C60000FEFF     test esi,$fffe0000
77D2F3A9 0F85911D0100     jnz +$00011d91
77D2F3AF 8B4D08           mov ecx,[ebp+$08]  //判断接收方窗体句柄
77D2F3B2 83F9FF           cmp ecx,-$01       
77D2F3B5 0F847BF10000     jz +$0000f17b       
77D2F3BB 81F9FFFF0000     cmp ecx,$0000ffff  
77D2F3C1 0F846FF10000     jz +$0000f16f     
77D2F3C7 E80491FEFF       call -$00016efc   //进入最终获得一个值eax= $0081DF50,
77D2F3CC 85C0             test eax,eax      //此值的获取于窗口句柄有关系.高低16位寻址操作 90586
77D2F3CE 0F845BF10000     jz +$0000f15b
77D2F3D4 6A01             push $01
77D2F3D6 FF7514           push dword ptr [ebp+$14]  //$0012F278 //DS: TCopyDataStruct的地址;
77D2F3D9 FF7510           push dword ptr [ebp+$10]  //0
77D2F3DC 56               push esi                  //$4A
77D2F3DD 50               push eax                  //$0081DF50=call -$00016efc
77D2F3DE E8F0C3FEFF       call -$00013c10
77D2F3E3 5E               pop esi
77D2F3E4 5D               pop ebp
77D2F3E5 C21000           ret $0010
77D2F3E8 81FF06010000     cmp edi,$00000106

call -$00016efc
77D184D0 64A118000000     mov eax, fs:[$00000018] //EAX=7FFDF000
77D184D6 85C9             test ecx,ecx            //函数首先检测ecx(接收方窗口句柄)
77D184D8 740C             jz +$0c
77D184DA 3B88F4060000     cmp ecx,[eax+$000006f4] //判断EAX偏移6F4位置内容(0)与ECX
77D184E0 0F84F62F0000     jz +$00002ff6
77D184E6 B201             mov dl,$01
77D184E8 EB26             jmp +$26
77D184EA 90               nop
77D184EB 90               nop
77D184EC 90               nop

jmp+$26
77D1850F 90               nop
77D18510 8BFF             mov edi,edi
77D18512 55               push ebp      //保存刚进入SendMessage位置,原先用来索引SendMessage的形参
77D18513 8BEC             mov ebp,esp
77D18515 51               push ecx      //消息接收方窗口句柄
77D18516 53               push ebx      //要发送数据的地址
77D18517 56               push esi      //消息WM_CopyData =$4A
77D18518 57               push edi      //edi=$02
77D18519 8855FC           mov [ebp-$04],dl //DL=$01
77D1851C 8BF9             mov edi,ecx   //edi=ecx =窗口句柄
77D1851E 33F6             xor esi,esi
77D18520 64A118000000     mov eax, fs:[$00000018]
77D18526 8B0D8000D777     mov ecx,[$77d70080]
77D1852C 8D98CC060000     lea ebx,[eax+$000006cc]
77D18532 8BC7             mov eax,edi
77D18534 25FFFF0000       and eax,$0000ffff  //获得窗体句柄的低16位 90586 ->0586
77D18539 3B4108           cmp eax,[ecx+$08]  //0586 - 12AA
77D1853C 734D             jnb +$4d  //不成立
77D1853E 8B0DA400D777     mov ecx,[$77d700a4] //ECX=00540000
77D18544 8D0440           lea eax,[eax+eax*2] //eax=eax+eax*2 (eax=$0586)
77D18547 8D0C81           lea ecx,[ecx+eax*4] //ecx=ecx+eax*4 (eax=$0586*3 , ecx=00540000)
77D1854A 8BC7             mov eax,edi  //eax=90586 窗口句柄
77D1854C C1E810           shr eax,$10  //右移16位,得到高16位 =9
77D1854F 663B410A         cmp ax,[ecx+$0a] //if 9= [ecx+$0a](=0009)  条件相等
77D18553 0F8577600000     jnz +$00006077   
77D18559 F6410901         test byte ptr [ecx+$09],$01 //byte ptr [ecx+$09]=$00
77D1855D 752C             jnz +$2c 
77D1855F 8A4108           mov al,[ecx+$08] //$01
77D18562 3AC2             cmp al,dl        //al=dl=$01
77D18564 0F8515560000     jnz +$00005615
77D1856A 8B4318           mov eax,[ebx+$18] //ebx=7FFDF6CC
77D1856D 85C0             test eax,eax      //EAX=00720650
77D1856F 8B31             mov esi,[ecx]
77D18571 0F8432580100     jz +$00015832
77D18577 3B30             cmp esi,[eax]   //ESI=BC73DF50
77D18579 0F822A580100     jb +$0001582a  
77D1857F 3B7004           cmp esi,[eax+$04]
77D18582 0F8321580100     jnb +$00015821
77D18588 2B731C           sub esi,[ebx+$1c]
77D1858B 85DB             test ebx,ebx
77D1858D 740D             jz +$0d
77D1858F F7431400000020   test [ebx+$14],$20000000
77D18596 0F858E5F0000     jnz +$00005f8e
77D1859C 6A01             push $01
77D1859E 57               push edi   //EDI=$90586
77D1859F E84CFFFFFF       call -$000000b4
77D185A4 85C0             test eax,eax  //判断eax,esi是否为零 eax=$01  esi=$0081DF50
77D185A6 0F84555F0000     jz +$00005f55
77D185AC 85F6             test esi,esi
77D185AE 0F844D5F0000     jz +$00005f4d
77D185B4 5F               pop edi
77D185B5 8BC6             mov eax,esi  //结果存入EAX= $0081DF50
77D185B7 5E               pop esi
77D185B8 5B               pop ebx
77D185B9 8BE5             mov esp,ebp
77D185BB 5D               pop ebp     //恢复SendMessage形参
77D185BC C3               ret
77D185BD 90               nop
77D185BE 90               nop


call -$000000b4
----------------------------------------------------
77D184F0 B848120000       mov eax,$00001248
77D184F5 BA0003FE7F       mov edx,$7ffe0300
77D184FA FF12             call dword ptr [edx]
77D184FC C20800           ret $0008
77D184FF 90               nop

call dword ptr [edx]
7C92EB8B 8BD4             mov edx,esp //EDX=12F234
7C92EB8D 0F34             sysenter
7C92EB8F 90               nop
7C92EB90 90               nop
7C92EB91 90               nop

sysenter
77D184FC C20800           ret $0008
77D184FF 90               nop
77D18500 90               nop
77D18501 90               nop
77D18502 90               nop
------------------------------------------------------

call -$00013c10
-------------------------------------------------------------------
77D1B7D3 8BFF             mov edi,edi
77D1B7D5 55               push ebp
77D1B7D6 8BEC             mov ebp,esp  //无局部变量
77D1B7D8 51               push ecx     //ECX=1
77D1B7D9 51               push ecx
77D1B7DA 53               push ebx     //$00E28684-->SendText='AL....'
77D1B7DB 56               push esi     //4A
77D1B7DC 8B7508           mov esi,[ebp+$08]  //$81DF50
77D1B7DF 8B06             mov eax,[esi]      //[$81DF50]=$090586 接收方窗口句柄
77D1B7E1 57               push edi
77D1B7E2 8B7D0C           mov edi,[ebp+$0c]
77D1B7E5 81FFE0030000     cmp edi,$000003e0
77D1B7EB 8945F8           mov [ebp-$08],eax
77D1B7EE C645FF00         mov byte ptr [ebp-$01],$00
77D1B7F2 C6450B00         mov byte ptr [ebp+$0b],$00
77D1B7F6 720C             jb +$0c
77D1B7F8 81FFE8030000     cmp edi,$000003e8
77D1B7FE 0F86AE1C0000     jbe +$00001cae
77D1B804 E84CCEFFFF       call -$000031b4   //以下没有继续分析
77D1B809 3B4608           cmp eax,[esi+$08]
77D1B80C 0F85A01C0000     jnz +$00001ca0
77D1B812 F6461604         test byte ptr [esi+$16],$04
77D1B816 0F85961C0000     jnz +$00001c96
77D1B81C 64A118000000     mov eax, fs:[$00000018]
77D1B822 8B88E4060000     mov ecx,[eax+$000006e4]
77D1B828 8B490C           mov ecx,[ecx+$0c]
77D1B82B 0B88F0060000     or ecx,[eax+$000006f0]
77D1B831 66F7C12020       test cx,$2020
77D1B836 0F85761C0000     jnz +$00001c76
77D1B83C 33C0             xor eax,eax


ida5.0.0.879 逆向User32.dll 5.1.2600.2622 Winxp SP2

.text:77D2F390 ; ---------------------------------------------------------------------------
.text:77D2F395                 align 4
.text:77D2F398                 db 2 dup(90h)
.text:77D2F39A ; Exported entry 572. SendMessageA
.text:77D2F39A
.text:77D2F39A ; *************** S U B R O U T I N E ***************************************
.text:77D2F39A
.text:77D2F39A ; Attributes: bp-based frame
.text:77D2F39A
.text:77D2F39A ; LRESULT __stdcall SendMessageA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
.text:77D2F39A                 public SendMessageA
.text:77D2F39A SendMessageA    proc near               ; CODE XREF: SendDlgItemMessageA+2Dp
.text:77D2F39A                                         ; PostMessageA+25424p
.text:77D2F39A                                         ; sub_77D1B217+2FBB8p
.text:77D2F39A                                         ; sub_77D1B217+2FBCCp
.text:77D2F39A                                         ; DATA XREF: sub_77D5BD93+54o
.text:77D2F39A                                         ; sub_77D5C506+BFo ...
.text:77D2F39A
.text:77D2F39A hWnd            = dword ptr  8
.text:77D2F39A Msg             = dword ptr  0Ch
.text:77D2F39A WideCharStr     = dword ptr  10h
.text:77D2F39A hMem            = dword ptr  14h
.text:77D2F39A
.text:77D2F39A ; FUNCTION CHUNK AT .text:77D3E52F SIZE 00000011 BYTES
.text:77D2F39A ; FUNCTION CHUNK AT .text:77D41140 SIZE 00000046 BYTES
.text:77D2F39A
.text:77D2F39A                 mov     edi, edi
.text:77D2F39C                 push    ebp
.text:77D2F39D                 mov     ebp, esp
.text:77D2F39F                 push    esi
.text:77D2F3A0                 mov     esi, [ebp+Msg]
.text:77D2F3A3                 test    esi, 0FFFE0000h
.text:77D2F3A9                 jnz     loc_77D41140
.text:77D2F3A9
.text:77D2F3AF                 mov     ecx, [ebp+hWnd]
.text:77D2F3B2                 cmp     ecx, 0FFFFFFFFh
.text:77D2F3B5                 jz      loc_77D3E536
.text:77D2F3B5
.text:77D2F3BB                 cmp     ecx, 0FFFFh
.text:77D2F3C1                 jz      loc_77D3E536
.text:77D2F3C1
.text:77D2F3C7                 call    sub_77D184D0
.text:77D2F3C7
.text:77D2F3CC                 test    eax, eax
.text:77D2F3CE                 jz      loc_77D3E52F
.text:77D2F3CE
.text:77D2F3D4                 push    1               ; int
.text:77D2F3D6                 push    [ebp+hMem]      ; hMem
.text:77D2F3D9                 push    [ebp+WideCharStr] ; WideCharStr
.text:77D2F3DC                 push    esi             ; int
.text:77D2F3DD                 push    eax             ; int
.text:77D2F3DE                 call    sub_77D1B7D3
.text:77D2F3DE
.text:77D2F3E3
.text:77D2F3E3 loc_77D2F3E3:                           ; CODE XREF: SendMessageA+F197j
.text:77D2F3E3                                         ; SendMessageA+11DD0j
.text:77D2F3E3                                         ; SendMessageA+11DE7j
.text:77D2F3E3                 pop     esi
.text:77D2F3E4                 pop     ebp
.text:77D2F3E5                 retn    10h
.text:77D2F3E5
.text:77D2F3E5 SendMessageA    endp
.text:77D2F3E5
.text:77D2F3E8 ; ---------------------------------------------------------------------------
.text:77D2F3E8 ; START OF FUNCTION CHUNK FOR sub_77D1B7D3
.text:77D2F3E8
.text:77D2F3E8 loc_77D2F3E8:                           ; CODE XREF: sub_77D1B7D3+13B70j
.text:77D2F3E8                 cmp     edi, 106h
.text:77D2F3EE                 jnb     loc_77D302C2
.text:77D2F3EE
.text:77D2F3F4                 cmp     edi, 2Fh
.text:77D2F3F7                 jz      loc_77D302C2
.text:77D2F3F7
.text:77D2F3FD                 cmp     edi, 0AFh
.text:77D2F403                 jbe     loc_77D2F373
.text:77D2F403
.text:77D2F409                 cmp     edi, 0B1h
.text:77D2F40F                 jbe     loc_77D302AE
.text:77D2F40F
.text:77D2F415                 cmp     edi, 0CCh
.text:77D2F41B                 jz      loc_77D302C2
.text:77D2F41B
.text:77D2F421                 cmp     edi, 101h
.text:77D2F427                 jbe     loc_77D2F373
.text:77D2F427
.text:77D2F42D                 jmp     loc_77D40E50
.text:77D2F42D
.text:77D2F42D ; END OF FUNCTION CHUNK FOR sub_77D1B7D3
.text:77D2F42D ; ----------------------------------------------- 

主题推荐
局部变量 全局变量 多线程 编译器 结构
猜你在找
多界面有背景程序在界面(窗体或窗体内容)切换时,闪烁问题
关于"Process32First"返回false的问题
如何使用Rebase以及bind来重定位和绑定dll
c#换肤(2005)
用python写1个简单的聊天服务器3-目录结构
在安装VC2012上再安装VC2010时连接出错解决
在使用ICSharpCode.SharpZipLib进行目录压缩后,再解压缩是提示这个错误Size mismatch: 4294967295;126976 70202;126976
多线程环境下调用SendMessage交叉发送消息可能导致的死锁问题
从信息隐藏的一个需求看C++接口与实现的分离
编译 libimobiledevice1.1.5 和 ideviceinstaller1.0.1
查看评论
2楼 混乱系 2010-05-20 01:49发表 [回复]
个人感觉最快的办法是自己建一个内存映像文件,然后发送WM_COPYDATA消息,COPYDATA结构里lpData设置为NULL,这样系统开销小些.[e04]
1楼 混乱系 2010-05-20 01:46发表 [回复]
最近调试DELPHI程序的时候发现WM_COPYDATA是用writeprocessmemory实现的,博主的结论不大一样啊..[e08]
发表评论

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约