#1
|
|||
|
|||
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); 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); 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. |
#2
|
|||
|
|||
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 } } I cannot defeat that damn GetMessagePos(). |
#3
|
|||
|
|||
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); (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); (3) post message without setting the cursor Code:
hTreeView.SetFocus(); ::PostMessage((HWND)hTreeView, WM_LBUTTONDOWN, MK_LBUTTON, cp.y<<16 | cp.x); (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); Best regards bilbo |
#4
|
|||
|
|||
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)¬ify); */ 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 Last edited by BlackWhite; 02-11-2015 at 12:04. |
#5
|
|||
|
|||
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 |
#6
|
|||
|
|||
Quote:
Will you please try intercepting WM_NOTIFY in message processing instead of WM_LBUTTON ? Thank you. |
#7
|
|||
|
|||
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 |
#8
|
|||
|
|||
Quote:
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); 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)¬ify); 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); 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 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? |
#9
|
|||
|
|||
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 substitute SendMessage() for PostMessage(), the above code will not work. So, I'm much indebted to bilbo. |
#10
|
|||
|
|||
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 |
#11
|
|||
|
|||
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)); } |
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Simulating a Button Push | lilmeanman | General Discussion | 16 | 02-18-2005 09:24 |