Exetools

Exetools (https://forum.exetools.com/index.php)
-   General Discussion (https://forum.exetools.com/forumdisplay.php?f=2)
-   -   Does simulating click affect GetMessagePos()? (https://forum.exetools.com/showthread.php?t=16535)

BlackWhite 02-09-2015 12:13

Does simulating click affect GetMessagePos()?
 
I want to select one item of CTreeCtrl by sending message
WM_LBUTTONDOWN to it, but the CTreeCtrl's message processing
function uses API GetMessagePos() to fetch the last message's
position which is always not the one corresponding to my simulating
WM_LBUTTONDOWN. The CTreeCtrl's message processing function
further uses CTreeCtrl::HitTest() to check whether that item was clicked,
and inevitably HitTest() returns NULL.

My code is as follows:
Code:

  POINT ap={0x0027, 0x0289}, cp={0x0025, 0x00AE};
  ScreenToClient((HWND)hTreeView, &ap);
  cp = ap;
  SendMessage((HWND)hTreeView, WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16|cp.x);

The screen coordinates (0x0027, 0x0289) are absolutly correct, because
I have used SetCursorPos() to check them. And I have also tried the
following code to activate the tree item, yet it was selected but not
activated(its corresponding code was not executed):
Code:

  SendMessage((HWND)hTreeView, TVM_SELECTITEM, TVGN_CARET, hLstRetract);
So the problem lies in GetMessagePos(). I guess this API ignores any
simulating message, it only returns the position of the last physical
message say one actual click. Does anyone have an idea to defeat
GetMessagePos()? Thank you.

BlackWhite 02-10-2015 15:42

It seems that no one has encountered such a problem.
Yesterday, I resorted to patch the message processing
function so that it can accept the item selected by my
simulating message:
Code:

extern "C" __declspec(naked) void hook_tc(void)
{
  __asm
  {
      test edi, edi // EDI=hMenuItem
      jz is_it_treeview
  do_nothing:
      ret
  is_it_treeview:
      mov eax, [esi+20h] // EAX=current hWnd processing message
      cmp eax, hTreeView
      je is_it_retract
  it_is_not_retract:
      add dword ptr [esp], (0x2D57D - 0x2D4CE)
      ret
  is_it_retract:
      call is_retract_selected
      test eax, eax
      jz it_is_not_retract
      mov edi, hLstRetract        // patch EDI
      mov dword ptr [esp+0x24], 4 // patch flag=4=TVHT_ONITEMLABEL
      ret
  }
}

Though the problem is solved temporarily, but it's still a pity that
I cannot defeat that damn GetMessagePos().

bilbo 02-10-2015 21:17

Hi BlackWhite,
I found your question rather interesting so I decided to spend some time on it.

First of all, do not forget to set the focus to the tree control, before sending the message, else no selection is performed!

Next, you can have 4 cases (note that in all 4 cases the simulated selection works, in the sense that the wanted item is highlighted):

(1) send message without setting the cursor:
Code:

hTreeView.SetFocus();
::SendMessage((HWND)hTreeView, WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16 | cp.x);

GetMessagePos() KO, GetCursorPos() KO, HitTest() KO

(2) send message preceeded by set cursor:
Code:

hTreeView.SetFocus();
SetCursorPos(screen.x, screen.y);
::SendMessage((HWND)hTreeView, WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16 | cp.x);

GetMessagePos() KO, GetCursorPos() OK, HitTest() KO

(3) post message without setting the cursor
Code:

hTreeView.SetFocus();
::PostMessage((HWND)hTreeView, WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16 | cp.x);

GetMessagePos() OK, GetCursorPos() KO, HitTest() OK

(4) post message preceeded by set cursor:
Code:

hTreeView.SetFocus();
SetCursorPos(screen.x, screen.y);
::PostMessage((HWND)hTreeView, WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16 | cp.x);

GetMessagePos() OK, GetCursorPos() OK, HitTest() OK

Best regards
bilbo

BlackWhite 02-11-2015 11:08

Hi, bilbo. Thank you for your experiments.
But I found that SetFocus() did not work in my case,
GetMessagePos() still returned the wrong value.
Code:

  SetFocus((HWND)hTreeView);
  SetCursorPos(0x0027, 0x0289);
  POINT ap={0x0027, 0x0289}, cp={0x0025, 0x00AE};
  ScreenToClient((HWND)hTreeView, &ap);
  cp = ap;
  SendMessage((HWND)hTreeView, WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16|cp.x); // It does not work.
/* Whether or not the following WM_NOTIFY is sent, GetMessagePos() returns the wrong value.
  NMHDR notify;
  notify.hwndFrom = (HWND)hTreeView;
  notify.idFrom = 0x9E01;
  notify.code = 0xFFFFFFFE; // NM_CLICK; 
  SendMessage(GetParent((HWND)hTreeView), 0x4E, 0x9E01, (long)&notify);
*/

In my case, the message processing function is bound to
hTreeView, and the message being processed is WM_NOTIFY,
here is the code for processing WM_NOTIFY:
Code:

0442D470    83EC 08        sub    esp, 8
0442D473    8B4424 10      mov    eax, [esp+10]
0442D477    53              push    ebx
0442D478    55              push    ebp
0442D479    56              push    esi
0442D47A    57              push    edi
0442D47B    8BF1            mov    esi, ecx; ESI=pTreeView
0442D47D    C700 00000000  mov    dword ptr [eax], 0
0442D483    C74424 20 00000>mov    dword ptr [esp+20], 0
0442D48B    E8 D8670200    call    <jmp.&MFC4.#CWnd::GetCurrentMessage_3021>; GetCurrentMessage() further calls GetMessagePos()
;[!] GetCurrentMessage() returns a MSG pointer in EAX
;EAX->hwnd=hTreeViewFather
;EAX->message=4Eh=WM_NOTIFY
;EAX->wParam=0xE901=hTreeView's ID
;EAX->lParam=pNMHDR
;      pNMHDR->hwndFrom=hTreeView, pNMHDR->idFrom=0xE901, pNMHDR->code=0xFFFFFFFE=NM_CLICK
;EAX->pt.x = screen coordinate x  \ They are not the same as those corresponding to my simulating
;EAX->pt.y = screen coordinate y  / WM_LBUTTON message.

0442D490    8BC8            mov    ecx, eax
0442D492    8D5424 10      lea    edx, [esp+10]
0442D496    52              push    edx
0442D497    8B41 14        mov    eax, [ecx+14]; EAX=pt.x
0442D49A    8B49 18        mov    ecx, [ecx+18]; ECX=pt.y
0442D49D    894424 14      mov    [esp+14], eax
0442D4A1    8B46 20        mov    eax, [esi+20]; EAX=hTreeView
0442D4A4    894C24 18      mov    [esp+18], ecx
0442D4A8    50              push    eax
0442D4A9    FF15 F4E24604  call    [<&USER32.ScreenToClient>]                  ; user32.ScreenToClient
;[!]
;EDX->x=client coordinate x
;EDX->y=client coordinate y
0442D4AF    8B5424 14      mov    edx, [esp+14]
0442D4B3    8B4424 10      mov    eax, [esp+10]
0442D4B7    8D4C24 20      lea    ecx, [esp+20]
0442D4BB    51              push    ecx
0442D4BC    52              push    edx
0442D4BD    50              push    eax
0442D4BE    8BCE            mov    ecx, esi
0442D4C0    E8 9D670200    call    <jmp.&MFC42.#CTreeCtrl::HitTest_3914>
;[!] EAX=NULL, that's bad!
0442D4C5    8BF8            mov    edi, eax                                    ; EDI=hMenuItem
0442D4C7    85FF            test    edi, edi
0442D4C9    0F84 AE000000  je      0442D57D
......
0442D57D    5F              pop    edi                                        ; 05B0D428
0442D57E    5E              pop    esi
0442D57F    5D              pop    ebp
0442D580    5B              pop    ebx
0442D581    83C4 08        add    esp, 8
0442D584    C2 0800        retn    8

So, is there any difference between WM_LBUTTON and WM_NOTIFY?

bilbo 02-11-2015 20:35

As I showed you, GetMessagePos does not work with SendMessage, but only with PostMessage.
In fact the GetMessagePos function retrieves the cursor position for the last message retrieved by the message queue, but SendMessage does not put the message on the queue.

Best regards
bilbo

BlackWhite 02-11-2015 21:40

Quote:

Originally Posted by bilbo (Post 97582)
As I showed you, GetMessagePos does not work with SendMessage, but only with PostMessage.
In fact the GetMessagePos function retrieves the cursor position for the last message retrieved by the message queue, but SendMessage does not put the message on the queue.

Best regards
bilbo

I have tried PostMessage(), but got failure.
Will you please try intercepting WM_NOTIFY in
message processing instead of WM_LBUTTON ?
Thank you.

bilbo 02-12-2015 15:54

I now see your problem: you must not intercept the first WM_NOTIFY which arrives, with notify code NM_CLICK (0xFFFFFFFE), but the following WM_NOTIFY, with notify code TVN_SELCHANGED (0xFFFFFE6E in ascii version, 0xFFFFFE3D in Unicode version)!

Best regards
bilbo

BlackWhite 02-12-2015 22:24

Quote:

Originally Posted by bilbo (Post 97613)
I now see your problem: you must not intercept the first WM_NOTIFY which arrives, with notify code NM_CLICK (0xFFFFFFFE), but the following WM_NOTIFY, with notify code TVN_SELCHANGED (0xFFFFFE6E in ascii version, 0xFFFFFE3D in Unicode version)!

Best regards
bilbo

Yes, I wish I could intercept WM_NOTIFY with code TVN_SELCHANGED
instead of NM_CLICK, but that program was not written by myself. It
stubbornly sticks to NM_CLICK.

I have done several experiments to defy GetMessagePos(), but got
no success:

Experiment1: It did not trigger the WM_NOTIFY event processing.
And if I change hWnd to hTreeView itself instead of its father,
the result is the same.
Code:

  POINT ap={0x0027, 0x0289}, cp={0x0025, 0x00AE};
  SetFocus(GetParent((HWND)hTreeView));
  SetCursorPos(ap.x, ap.y);
  ScreenToClient(GetParent((HWND)hTreeView), &ap);
  cp = ap;
  PostMessage(GetParent((HWND)hTreeView), WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16 | cp.x);

Experiment2: It triggered the WM_NOTIFY event processing,
but GetMessagePos() got the wrong coordinates.
Code:

  POINT ap={0x0027, 0x0289}, cp={0x0025, 0x00AE};
  SetFocus(GetParent((HWND)hTreeView));
  SetCursorPos(ap.x, ap.y);
  ScreenToClient(GetParent((HWND)hTreeView), &ap);
  cp = ap;
  PostMessage(GetParent((HWND)hTreeView), WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16 | cp.x);
  NMHDR notify;
  notify.hwndFrom = (HWND)hTreeView;
  notify.idFrom = 0xE901;
  notify.code = NM_CLICK; // 0xFFFFFFFE
  SendMessage(GetParent((HWND)hTreeView), WM_NOTIFY, 0xE901, (long)&notify);

Experiment3: It did not triggered the WM_NOTIFY event processing.
Code:

  TV_ITEM ti;
  POINT ap={0x0027, 0x0289}, cp={0x0025, 0x00AE};
  ti.hItem = (HTREEITEM)hLstRetract;   
  ti.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM; 
  ti.stateMask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;
  SendMessage((HWND)hTreeView, TVM_GETITEM, 0, (long)&ti);
  NM_TREEVIEW tvnm;
  memset(&tvnm, 0, sizeof(tvnm));
  tvnm.itemNew = ti;         
  tvnm.action = TVC_BYMOUSE; // by mouse click
  tvnm.ptDrag.x = cp.x; // client
  tvnm.ptDrag.y = cp.y; // coordinates
  tvnm.hdr.hwndFrom = (HWND)hTreeView;
  tvnm.hdr.idFrom = 0xE901;
  tvnm.hdr.code = TVN_SELCHANGED;
  SendMessage(GetParent((HWND)hTreeView), WM_NOTIFY, 0xE901, (long)&tvnm);

I have traced that program by using OllyDbg & Soft-ICE to figure out
why Experiment3 failed:
Code:

73D322C3    FF75 08        push    dword ptr [ebp+8]
73D322C6    FF75 0C        push    dword ptr [ebp+C]
73D322C9    53              push    ebx
73D322CA    FF76 04        push    dword ptr [esi+4]
73D322CD    E8 DDFEFFFF    call    #AfxFindMessageEntry_1145;[!]=>*73D321AF
73D322D2    85C0            test    eax, eax; in simulating case, EAX=0
73D322D4    75 0F          jnz    short 73D322E5; in physical click case, EAX=4471860h
this_is_bad:
...
that_is_good:
73D322E5    FF75 14        push    dword ptr [ebp+14]
73D322E8    FF70 10        push    dword ptr [eax+10]; 0x26
73D322EB    FF75 10        push    dword ptr [ebp+10]
73D322EE    FF70 14        push    dword ptr [eax+14]; 0x442D470=message_processing_function_ptr
73D322F1    FF75 0C        push    dword ptr [ebp+C]
73D322F4    FF75 08        push    dword ptr [ebp+8]
73D322F7    57              push    edi
73D322F8    E8 6E000000    call    73D3236B; [!]
73D322FD  ^ EB DF          jmp    short 73D322DE
  ||
  \||/
  \/
;[!]*73D3236B
73D3236B    55              push    ebp
73D3236C    8BEC            mov    ebp, esp
73D3236E    8B45 20        mov    eax, [ebp+20]
73D32371    53              push    ebx
73D32372    33DB            xor    ebx, ebx
73D32374    43              inc    ebx
73D32375    85C0            test    eax, eax
73D32377    74 12          je      short 73D3238B
...
73D323C2    8B45 18        mov    eax, [ebp+18]
73D323C5    FF30            push    dword ptr [eax]
73D323C7    8B4D 08        mov    ecx, [ebp+8]
73D323CA    FF70 04        push    dword ptr [eax+4]
73D323CD    FF55 14        call    [ebp+14]; [!]=>*0x442D470=message_processing_function_ptr
73D323D0    E9 97000000    jmp    73D3246C
  ||
  \||/
  \/
;[!]73D321AF;#AfxFindMessageEntry_1145
73D321AF >  55              push    ebp
73D321B0    8BEC            mov    ebp, esp
73D321B2    53              push    ebx
73D321B3    8B5D 08        mov    ebx, [ebp+8]; EBX=4471848h->MessageMap
73D321B6    8B45 0C        mov    eax, [ebp+C]; EAX=EventCodePrefix=0xBC4E
73D321B9    8B55 10        mov    edx, [ebp+10]; EDX=EventCode=TVN_SELCHANGED=0xFE6E
73D321BC    8B4D 14        mov    ecx, [ebp+14]
73D321BF    837B 10 00      cmp    dword ptr [ebx+10], 0; is_it_null_message_map_item
73D321C3    74 1D          je      short 73D321E2; has reached the end of message map
73D321C5    3B03            cmp    eax, [ebx]; check EventCodePrefix
73D321C7    74 05          je      short 73D321CE
73D321C9    83C3 18        add    ebx, 18; EBX->next_message_map_item
73D321CC  ^ EB F1          jmp    short 73D321BF
73D321CE    3B53 04        cmp    edx, [ebx+4]; check EventCode
73D321D1  ^ 75 F6          jnz    short 73D321C9
73D321D3    3B4B 08        cmp    ecx, [ebx+8]
73D321D6  ^ 72 F1          jb      short 73D321C9
73D321D8    3B4B 0C        cmp    ecx, [ebx+C]
73D321DB  ^ 77 EC          ja      short 73D321C9
73D321DD    895D 08        mov    [ebp+8], ebx; save message_map_item_ptr
73D321E0    EB 05          jmp    short 73D321E7
73D321E2    33C0            xor    eax, eax
73D321E4    8945 08        mov    [ebp+8], eax
73D321E7    8B45 08        mov    eax, [ebp+8]; EAX=message_map_item_ptr
73D321EA    5B              pop    ebx        ; [EAX+14h]=message_processing_function_ptr
73D321EB    5D              pop    ebp
73D321EC    C2 1000        retn    10

;This is the message map. It contains 3 items. Every item holds 18h bytes.
04471848  64 03 00 00 00 00 00 00  00 00 00 00 00 00 00 00
04471858  0C 00 00 00 40 D4 42 04  4E BC 00 00 FE FF 00 00
04471868  00 00 00 00 00 00 00 00  26 00 00 00 70 D4 42 04

04471878  0F 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
04471888  0C 00 00 00 90 D5 42 04  00 00 00 00 00 00 00 00
04471898  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00

According to the above analysis, that program's message map does not
contain an item related to TVN_SELCHANGED.

So I have fallen into a dilemma. If I PostMessage(WM_LBUTTON), that
will not trigger WM_NOTIFY's processing; If I SendMessage(WM_NOTIFY),
that program gets the wrong coordinates.

Do you have any good idea to defeat that damn program?

BlackWhite 02-12-2015 23:31

I finally figure out why WM_LBUTTON does not trigger message processing of WM_NOTIFY. WM_NOTIFY should be simulated by posting two messages:
WM_LBUTTONDOWN
WM_LBUTTONUP
Here is the code:
Code:

  POINT ap={0x0027, 0x0289}, cp={0x0025, 0x00AE};
  SetFocus((HWND)hTreeView);
  SetCursorPos(ap.x, ap.y);
  ScreenToClient((HWND)hTreeView, &ap);
  cp = ap;
  PostMessage((HWND)hTreeView, WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16 | cp.x);
  PostMessage((HWND)hTreeView, WM_LBUTTONUP, 0, cp.y<<16 | cp.x);

If I delete SetFocus() or SetCursorPos(), the above code will not work;
If I substitute SendMessage() for PostMessage(), the above code will not work.
So, I'm much indebted to bilbo.

bilbo 02-13-2015 22:04

Nice trick, BlackWhite, to put a WM_LBUTTONUP message in the message queue too!
This way both notifications (TVN_SELCHANGED and NM_CLICK) now work!

Best regards
bilbo

mr.exodia 02-14-2015 02:54

Try this one, it simulates an actual click, in combination with SetMousePos you can click anywhere:

Code:

void LeftClick()
{
    INPUT Input = {0};
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    SendInput(1, &Input, sizeof(INPUT));
    ZeroMemory(&Input, sizeof(INPUT));
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
    SendInput(1, &Input, sizeof(INPUT));
}

Greetings


All times are GMT +8. The time now is 12:37.

Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2026, vBulletin Solutions, Inc.
Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX