I am trying to send some simple mouse down/up messages to Windows Calculator using SendMessage. I have been able to press the buttons by sending the messages to the buttons directly. However, I have not been able to successfully send the same messages to the main calculator window handle. Given that hWnd is the window handle to calculator, this is my code.
IntPtr fiveKey = FindWindowEx(hWnd, IntPtr.Zero, "Button", "5");
int x = 5; // X coordinate of the click
int y = 5; // Y coordinate of the click
IntPtr lParam = (IntPtr)((y << 16) | x); // The coordinates
IntPtr wParam = IntPtr.Zero; // Additional parameters for the click (e.g. Ctrl)
const uint downCode = 0x201; // Left click down code
const uint upCode = 0x202; // Left click up code
SendMessage(fiveKey, downCode, wParam, lParam); // Mouse button down
SendMessage(fiveKey, upCode, wParam, lParam); // Mouse button up
Can anyone explain to me why sending the messages to hWnd instead of fiveKey with the x/y offsets changed to the position of the "5" key does not work? I would like to eventually use this code to simulate mouse clicks on a different application that doesn't have buttons like calculator.
I'm not sure I follow you. Are you trying to send WM_LBUTTONDOWN to the main window with the coordinates of where the 5 button is, with the hopes that the 5 button will get "clicked"? If so, that's just not going to work. WM_LBUTTONDOWN is only ever sent to the window under the mouse cursor. In theory the main window could handle WM_LBUTTONDOWN and see if any of its child windows are at that location, but nobody does that because that's not how WM_LBUTTONDOWN is designed to work.
Related
I have to change the button text/size of a standard MessageBox in Windows (Win 7 actually). I'm coding in C++/CLI so the only way to show such a window is to do System::Windows::Forms::MessageBox::Show(...). I can modify the button by hooking a function to WH_CBT and getting the code HCBT_ACTIVATE...
So far so good. Now I want to resize the window itself to fit the buttons. I get the code HCBT_CREATEWND and I modify the struct CBT_CREATEWND (member cx for the width, etc.) And it works...
But it appears that the MessageBox buttons are drawn over a gray rectangle that spans the whole window width. This rectangle is not a window (using Spy++) but probably a draw done with FillRectangle() or something equivalent. And unfortunately my procedure won't allow to redraw it. In other words, if I widen the MessageBox, the gray rectangle under the buttons is not redrawn.
Here is my (oversimplified) function:
// Simplified code...
#define NEW_WIDTH 300
LRESULT CALLBACK CBTProc( int code, WPARAM wParam, LPARAM lParam )
{
HWND hwnd = (HWND)wParam;
switch ( code )
{
case HCBT_ACTIVATE:
// Change buttons here
return 0;
case HCBT_CREATEWND:
// Change window size here
LPCBT_CREATEWND cw = (LPCBT_CREATEWND)lParam;
if ( 'Main MessageBox Window, don't worry it works fine' )
{
cw.cx = NEW_WIDTH;
return 0;
}
}
}
return CallNextHookEx( hook, code, wParam, lParam );
}
So my question is quite simple: how, using the hook mechanism, can I force the MessageBox to draw the gray rectangle under the buttons correctly when it is resized by a hook function?
I'm using checkbox buttons and I want to make the button expand in width when I click it using LBUTTONDOWN
Would this involve using something like AppendMenu()? If so, how would I do it?
This is what I have so far for my button proc:
LRESULT CALLBACK ButtonWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
static int x,y,btnwidth, btnheight;
switch (message){
case WM_USER:
btnwidth=wParam;
btnheight=lParam;
return 0;
case WM_LBUTTONDOWN:
btnwidth *= 2;
break;
}
You can expand the button by calling MoveWindow. You probably will want to first call GetWindowRect to get the current size and position, and then ScreenToClient to convert the rect to client coordinates. Then you can adjust the client coordinates as you wish and pass them to MoveWindow.
I'm developing a Word addin for MS Word on Windows, and this addin has as 'advanced task pane' showing and docking on the left side of the Word document window (it's treeview(outline) showing a list of Word documents for fast editing multiple documents in a project).
My question is, the Word document window responds to mouse wheel message only when it's focused, but I want to be able to scroll the document with mouse wheel whenever the mouse cursor is hovering on it even the Word document window doesn't have a input focus.
Any hints in this particular case? Thank you!
Not quite sure it will work, but I'd try the following:
Implement a global low-level mouse hook using the SetWindowsHookEx function.
In the hook procedure, which should be called on mouse wheel scroll events, check if the window under mouse cursor is the Word document window. If so, set a shared flag indicating the needed scroll action.
Don't send WM_VSCROLL directly from the hook procedure! This procedure has to be really fast and simple.
Now, in your add-in's message loop check the flag and if it is set, send WM_VSCROLL to the Word document window.
Perhaps you could make use of the SetCapture(hWnd) Windows API function. This will cause all mouse events to go to your hWnd instead of whatever hWnd might normally expect to receive them. If you capture when the mouse enters the Word document window, and ReleaseCapture() when the mouse leaves or Word gains focus, it should work alright.
Disclaimer: I've used mouse capturing in C# before, but I've never done it in C++. I don't know if it behaves exactly the same way.
Try the following , this might help you.
1) Handle WM_MOUSEHOVER message.
2) In the handler , use SendMessage, with WM_VSCROLL as the message parameter .
Using Spy++ I saw that the window that gets the messages is of the class _Wwg (At least 2003 is) and it is responding to the WM_MOUSEWHEEL message. So you would send that window a WM_MOUSEWHELL message when you want it to scroll.
I've got the C++ code snipped below from a comment in https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
And I used it (and variations on it) successfully.
The user who wrote it claims it was inspired by a ms recommendation on a Windows Vista best practices guide, to forward the mouse wheel event to whatever window is hovered by the mouse cursor. The virtue of his/her implementation is that it's completely unintrusive, you just drop it and it set the hooks, referencing your main thread. It avoids forwarding the event to windows belonging to other processes, but perhaps that could actually be a good thing.
namespace {
LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam) {
//"if nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function
//without further processing and should return the value returned by CallNextHookEx"
if (nCode >= 0) {
MSG& msgInfo = *reinterpret_cast<MSG*>(lParam);
if (msgInfo.message == WM_MOUSEWHEEL ||
msgInfo.message == WM_MOUSEHWHEEL) {
POINT pt = {};
pt.x = ((int)(short)LOWORD(msgInfo.lParam)); //yes, there's also msgInfo.pt, but let's not take chances
pt.y = ((int)(short)HIWORD(msgInfo.lParam)); //
//visible child window directly under cursor; attention: not necessarily from our process!
//http://blogs.msdn.com/b/oldnewthing/archive/2010/12/30/10110077.aspx
if (HWND hWin = ::WindowFromPoint(pt))
if (msgInfo.hwnd != hWin && ::GetCapture() == nullptr) {
DWORD winProcessId = 0;
::GetWindowThreadProcessId(//no-fail!
hWin, //_In_ HWND hWnd,
&winProcessId); //_Out_opt_ LPDWORD lpdwProcessId
if (winProcessId == ::GetCurrentProcessId()) //no-fail!
msgInfo.hwnd = hWin; //it would be a bug to set handle from another process here
}
}
}
return ::CallNextHookEx(nullptr, nCode, wParam, lParam);
}
struct Dummy {
Dummy() {
hHook = ::SetWindowsHookEx(WH_GETMESSAGE, //__in int idHook,
mouseInputHook, //__in HOOKPROC lpfn,
nullptr, //__in HINSTANCE hMod,
::GetCurrentThreadId()); //__in DWORD dwThreadId
assert(hHook);
}
~Dummy() {
if (hHook)
::UnhookWindowsHookEx(hHook);
}
private:
HHOOK hHook;
} dummy;
}
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.
Note: code samples have been simplified, but the overall structure remains intact.
I am working on a Win32 application whose main interface is a system tray icon. I create a dummy window, using HWND_MESSAGE as its parent, to receive the icon's messages:
WNDCLASSEX wndClass;
wndClass.lpfnWndProc = &iconWindowProc;
// ...
iconWindowHandle = CreateWindow(wndClass.lpszClassName, _T(""), 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandle(NULL), 0);
Then the icon is created, referring to this message-only window:
NOTIFYICONDATA iconData;
iconData.hWnd = iconWindowHandle;
iconData.uCallbackMessage = TRAYICON_MESSAGE;
// ...
Shell_NotifyIcon(NIM_ADD, &iconData)
When the tray icon is double-clicked, I create and show a property sheet (from comctl32.dll):
LRESULT CALLBACK iconWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case TRAYICON_MESSAGE:
switch (lParam) { // that contains the "real" message
case WM_LBUTTONDBLCLK:
showPropertySheet();
return 0;
// ...
}
break;
// ...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
The property sheet has no parent window. The PropertySheet function is called from the window procedure of the message-only window. The PSH_MODELESS flag is not set; thus, PropertySheet only returns after the property sheet window is closed again:
void showPropertySheet() {
PROPSHEETPAGE pages[NUM_PAGES];
pages[0].pfnDlgProc = &firstPageDialogProc;
// ...
PROPSHEETHEADER header;
header.hwndParent = NULL;
header.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
header.ppsp = pages;
// ...
PropertySheet(&header);
}
Now all this works just fine, until I set a breakpoint inside the dialog procedure of one of the property sheet's pages:
BOOL CALLBACK firstPageDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return FALSE; // breakpoint here
}
When the program stops on the breakpoint, the entire taskbar locks up!
The call stack is quite useless; it shows that the dialog procedure is called from somewhere inside comctl32.dll, via some calls inside user32.dll. No window procedure of my own is in between.
Making the property sheet modeless doesn't seem to help. Also, I'd rather not do this because it makes the code more complex.
As long as my dialog procedure returns quickly enough, this shouldn't be a problem. But it seems so weird that a longer operation inside the dialog procedure would not only lock up the dialog itself, but the entire shell. I can imagine that the message-only window procedure has the power to cause this behaviour, since it's more closely related to the tray icon... but this function is not shown on the call stack.
Am I doing something fundamentally wrong? Can anyone shed some light on this issue?
Actually, it's rather obvious, and the confusion must have been due to a lack of coffee.
The taskbar probably uses SendMessage to send the message to my application, which causes it to block until the message is handled. SendMessageTimeout is apparently not used.
I still think it's strange that no function of my own shows up on the call stack. Surely, such a message must flow through my message loop in order to be processed? Maybe the warning that "stack frames below this line may be incomplete or missing" was actually right, then.