I'm working on a 3D editor app using Direct3D and WinAPI. I create a main window, which has a child window that takes up a portion of the main window's client area. This is used by D3D as the render target. I then also create a separate window with CreateWindow, which is built the same way as the main window (i.e a "main window" and an internal child used as a render target), and I make this window a child of the main application window (to ensure that they are minimized/restored/closed together).
The D3D rendering is executed by the render target child windows processing their WM_PAINT messages. To reduce unnecessary overhead, I set the window procedures to only render on WM_PAINT if GetForegroundWindow and GetFocus match the respective window handles. In other words, I only want a window's rendering to be refreshed if it's on top and is focused.
This is my main message loop:
HWND mainWnd;
HWND mainRenderWnd;
HWND childWnd;
HWND childRenderWnd;
// ...
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (!TranslateAccelerator(mainWnd, accel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// Run non-UI code...
}
}
When the main window gets WM_SETFOCUS, I have it set the focus to its render target child window, since I'll want to process inputs there (e.g camera controls):
// ...
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
//...
case WM_SETFOCUS:
{
SetFocus(mainRenderWnd);
return 0;
}
//...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
I do the same in the window procedure for childWnd, setting the focus to childRenderWnd. This window is opened and closed by the user, i.e at any one time it may or may not exist, but when it does and is not minimized, it needs to be the foreground window with focus. Also, to control the framerate for the child window, I use a Timer to refresh it:
static constexpr UINT_PTR RENDER_TIMER_ID = (UINT_PTR)0x200;
void TimerCallback(HWND Arg1, UINT Arg2, UINT_PTR Arg3, DWORD Arg4)
{
if (IsIconic(childWnd) || !(GetFocus() == childRenderWnd))
return;
// Invalidate the render area to make sure it gets redrawn
InvalidateRect(childRenderWnd, nullptr, false);
}
// ...
SetTimer(childWnd, RENDER_TIMER_ID, 16, (TIMERPROC)TimerCallback);
With all this set up, mainWnd and mainRenderWnd seem to work just fine. However, childRenderWnd refuses to have anything rendered to it when it is in the foreground and in focus. While debugging, I found that while this is the case, the timer callback never gets executed, nor does a WM_TIMER message get dispatched to the child window.
On the other hand, the moment I deliberately move focus out of the child window and onto the main window (while keeping both open), the timer message gets sent, and the callback is executed. Another problem is that when I minimize the app while both windows are open, and then restore them both, the render target of neither of the windows is refreshed. Instead, it seems like the focus got "flipped", as I have to click on my child window first, then my main window, and that makes it refresh properly (while the child still refuses to render anything).
What am I missing? I searched for others having problem, e.g an incorrect message pump setup blocking WM_TIMER, but nothing seems to explain what's going on here.
Thanks in advance for the help!
IInspectable and Raymond Chen's comments have helped lead me to the answer. I tried to reproduce the error in a minimal app and finally came upon the source of my trouble. Originally, the main window would just call InvalidateRect in the message loop if there were no messages to be dispatched, effectively redrawing itself every chance it got. This originally did not seem to cause any harm, the scene was rendered just fine, and the window responded to inputs. Once I introduced a second window, however, it must have flooded the message loop with paint messages, making it impossible for any timer messages to get through.
The solution, quite simply, was to give a timer to both windows to set the rate at which they would refresh their D3D render targets. Coupled with focus checks, I can now easily alternate between both windows, with only one refreshing itself at any given time.
The question remains whether the WinAPI timer system is the best choice. My bad code aside, people have mentioned that the timer's messages are low-priority, which might make it a poor choice for framerate control in the long run.
EDIT: I ended up going with IInspectable's suggestion to use a while loop within the main message loop to process to dispatch all messages, and once those are processed, I perform a frame update. This allows me to consistently update all windows and control the frame rate without risking the issue of window messages being stuck.
Related
I need to be able to determine which window the message is intended for, but I don’t understand how to do it correctly. In WH_MOUSE has a special structure (MOUSEHOOKSTRUCT) that stores the hwnd of the window, but where to get the hwnd in WH_KEYBOARD?
LRESULT CALLBACK messageHandler(int nCode, WPARAM wParam, LPARAM lParam)
{
// ???
}
DWORD WINAPI messageDispatcher(LPVOID thread)
{
hookHandle = SetWindowsHookEx(WH_KEYBOARD, messageHandler, GetModuleHandle(nullptr), *reinterpret_cast<DWORD*>(thread));
if (!hookHandle)
{
return GetLastError();
}
MSG message{};
while (GetMessage(&message, 0, 0, 0) > 0)
{
TranslateMessage(&message);
DispatchMessage(&message);
}
return 0;
}
In theory, I could use GetForegroundWindow, but it seems to me that this is a terrible option, because the window can receive a keyboard message from some other process (if another process sends a SendMessage to this window) and not the fact that the current window will be exactly the one for which the message was intended.
At the time a keyboard action is generated, the OS doesn't know yet which window will eventually receive the message. That is why the WH_KEYBOARD hook doesn't provide a target HWND, like a WH_MOUSE hook does (since a mouse message carries window-related coordinates).
When a keyboard message is being routed to a target, the message gets delivered to the window that currently has input focus.
Per About Keyboard Input:
The system posts keyboard messages to the message queue of the foreground thread that created the window with the keyboard focus. The keyboard focus is a temporary property of a window. The system shares the keyboard among all windows on the display by shifting the keyboard focus, at the user's direction, from one window to another. The window that has the keyboard focus receives (from the message queue of the thread that created it) all keyboard messages until the focus changes to a different window.
Since your hook runs inside of the message queue of the target thread, you can use GetFocus() to get the target HWND at that time:
Retrieves the handle to the window that has the keyboard focus, if the window is attached to the calling thread's message queue.
Otherwise, you can use a WH_CALLWNDPROC/RET hook instead, which gets called when the message is actually delivered to a window. However, you can't block messages with this hook (as you were asking about in your previous question).
I think what you might be looking for is a hook of type WH_JOURNALRECORD.
With this, the callback procedure that Windows will call in response to the various events that this hook intercepts is of type JournalRecordProc, and the lparam parameter passed to this function points to an EVENTMSG structure, which looks like this:
typedef struct tagEVENTMSG {
UINT message;
UINT paramL;
UINT paramH;
DWORD time;
HWND hwnd;
} EVENTMSG;
And there is your hwnd!
I have created a dialog that runs on the main thread of an MFC application. While this dialog is made modal, it does a few things on the main thread (i.e message pump thread) that takes time. Whenever something takes time, I create an automatic variable using the MFC class CWaitCursor.
While in the OnInitDialog method, it works as intended. However, If I do the same past the end of OnInitDialog, like for example in a method called in response to a custom Window Message, the the wait cursor doesn't show up. However, if I "activate the application" using EveryWAN (which is a remote control application allowing to see and control the device from the PC), then the cursor DOES show up.
I could provide the code, but it's filled with a lot of stuff that is unlikely to be related to the problem. I'll just write an example code instead that shows what I just tried to explain :
class MyDialog : public CDialog
{
public:
BOOL OnInitDialog()
{
CWaitCusor oWaitCursor; // Shows wait cursor.
Sleep( 5000 );
PostMessage( WM_FOO, 0, 0 );
}
LRESULT OnFoo( WPARAM wParam, LPARAM lParam )
{
CWaitCusor oWaitCursor; // Does NOT show wait cursor, unless I "activate" the application...
Sleep( 5000 );
}
};
Do you have an idea of what could cause the wait cursor not to show up past InInitDialog ? Is it a Windows CE bug ? Is it a focus, screen refresh or a z-index problem ? Thank you.
I am trying to detect when a user alt tabs out of my window. I have tried several messages (WM_ACTIVATE, WM_KILLFOCUS, WM_ACTIVATEAPP, WM_NCACTIVATE, WM_CANCELMODE, and WM_SYSCOMMAND checking for SC_MINIMIZE). But the same problem still occurs. The following happens.
My application is in focus, and I hold down alt and press tab.
The next window is brought forth.
I keep holding down alt and then press tab again, but this time, only tab is recognized and so I end up tabbing in the new window rather than alt tabbing again as is the usual behavior.
I am using a low level keyboard hook, and if I uninstall that this alt tabbing behavior described above works perfectly. I basically want to uninstall the keyboard hook as soon as the user leaves my window in this fashion. As soon as I release alt tab while in another window, my window receives several of the messages I look for and the hook is uninstalled. But when you hold the keys in this manner, it does not work as expected while the hook is installed.
why do you need to remove the keyboard hook? The SetWindowsHook is local to your application and not affect the other apps, therefore it already does nothing if your app doesn't have the focus.
Edit
make sure you call the next hook in your callback with CallNextHookEx to not screw up screen readers
I used the following code and it seems to be catching the focus changes, and I am not getting any problem with regard to holding down ALT or TAB in various combinations. But I didn't thoroughly test it yet.
EDIT: I read your post again and see that your problem is probably an interaction between the two different hooks. So I am also hooking the keyboard in my app, but I am using an Import Address Table hook of DispatchMessage. My hooks don't interact the way yours do, but my solution may not be appropriate for you.
bool AddHookToKeyboardFocusChanges()
{
HHOOK hhookFocusChange = NULL;
hhookFocusChange = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)CallWindowProcHook, NULL, GetCurrentThreadId()); // the last parameter makes it a local, not global hook
if(hhookFocusChange == NULL)
return false;
return true;
}
///////////////////////////////////////////////////////////////////////////////
// This is the routine that we register to be called on every call to a
// WindowProc in our application; we use it to catch WM_SETFOCUS and
// WM_KILLFOCUS messages that indicate gaining or losing keyboard input focus.
// Unlike keyboard, mouse, paint, and timer messages, the focus messages are not
// posted to the message queue. Instead they are sent directly to WindowProc.
// We must hook them here.
//
LRESULT WINAPI CallWindowProcHook(int nCode, WPARAM wParam, LPARAM lParam)
{
UINT message = ((CWPSTRUCT*)lParam)->message;
switch(message)
{
case WM_ACTIVATE:
OutputDebugString(L"Window activated.\n");
break;
case WM_SETFOCUS:
OutputDebugString(L"Window focused.\n");
break;
case WM_KILLFOCUS:
OutputDebugString(L"Window focus lost.\n");
break;
default:
break;
}
// CallNextHookEx calls the next hook in the chain.
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
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);
I'm writing a Windows CE application in C++ directly applying the WINAPI. In this application I parse a text file, which may or may not be large and thus may or may not take a while to load; and as I will add functionality to load files across the wireless network, I figured it would be best to add a progress bar.
My aim is to display the progress bar in a modal dialog, thereby preventing the user from interacting with the main window. A thread is then created to perform the loading in the background, leaving the main thread to update the GUI.
However, using EndDialog() prevents me to return to the code which loads the file until the dialog has been closed. Obviously I want to show the dialog and then load the load, periodically updating the progress from the background thread. At this point I only know of two options to circumvent this:
Create the dialog using CreateDialog, modify the message handler to accommodate messages designated to the dialog, disable the main window and lastly create the background thread.
Create the background thread in a suspended initial state, create the dialog using DialogBoxParam passing along the thread ID, and when capturing the WM_INITDIALOG resume the thread.
Although any of these two would probably work (I'm leaning towards the second option), I'm curious about whether this is the way progress bars are supposed to be handled in a Windows environment -- or if there is a leaner, more clever way of doing it.
You don't have to do anything particularly tricky or unusual. Just create the modal dialog box with DialogBox(). In the WM_INITDIALOG handler of your dialog box procedure, create the background thread to load the file. As the loading progresses, send the PBM_SETPOS message to the progress bar control to update it.
When the loading completes, call EndDialog() to close the dialog box. However, EndDialog() must be called from within your dialog procedure. So to do that, you need to send a dummy message (e.g. WM_APP):
DialogBox(..., DlgProc);
// File loading is done and dialog box is gone now
...
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, LPARAM lparam, WPARAM wparam)
{
switch(msg)
{
case WM_INITDIALOG:
CreateThread(..., LoadingThread, ...);
return TRUE;
case WM_APP:
EndDialog(hwnd);
return TRUE;
...
}
return FALSE:
}
DWORD WINAPI LoadingThread(LPVOID param)
{
// Load the file
while(!done)
{
...
SendMessage(hwndProgressBar, PBM_SETPOS, progress, 0);
}
SendMessage(hwndDialogBox, WM_APP, 0, 0);
return 0;
}