I'm using the Win32 API and I want to check if a minimized window is flashing, ie showing the window tab with orange, blinking color, through C code.
I checked the API and I only found FlashWindow/Ex, which is causing the flashing; however I want to test it against a specific window.
I also found SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT,..), which may be related, but also global, I suppose.
I'm using Windows 7
There is no Win32 API function to query if a given window is currently being flashed with FlashWindow/Ex().
However, SetWindowsHookEx() has a WH_SHELL hook, which reports HSHELL_REDRAW notifications when a window title is redrawn:
nCode [in]
Type: int
The hook code. 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. This parameter can be one of the following values.
...
HSHELL_REDRAW
6
The title of a window in the task bar has been redrawn.
wParam [in]
Type: WPARAM
This parameter depends on the value of the nCode parameter, as shown in the following table.
...
HSHELL_REDRAW
A handle to the redrawn window.
lParam [in]
Type: LPARAM
This parameter depends on the value of the nCode parameter, as shown in the following table.
...
HSHELL_REDRAW
The value is TRUE if the window is flashing, or FALSE otherwise.
So, you can use GetWindowThreadProcessId() to get the thread ID of the target window, and then pass that to the dwThreadId parameter of SetWindowsHookEx() to start monitoring all windows in the same thread as the target window, and then you can filter for HSHELL_REDRAW notifications to detect if the target window is flashing or not.
Related
I have a custom window class (that I created with RegisterClassEx()). If I create a window instance of this class and set its HMENU property the CreateWindowEx() function fails.
Why can I not set this kind of window's id/HMENU id?
// hwnd = NULL
hwnd = CreateWindowEx(0, WND_CLASS_NAME.c_str(), wndTitle.c_str(), wndFlags,
wndDimensions.left, wndDimensions.top, wndDimensions.right, wndDimensions.bottom,
NULL, (HMENU)50001, hinstance, NULL);
// hwnd is valid
hwnd = CreateWindowEx(0, WND_CLASS_NAME.c_str(), wndTitle.c_str(), wndFlags,
wndDimensions.left, wndDimensions.top, wndDimensions.right, wndDimensions.bottom,
NULL, 0, hinstance, NULL);
The whole purpose is to be able to call GetDlgCtrlId(hwnd);.
This cannot work. The CreateWindowEx function is a little bit confusing in that the interpretation of the parameters differs, depending on which type of window you are creating. You have to read the documentation carefully to avoid making erroneous assumptions.
There are two fundamental types of windows: overlapped/pop-up windows (I think there used to be a distinction back in 16-bit Windows, but that distinction is no longer relevant; these are practically identical) and child windows. The first type are the ones you intuitively think of, since they're the ones that look like windows. They're what an application uses for its main window, dialog boxes, floating tool windows, etc. The second type are a specific type of windows that can only be used as children of another window. Controls are child windows—things like buttons, static controls, listviews, etc. Child windows are hosted by a parent window (they always have a parent), which can either be another child window or an overlapped/pop-up window.
Only child windows have application-defined IDs. You specify this ID when calling the CreateWindowEx function with the WS_CHILD style flag (which requests the creation of a child window). When that flag is present, the hMenu parameter is not interpreted as a handle to a menu. Rather, it is interpreted as the child window's ID.†
When calling the CreateWindowEx function without the WS_CHILD style flag (which means you are passing either WS_OVERLAPPED or WS_POPUP, which we have already seen are essentially interchangeable), the hMenu parameter is interpreted as a handle to a menu. If it is a valid handle to a menu, then this menu is associated with the window. If it is NULL, then the window uses the class menu (the one specified when RegisterClassEx was called during creation of the window class).‡
The function's documentation tries to make this clear in the description of the hMenu parameter.
It says:
hMenu [in, optional]
Type: HMENU
A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used. For a child window, hMenu specifies the child-window identifier, an integer value used by a dialog box control to notify its parent about events. The application determines the child-window identifier; it must be unique for all child windows with the same parent window.
The implication should be obvious. A window cannot have both a menu and an ID. It has either a menu (if it is an overlapped/pop-up window) or a child-window ID (if it is a child window). Child windows never have menus—this is technically impossible because they have IDs. So what you are trying to do is impossible: overlapped/pop-up windows cannot have an ID set.
You state in the question that your intent is to be able to call the GetDlgCtrlID function, but that function's documentation is pretty clear about the fact that it works only for child windows. For starters, the very name of the function implies that it works for controls (abbreviated to "ctrl"), which by definition must be child windows. Reading further (italicized annotations are mine):
GetDlgCtrlID accepts child window handles as well as handles of controls in dialog boxes. [Technically, this is an unnecessary distinction. As we have seen, controls in dialog boxes are child windows. But, presumably, the author was taking special care to be as clear as possible.] An application sets the identifier for a child window when it creates the window by assigning the identifier value to the hmenu parameter when calling the CreateWindow or CreateWindowEx function.
Although GetDlgCtrlID may return a value if hwndCtl is a handle to a top-level window, top-level windows [by this is meant either overlapped or pop-up windows] cannot have identifiers and such a return value is never valid.
Note in particular that final sentence.
Of course, there is some way to identify overlapped and pop-up windows: their handle. This is the value returned to you by the CreateWindowEx function (assuming it is successful), and you can save this handle as the window's ID. It is guaranteed to be unique system-wide (although it may be reused for another window after your window has been destroyed), and is the most reliable way of identifying a window.
If, for some reason, you are unable to save the window handle but still need to find a top-level window later, you can call the FindWindow function. This uses the window class name and the window caption to find a matching window. If it finds a match, it returns the window handle. (Note again the distinction between top-level and child windows. FindWindow does not work on child windows. If you want to search child windows, you must call FindWindowEx instead.)
† Note that, once a child window has been created, its ID can be set or retrieved by passing the GWL_ID index to the GetWindowLongPtr or SetWindowLongPtr functions, respectively. This index only has meaning for child windows, because no other type of window has an ID.
‡ Note that, once an overlapped/pop-up window has been created, its menu handle can be retrieved with GetMenu or set with SetMenu. Again, the documentation for those functions is forced to emphasize the fact that child windows cannot have menus. GetMenu fails when called on a child window, returning an "undefined" result; SetMenu similarly fails, setting an error code.
My goal is to have a single icon for all the windows of my application.
After some reading, my understanding is that creating a tray icon is achieved through Shell_NotifyIcon(). This function gets a NOTIFYICONDATA structure which contains a hWnd field. This HWND is used by the system to notify the corresponding window of tray icon events. These events are handled by a WindowProc callback that is set on the window with SetWindowLongPtr().
Hence my questions:
How can a single icon notify all the windows of my app of say a left mouse click ?
Can I Shell_NotifyIcon() multiple times with different NOTIFYICONDATA structures, each one with a different hWnd, but with the same icon ?
What if the original window that got registered for creating the tray icon is destroyed ?
Would creating a hidden proxy window be an appropriate solution ?
Some background: my application calls the WinAPI with C (using js-ctypes), and should ideally work on all Windows versions from XP on.
You practically answered your own question in the question itself. The best thing to do is create a hidden window that survives as long as you need the tray icon to exist.
You would call Shell_NotifyIcon() only once with the hWnd referring to this hidden window, and have this window post the messages to the individual windows that need to receive them.
This also gives you the flexibility of being able to decide to skip sending messages to a particular window, or being able to send a different message to each window, depending on the requirements of your particular application.
I made a custom Splitter control in pure Windows API. It's made of 4 controls: the main container, the splitter and the 2 panes.
Now I needed to hook into the windows procedure in order to find out when one of its child controls was moving or resizing, so I used SetWindowsHookEx. I get the WM_SIZE messages in my hook procedure just fine, but no WM_MOVE messages are ever caught from my Splitter's child windows.
I tried adding a child window to a Groupbox (which I know isn't the way they're supposed to be used) just to see if the WM_MOVE messages were caught by the hook procedure, and they were.
So what am I missing here? What do I need to add to my Splitter window procedure so those WM_MOVEs get sent? Or was my error somewhere else?
PS: SetWindowPos does work on those child windows, it's just not catching WM_MOVE.
EDIT: As requested, here is the full code of the Splitter window class: http://pastebin.com/Lgvb0Vfv
Here is the part of the code that matters:
LRESULT WINAPI AnchorProc(int nCode, WPARAM wParam, LPARAM lParam) {
CWPRETSTRUCT* theMessage = (CWPRETSTRUCT*)lParam;
if (theMessage->message == WM_MOVE) printf ("!");
}
Sometime after the main window's WM_CREATE:
SetWindowsHookEx(WH_CALLWNDPROCRET,AnchorProc,NULL,GetCurrentThreadId());
// groupbox
HWND gb = CreateWindowEx(0,"button",NULL,BS_GROUPBOX|WS_CHILD,0,0,200,200,hwndMain,0,hInst,NULL);
HWND but = CreateWindowEx(0,"button",NULL,BS_PUSHBUTTON|WS_CHILD,0,0,40,40,gb,0,hInst,NULL);
// custom control
HWND split = CreateWindowEx(0,"FSplitterClass",NULL,WS_CHILD,200,0,200,200,hwndMain,0,hInst,NULL);
HWND pane1 = (HWND)SendMessage(split,WM_SPGETPANE,0,0);
HWND but1 = CreateWindowEx(0,"button",NULL,BS_PUSHBUTTON|WS_CHILD,0,0,40,40,pane1,0,hInst,NULL);
SetWindowPos(but, NULL, 1,1,0,0,SWP_NOSIZE|SWP_NOZORDER); // triggers WM_MOVE
SetWindowPos(but1, NULL, 1,1,0,0,SWP_NOSIZE|SWP_NOZORDER); // doesn't
A windows hook is overkill here. Subclassing is much more efficient.
WM_MOVE is generated only if the window procedure passes the WM_WINDOWPOSCHANGED message to DefWindowProc. If you cannot guarantee that, then you are not guaranteed a WM_MOVE message. Listen for WM_WINDOWPOSCHANGED.
In my C++ MFC application I have an ActiveX control on a form. At some point I create and show a new dialog. I don't want the user to be able to click the ActiveX control while this second dialog is up so I tried creating it as a child dialog. However the ActiveX control always appears above the child dialog in Z order. I have tried sending message on the create to change the Z order but nothing worked.
I've tried using Windows Hooks to intercept the mouse click using the following code:
GetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)CDWFDLG::ClickProc, GetModuleHandle(NULL), 0)
LRESULT CALLBACK CDWFDLG::ClickProc(int ncode, WPARAM wparam, LPARAM lparam)
{
if(wparam == WM_LBUTTONDOWN)
{
Beep(110, 30);
return TRUE;
}
return CallNextHookEx(0, ncode, wparam, lparam);
}
This blocks all left mouse clicks which is what I want. However it does this on everything, not just on my application. I've tried setting the thread Id using
GetCurrentThreadId()
and
GetWindowThreadProcessId(this->m_hWnd, &threadId )
However neither of these worked. What should I use to just get the hook to run on my application? Once this is working I was planning on using the coordinates of the click to check whether is was on the new dialog and handle it from there.
Thanks
GetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)CDWFDLG::ClickProc, GetModuleHandle(NULL), 0)
Means you are hooking globally, all mouse clicks performed.
What you want is to hook WH_MOUSE, with the option GetCurrentThreadId() instead of 0, this will yield the results you want.
Although I couldn't fix the problem using Window Hooks, I think I've fixed it using the dialog properties. I've set the parent dialog's Control Parent to True and left everything else in the child dialog's properties to default (Control is false and and Style is Popup etc).
Now when I call the dialog through DoModal() it has focus and doesn't allow clicks on the ActiveX control.
Thanks
When i open a window it first has false title(still not updated) after several seconds title updates, is there any function or a practical solution to wait for this so then i could send a message somewhere when this happens...
What i have is a cbt hook that gets wparam when window activates, i need to wait for this wparam to become real title(not the fake one)
I can put a sleep for every activation but that would slow everything down.
Provided your target window is following the standard method to update its caption, you can add a hook to catch the precise moment when the caption has changed.
To do this, you will need to add a windows message return hook with SetWindowsHookEx, passing it a CallWndRetProc handler from your code to receive notices upon completion of windows messages by the target window's message handler.
SetWindowsHookEx() # MSDN
CallWndRetProc # MSDN
The windows message you need to trap is WM_SETTEXT, which is sent to trigger the change in the default text/caption for a window.
WM_SETTEXT # MSDN
If you would like to test this in isolation, WM_SETTEXT is an implicit message sent upon calling SetWindowText to change the window text/caption.
SetWindowText() # MSDN
Also, you might also be able to get rid of your cbt hook, as WM_ACTIVATE, WM_MOUSEACTIVATE, and WM_SETFOCUS can be trapped with the same hook used to trap WM_SETTEXT. These messages cover various levels of activation activity and type for windows.
WM_ACTIVATE # MSDN
WM_MOUSEACTIVATE # MSDN
WM_SETFOCUS # MSDN
Last, if the target window is a window you create and control in your own process, you could simply intercept those windows messages instead of using hooks. If it is impossible to use your available frameworks to intercept those messages, SetClassLongPtr is also another alternative, which may be used to implement a subclass for that type of window to allow you to intercept window messages. If you use this method, you would delegate all calls to the original windows message handler, and only act during a return from the original procedure upon receiving a message of interest.
SetClassLongPtr() # MSDN
WindowProc # MSDN
Here's how I would do it in c#:
Set up a listener for the textchanged event.
this.something.TextChanged += new System.EventHandler(something_TextChanged);
Then setup the code that will do whatever you need when the text changes
void something_TextChanged(object sender, System.EventArgs e)
{
// your code
}