I am having trouble getting SendMessage to activate a menu on another program. When looking through Spy++ when a user clicks on the menu the following occurs:
000A0628 P WM_NCLBUTTONDOWN nHittest:HTMENU xPos:1706 yPos:395 [wParam:00000005 lParam:018B06AA time:27:23:56.156 point:(1706, 395)]
000A0628 S WM_SYSCOMMAND uCmdType:SC_MOUSEMENU xPos:1706 yPos:395 [wParam:0000F095 lParam:018B06AA]
000A0628 S WM_ENTERMENULOOP fIsTrackPopupMenu:False [wParam:00000000 lParam:00000000]
000A0628 R WM_ENTERMENULOOP lResult:00000000
000A0628 S WM_SETCURSOR hwnd:000A0628 nHittest:HTCAPTION wMouseMsg:0000 [wParam:000A0628 lParam:00000002]
000A0628 R WM_SETCURSOR fHaltProcessing:False [lResult:00000000]
000A0628 S WM_INITMENU hmenuInit:00200355 [wParam:00200355 lParam:00000000]
000C0672 S EM_GETSEL lpdwStart:0010EFA0 lpdwEnd:0010EFA4 [wParam:0010EFA0 lParam:0010EFA4]
000C0672 R EM_GETSEL wStart:26 wEnd:26 lpdwStart:0010EFA0 (684) lpdwEnd:0010EFA4 (276) [lResult:001A001A]
000C0672 S WM_GETTEXTLENGTH wParam:00000000 lParam:00000000
000C0672 R WM_GETTEXTLENGTH cch:26 [lResult:0000001A]
000C0672 S EM_CANUNDO wParam:00000000 lParam:00000000
000C0672 R EM_CANUNDO fCanUndo:True [lResult:00000001]
000A0628 R WM_INITMENU lResult:00000000
000A0628 S WM_MENUSELECT uItem:0 fuFlags:MF_POPUP | MF_HILITE | MF_MOUSESELECT hmenu:00200355 [wParam:80900000 lParam:00200355]
000A0628 R WM_MENUSELECT lResult:00000000
000A0628 S WM_INITMENUPOPUP hmenuPopup:00150595 uPos:0 fSystemMenu:False [wParam:00150595 lParam:00000000]
000A0628 R WM_INITMENUPOPUP lResult:00000000
Whereas when I attempt the same using SendMessage with WM_NCLBUTTONDOWN the following happens instead:
000A0628 P WM_NCLBUTTONDOWN nHittest:HTMENU xPos:1700 yPos:400 [wParam:00000005 lParam:019006A4 time:27:25:02.156 point:(1700, 400)]
000A0628 S WM_SYSCOMMAND uCmdType:SC_MOUSEMENU xPos:1700 yPos:400 [wParam:0000F095 lParam:019006A4]
000A0628 S WM_ENTERMENULOOP fIsTrackPopupMenu:False [wParam:00000000 lParam:00000000]
000A0628 R WM_ENTERMENULOOP lResult:00000000
000A0628 S WM_EXITMENULOOP fIsTrackPopupMenu:False [wParam:00000000 lParam:00000000]
000A0628 R WM_EXITMENULOOP lResult:00000000
000A0628 S WM_MENUSELECT uItem:0 fuFlags:FFFF (menu was closed) hmenu:00000000 [wParam:FFFF0000 lParam:00000000]
000A0628 R WM_MENUSELECT lResult:00000000
000A0628 S WM_EXITMENULOOP fIsTrackPopupMenu:False [wParam:00000000 lParam:00000000]
000A0628 R WM_EXITMENULOOP lResult:00000000
000A0628 R WM_SYSCOMMAND lResult:00000000
Does anyone know how to get this to work properly?
Cheers
Ross
I'd guess that the menu loop tests for the state of the mouse button. However, you haven't simulated pressing the mouse button, all you're doing is sending a message saying that the button is down.
If you need synchronous behavior, I would suggest a windows hook and have your thread block until the right time. (In other words, you can use an asynchronous approach and then wrap it so that it blocks and acts as a synchronous call. It's extra work, but if you insist on synchronous behavior from something that is inherently asynchronous, it's your best solution.)
You shouldn't use SendMessage to simulate mouse clicks and key presses. Use SendInput instead.
You can perfectly use SendMessage(), in particular for menus (see MSDN and Google Groups for code samples)
(and hooks have nothing to do here)
Related
I got a WM_COMMAND in a button event by Spy++, It looks like:
<000116> 001B0A02 S WM_NOTIFY idCtrl:133978 pnmh:0019F9A0
<000117> 001B0A02 R WM_NOTIFY
<000118> 001B0A02 S WM_COMMAND wNotifyCode:0000 wID:2 hwndCtl:00020B5A
<000119> 001B0A02 R WM_COMMAND
<000120> 001B0A02 S WM_NOTIFY idCtrl:133978 pnmh:0019F9BC
<000121> 001B0A02 R WM_NOTIFY
Then I tried to redo WM_COMMAND by SendMessage:
Nothing happened. I used the AHK script to be sure, same result.
When I used SendMessage(), Spy++ got this:
<000423> 001B0A02 S WM_COMMAND wNotifyCode:0000 wID:2 hwndCtl:00000014
<000424> 001B0A02 R WM_COMMAND
I also let the lParam = 0, then it worked but not as I expected, another Menu item opened.
<000001> 001B0A02 S WM_COMMAND wNotifyCode:0 (sent from a menu) wID:2
So how can I do this?
To emulate the WM_COMMAND for a button click, you need to send the ID of your button and the BN_CLICKED notification code (combined) as the wParam argument to SendMessage(), and the handle (HWND) of the button as the lParam.
If hDlg is the handle of your dialog window, and IDC_MYBUTTON is the resource ID of your button, the call would be like this:
SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_MYBUTTON, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, IDC_MYBUTTON));
As it happens, the BN_CLICKED notification code has a value of 0, so the wParam value will be just the control ID; this appears to be 2 in your case – the value used by Windows for the "Cancel" button. The problem, in your code, seems to be an invalid window handle for the lParam argument.
I am experimenting with Suave to send a stream of data updates; I want to replace a system we have that does polling with a socket implementation.
Here's some code:
let updateStreamSocket (webSocket : WebSocket) (context: HttpContext) =
socket {
printfn "connection"
candleUpdateEvent.Publish.Add(fun d ->
(webSocket.send Binary (d |> ByteSegment) true |> Async.RunSynchronously |> ignore)
)
let mutable loop = true
while loop do
let! msg = webSocket.read()
match msg with
| (Close, _, _) ->
let emptyResponse = [||] |> ByteSegment
do! webSocket.send Close emptyResponse true
loop <- false
| _ -> ()
printfn "disconnection"
}
Since I'm testing, I just care about the Close message, but eventually I'll have to process the Text messages to handle subscriptions.
The model is that data gets processed and each batch triggers an event (through a mailbox processor to separate threads). In the socket code, I need to handle both the socket messages I receive but also these events to send the data.
How could I join this in a single loop and wait for either event?
Right now the event handler in the socket {} section will be added / removed with connection / disconnections, but it would be possible that the close get called and then an event arrives and tries to send data, etc.. while it works while testing, this is not right.
I have an MFC CWnd derived text editor and when I (accidentally) press Alt+A, WM_CHAR inserts a character '☺'.
Spy++ logs (I can't copy/paste - I had to type this):
WM_SYSKEYDOWN nVirtKey:VK_MENU cRepeat:1 ScanCode:38 fExtended:0 AltDown:1 fRepeat:0 fUp:0
WM_SYSKEYDOWN nVirtKey:VK_MENU cRepeat:1 ScanCode:38 fExtended:0 AltDown:1 fRepeat:1 fUp:0
WM_SYSKEYDOWN nVirtKey:'A' cRepeat:1 ScanCode:1 fExtended:0 AltDown:1 fRepeat:0 fUp:0
WM_SYSCHAR chCharCode:97 cRepeat:1 ScanCode:1E fExtended:0 AltDown:1 fRepeat:0 fUp:0
WM_SYSKEYUP nVirtKey:'A' cRepeat:1 ScanCode:1E fExtended:0 AltDown:1 fRepeat:1 fUp:1
WM_KEYUP nVirtKey:VK_MENU cRepeat:1 ScanCode:38 fExtended:0 AltDown:0 fRepeat:1 fUp:1
WM_CHAR chCharCode:63 cRepeat:1 ScanCode:38 fExtended:0 AltDown:1 fRepeat:1 fUp:1
My debugger says OnChar() gets nChar = 0x0000263a, nFlags = 0x0000c038, nRepCnt=1.
How can I distinguish this code from a proper code like AltGr+m = µ on a German keyboard?
Using autohotkey, I am trying to do make:
pressing and holding LCtrl & LShift behave like pressing and holding RCtrl + Left mouse button
releasing LCtrl & LShift behave like releasing RCtrl + Left mouse button
ideally the order in which the keys are pressed should not matter.
What I have at the moment is:
LCtrl & LShift::
If (A_PriorHotKey = A_ThisHotKey) ;these are built in variables
return
Send {RCtrl Down}
MouseClick, left,,, 1, 0, D ; Hold down the left mouse button.
return
LCtrl & LShift Up::
Send {RCtrl Up}
MouseClick, left,,, 1, 0, U ; Release the mouse button.
return
While pressing ^LShift does simulate pressing RCtrl & Left click, releasing ^LShift does nothing most of the time. Usually, even if I release them, RCtrl + Left click keep being "pressed" and I have to manually press them to (activate and) deactivate them.
Try this
LCtrl & LShift::
Send {RCtrl Down}
MouseClick, left,,, 1, 0, D ; Hold down the left mouse button.
KeyWait, LCtrl ; Wait for LCtrl to be released
Send {RCtrl Up}
MouseClick, left,,, 1, 0, U ; Release the mouse button.
return
EDIT:
To make it work no matter the order in which you press the keys, try this:
LCtrl & LShift::
LShift & LCtrl::
Send {Blind}{Shift Up}
Send {RCtrl Down}
MouseClick, left,,, 1, 0, D ; Hold down the left mouse button.
KeyWait, LCtrl ; Wait for LCtrl to be released
Send {RCtrl Up}
MouseClick, left,,, 1, 0, U ; Release the mouse button.
return
Below is my code to generate left click using win32 api. The problem is that it gets stuck and does not return to main. When i press Ctrl+c, then it returns to main. BUT when I call it twice, to simulate double click then it is fine. Is there anything wrong with this code?
Thank you.
void LeftClick(void)
{
INPUT Input={0};
// left down
Input.type = INPUT_MOUSE; /*The event is a mouse event. Use the mi structure of the union.*/
Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1,&Input,sizeof(INPUT));
// left up
ZeroMemory(&Input,sizeof(INPUT));
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1,&Input,sizeof(INPUT));
}
If you're not committed to using SendInput, I've had success in the past using SendMessage on the desired hWnd with WM_LBUTTONDOWN then again with WM_LBUTTONUP.
Most buttons also simulate a click with keyboard entry. You can use SendMessage to your desired hWnd with WM_KEYDOWN and wParam VK_SPACE, then WM_KEYUP with VK_SPACE to complete the space bar keypress simulation.