I am trying to make a program click a button on another program; I decided to use the function:
SendMessage(hWndVariable, MsgVariable, wParamVariable, lParamVariable)
but there is an issue. The window with the window handle hWndVariable has 3 buttons. I initially planned to use the following parameters in the SendMessage function:
SendMessage(hWndVariable, BN_CLICK, 0, 0);
Question:: but since there are 3 buttons, how would the program know which one is being clicked? lol I am guessing that I am either missing something or doing something completely wrong.
SendMessage(hWndVariable, BN_CLICK, 0, 0);
This is your problem right here. First, you're sending a nonexistent message (you meant BN_CLICKED). Second, you're not sending the message correctly.
As described in answers to your previous questions, you need to get the button's ID. You can do this using Spy++.
Then, per the BN_CLICKED documentation, you can send your message like this:
SendMessage(parentWindowhWnd, WM_COMMAND, (BN_CLICKED << 16) | BUTTONID, buttonhWnd);
Fill in the values appropriately - parentWindowhWnd is the HWND of the window containing the button, BUTTONID is the button's ID, and buttonhWnd is the HWND of the button itself.
BN_CLICKED is a notification that the button sends to its parent window. You are thinking of the BM_CLICK message instead, which you can send to a button window to simulate a click on it:
//SendMessage(hWndVariable, BM_CLICK, 0, 0);
SendMessage(hWndVariable, BM_CLICK, 0, 0);
In order for this to work, hWndVariable has to point to the specific button that you want to click on, not its parent window like you are currently doing.
Related
When you have an application hiding in the tray, you should be able to expand it. Here the problem comes in: when I holding tab key to switch focus in taskbar and try to show the window with hWnd->ShowWindow(SW_SHOW) and SetForegroundWindow(*hWnd) calls, my window is not activated. I have the ability to work with it, but it doesn't have focus for the tab key until I click it again (in the tray) without holding the tab.
The problem is that when I call ShowWindow and SetForegroundWindow I get 0 return code from both.
Sample code:
// blablabla working with messages loop
void OnShowTrayWindow(UINT /*message*/, WPARAM /*wParam*/, LPARAM /*lParam*/) {
// handle some situations
m_hWnd->ShowWindow(SW_SHOW); // returns 0 in my case
SetForegroundWindow(*m_hWnd); // returns 0 in my case
SetFocus(m_hWnd);
}
My window have WS_TABSTOP flag in styles. Actually, tab works perfectly when I have activated window.
P.S. I think that this behavior can be related with the SetForegroundWindow requirements (see https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow)
When I set the parent of a button (A) to another button (B), I found that it won't trigger the WM_COMMAND message of button A, and that there will be a WM_PARENTNOTIFY message received by the window (parent of button A). But after looking up the reference, I found no way to get the identity of button B, like HMENU or HWND. Could someone help me?
Yes, it's sure that it seems strange that somebody put a button into another button, or say set the parent of a button to another button, but it do has some realistic meaning when the parent button has a style of BS_GROUPBOX, just as the Frame in Visual Basic.
img_button_in_button
My solution to this problem is calling SetWindowSubClass after created a new control and so that the new callback function set in calling this API can receive the real hWnd of the control triggering this event.
Something to remark:
Use return DefSubclassProc(hWnd, uMsg, wParam, lParam); if that event is not to be handled by the control.
Remove WS_CHILD style for those controls calling SetWindowSubClass, or those controls can not be correctly displayed and the only thing displayed would be just an empty window.
Thanks for all who helped me in this question!
Is it normal behavior that a subclassed control does not receive WM_PAINT after you call WM_SETTEXT on it?
The parent does receive WM_CTLCOLOR, but I want to paint evertything inside my subclassed WM_PAINT message.
I assume calling InvalidateRect after calling WM_SETTEXT is the way to go?
Let me know if you want to see code. I feel like its not necessary for this question that is why I left it out initially.
Whether WM_PAINT is sent in response to WM_SETTEXT depends on what window class has been sub-classed, buttons for example are invalidated but list boxes are not (the window text for a list box is little more than a debugging aid as it is not shown in the UI).
If your class is such that setting the text should invalidate you could always add something like the following to your subclass' WindowProc:
case WM_SETTEXT: {
LRESULT res = CallWindowProc(lpfnParent, hWnd, WM_SETTEXT, wParam, lParam);
InvalidateRect(hWnd, nullptr, true);
return res;
}
That way you don't need to have an InvalidateRect each time you set the control text.
I'm working on an application to detect a pop-up dialog and then
automatically dismiss it. I'm writing this as a C++/Win32 app. The
dialog box is generated by IE 7 and I can detect the window, but
several methods to get the OK button to "click" have failed.
Doing searches for other people's solutions, sending these messages to
the button handle seems to have worked in a lot of situations:
PostMessage( handle, WM_LBUTTONDOWN, 0, 0 );
PostMessage( handle, WM_LBUTTONUP, 0, 0 );
PostMessage( handle, BM_SETSTATE, 1, 0 );
It has no effect on the button state in my tests though.
I can send tab characters to the main window and see that the OK
button gets focus, but then sending return characters does nothing.
To learn more about this I used Spy++ to get information about the
window hierarchy and what messages are delievered when I manually
click the OK button.
Looking at the message log and reading about WM_MOUSEACTIVATE seamed
to offer a solution. The log info shows that 0002166C was the button
window. So in my code I tried this:
GetClassNameA( handle, str, str_size );
if( strcmp( str, "Internet Explorer_Server" ) != 0 )
return TRUE; // Not the window we're interested in.
// Send a message to activate the button window and have it process a mouse click.
PostMessage( handle, WM_MOUSEACTIVATE, (WPARAM) dialog_handle, MAKELPARAM( HTCLIENT, WM_LBUTTONDOWN );
Based on the window hierarchy and message log, I think the window with
the class name "Internet Explorer_Server" is the button. Maybe I'm
wrong, because it does seem like an odd class name for a button...
Below is a link to the window hierarchy image, message log when I
manually click the OK button. Last is the code that's executed on a 1
second timer ticket, looking for the window.
Any insight and help is appreciated!
Image of the window hierarchy, source, window messages, and test dialog source are available here:
https://sites.google.com/site/matthewmillersmiscellanea/Home/
Ideally, you should create a DLL which exports a Global CBT Window Hook. This would allow you to get early notification when a dialog is going to be created. This would avoid the need to drain resources by constantly polling.
Once you've detected that a dialog is about to be created, you have two options:
1) Prevent the dialog creation.
I don't recommend this, it causes all sorts of problems with code that was fully expecting a valid HWND to be returned by CreateDialog();
2) Asynchronously control the dialog.
We achieved this by using PostMessage with a Registered user message and picking it up by hooking the WNDPROC. When you get this message, then you have to decide how to kill the dialog that you're in.
There are multiple ways to exit the dialog:
a) Simulate pressing OK, Cancel, Abort, No buttons using WM_COMMAND(BN_CLICKED) (as Chris comments). You can use GetDlgItem(), look for the WindowText and make your choice. However, this doesn't work for non-US-English. There may be some distance in leveraging the Accessibility API here though.
b) Simulate closing the dialog with PostMessage(WM_CLOSE, m_hWnd). This doesn't always work as expected - some dialogs have no [X] close button and their client code is expecting a specific button to be pressed instead.
c) Simulate user input using the SendInput() API. This worked around dialogs that had anti-popup-killer code in them :)
Our final solution was a rule+heuristic-based approach that had a configuration file which we could tweak when the app/IE dialogs changed their ID's, class names or parent class names.
To close continually a specific popup given that you know the window class name and window caption
#define UNICODE
#include <windows.h>
#pragma comment(lib, "user32")
int main (int nn, char ** aa)
{
while (true) {
HWND iHandle = FindWindow (L"theWindowClassName", L"theWindowCaption");
if (iHandle > 0) SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
Sleep (200); // check 5 times per second
}
return 0;
}
if one is not known or too generic (e.g. "Dialog") you can omit it by passing a null
HWND iHandle = FindWindow (L"theWindowClassName", 0);
or
HWND iHandle = FindWindow (0, L"theWindowCaption");
of course this will close all windows with the given names.
I am trying to get a GUI running for a C++ application but I am having an issue with key press events. Basically, everything works fine, as long as I do not click on any buttons (the main window registers key events), but as soon as I click on a button, the main window loses focus and it no longer captures key events. This might be a stupid question, but I am very new to C++. This is some of the code I am using:
Creation of the main window:
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Application Name", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
540, /* The programs width */
250, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
Creation of one of the buttons:
CreateWindow(TEXT("button"), TEXT("Start"),
WS_VISIBLE | WS_CHILD,
x, y, width, height,
hwnd, (HMENU) 6, NULL, NULL);
I have also noticed that whenever I click on a button, the WM_KILLFOCUS event is fired, which is why I think that this is a focus issue. I have also tried capturing the WM_KILLFOCUS event and then set the focus again with SetActiveWindow but that crashed my program.
Any help would be appreciated :)
It turned out that I was using the wrong function (SetWindowActive). Assaf Levy's answer seemed to complex for me and I thought that there might be another way around this. I managed to find the SetFocus function which gives the focus to any given window by providing it it's handle.
To make it work, what I needed to do was to, once that the the necessary code was executed within the WM_COMMAND block, I called the SetFocus function with the handle of the main window. This gave focus back to the main window and allowed it to receive events.
Note, putting the SetFocus in the WM_KILLFOCUS block will cause the buttons and any other component in it to become unresponsive to events.
This is by design. The main window is a window, but so the button is a window, and only one can have focus at any given time. If you don't want the button to "steal" the focus, add an OnFocus handler (or intercept WM_SETFOCUS) and immediately return focus to the previous window (I believe it's in the WPARAM of WM_SETFOCUS).
An easy hack would be:
hMyButton = CreateWindow("button", ...).
Define a MyButtonProc(HWND, UINT, WPARAM, LPARAM) function.
Call SetWindowLong(hMyButton, GWL_WNDPROC, (LONG)MyButtonProc). Save the value returned by this function in a g_OldButtonProc.
Inside MyButtonProc() catch WM_SETFOCUS, and call SetFocus(hMyMainWindow).
Always return CallWindowProc(h_OldButtonProc, hwnd, msg, ...) at the end of your MyButtonProc() function, unless the message was WM_SETFOCUS.
That will do the trick (tested).
The first answer was partially accurate. Subclassing the button can get "rid" of the "problem"; however handling WM_SETFOCUS be it in parent window, or subclass procedure or BN_SETFOCUS will result in unresponsive UI if you take the focsus from the button.
What you should override in the subclass procedure is WM_LBUTTONUP. Since by the time you release the mouse button you have already clicked the windows button.
Note I think this is utter rubbish for a button to be stealing focus. There should be a style like BS_NO_STEAL_FOCUS, that prevents this. As it is very cumbersome when you want another window to be handling key presses or scrolling.
/** Procedure for subclass.
This procedure is called first for the widget/control.
Unhandled or partially handled message can goes to
original procedure by calling DefSubclassProc(...).
buttonProcEx takes all four params of normal procedure, plus a
param for user defined object.
Note this is what win32 should have done in the first place.
*/
LRESULT CALLBACK buttonProcEx(HWND hwnd,uint msg,WPARAM,LPARAM,DWORD_PTR)
{
if(msg == WM_LBUTTONUP)
{
setFocus(GetParent(hwnd));
return 0; //do not allow default behaviour
}
else return DefSubclassProc(hwnd,msg,wparam,lparam);
}
//Call this after creating your button
SetWindowSubclass((HWND)button,buttonProcEx,0,NULL);
or
struct Content {...}content; //lifetime should be long enough
SetWindowSubclass((HWND)button,buttonProcEx,0,(DWORD_PTR)&content);