I wanted to 'protect' a certain window from closing. So I wanted to hook WM_CLOSE, WM_DESTOY or WM_QUIT. And this is what I tried to do so:
LRESULT CALLBACK WindowHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
{
MSG* msg = (MSG*)lParam;
//if (msg->hwnd == GetForegroundWindow())
{
if (msg->message == WM_CLOSE || msg->message == WM_QUIT || msg->message == WM_DESTROY)
{
//MessageBox(0, "TEST", "", 0);
msg->message = 0;
CallNextHookEx(hMsg, nCode, (WPARAM)NULL, (LPARAM)NULL);
return TRUE;
}
}
}
CallNextHookEx(hMsg, nCode, wParam, lParam);
}
I tried:
Returning TRUE or FALSE
Calling CallNextHookEx with NULL as LPARAM or
editing the msg->message to 0
Also what I noticed, if I hook WH_GETMESSAGE it doesn't 'block' the message loop. But it does so with hooking WH_CALLWNDPROC. I discovered this with prompting a messagebox when the msg->message equals to WM_CLOSE.
Thanks in advance,
Regards.
WH_GETMESSAGE installs a hook for messages that are retrieved from the message queue. It does allow you to modify the message. But the problem is that WM_CLOSE is not posted to the message queue with PostMessage(), it is sent with SendMessage(). This hook can't see it.
WH_CALLWNDPROC installs a hook that runs just before the window procedure is called. Which will allow you to see WM_CLOSE. However, you are not allowed to modify the message or make it disappear.
There is no hook that allows you to do what you want to do. Instead you have to sub-class the window procedure. So that your own custom window procedure is called before the window's own procedure. Now you can simply filter WM_CLOSE by just not calling the old window procedure.
Beware that you still need the same kind of plumbing that SetWindowsHookEx() provides, you still need to inject a DLL with your window procedure into the process. Best way is to still use SetWindowsHookEx to get that done with a dummy hook that doesn't otherwise do anything.
Instead of trying to install a hook, just subclass the window and have your subclass WndProc ignore the WM_CLOSE while forwarding all the other messages.
You can't and shouldn't try to stop WM_QUIT. As rodrigo pointed out, you can't do anything about WM_DESTROY. At that point, DestroyWindow has already been called, and your window is going away whether you like it or not.
Related
This question already has answers here:
Disable X-Button Icon on the top of the right in Messagebox Using C++ Win32 API?
(4 answers)
Closed 2 years ago.
I had this problem for quite a while now.
In my message box, I have a cancel button. I don't want the close button to interfere because my cancel button does something, which shouldn't happen if I press the close button.
I used a loop-and-find trick, but when I open a folder that also has the same title as the message box, the code disables the close button of Explorer, which is very annoying.
This was what I used to disable the close button that also interferes with other windows that have the same title:
DWORD WINAPI CreateMessageBox(void *argr){
*(int*)argr = MessageBoxA( NULL,
"Time is past active hours\nPlease save your work\nSystem is shutting down in 1 minute.\n",
"Warning",
MB_OKCANCEL|MB_ICONSTOP|MB_SYSTEMMODAL );
return 0;
}
thread = CreateThread(NULL, 0, CreateMessageBox, &opt, 0, NULL);
while (!(msg = FindWindow(NULL, "Warning")));
style = GetWindowLongPtr(msg, GWL_STYLE);
SetWindowLongPtr(msg, GWL_STYLE, style & ~WS_SYSMENU);
WaitForSingleObject(thread, INFINITE);
But now, I read somewhere that there is this SC_CLOSE thing that also disables the close button exclusively to that window. How do I use it? I've gone through some posts about it, but none of them uses MessageBox() and that just overwhelms me.
Please, can somebody help me?
Your call to FindWindow() is not specifying a class name, thus it will find any top-level window with a matching title. In your case, an Explorer window. Provide the actual class name for the MessageBox window, which is "#32770", eg:
msg = FindWindow("#32770", "Warning")
However, there is a much more reliable approach that doesn't invoke searching window titles - get rid of the calls to CreateThread() and FindWindow() completely, and instead make the thread that calls MessageBox() use a local WH_CBT hook via SetWindowsHookEx() to catch the HWND that MessageBox() creates, then you can manipulate that window as desired, eg:
LRESULT CALLBACK MyCBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_CREATEWND)
{
((CBT_CREATEWND*)lParam)->lpcs->style & ~WS_SYSMENU;
}
/* alternatively:
if (nCode == HCBT_ACTIVATE)
{
HWND hwnd = (HWND)wParam;
LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
SetWindowLongPtr(hwnd, GWL_STYLE, style & ~WS_SYSMENU);
}
*/
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
...
HHOOK hHook = SetWindowsHookEx(WH_CBT, &MyCBTProc, NULL, GetCurrentThreadId());
opt = MessageBoxA(NULL,
"Time is past active hours\nPlease save your work\nSystem is shutting down in 1 minute.\n",
"Warning",
MB_OKCANCEL|MB_ICONSTOP|MB_SYSTEMMODAL);
if (hHook)
UnhookWindowsHookEx(hHook);
Alternatively, simply use TaskDialog() or TaskDialogIndirect() instead of MessageBox(). A Task Dialog does not have a close button in the title bar, unless you call TaskDialogIndirect() with the TDF_ALLOW_DIALOG_CANCELLATION flag enabled:
Indicates that the dialog should be able to be closed using Alt-F4, Escape, and the title bar's close button even if no cancel button is specified in either the dwCommonButtons or pButtons members.
TaskDialog(NULL, NULL,
L"Warning",
L"Time is past active hours",
L"Please save your work\nSystem is shutting down in 1 minute.",
TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON,
TD_ERROR_ICON,
&opt);
Original Problem:
I have a window that cannot be closed immediately after being created despite my window procedure function being able to detect the WM_CLOSE message, and calling PostQuitMessage and letting Windows continue to handle the window messages with DefWindowProc.
But, after moving the window by dragging the title bar, it seems to be able to close normally.
The code for the my window procedure function is the following:
LRESULT CALLBACK OnEvent(HWND handle, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_QUIT:
{
printf("WM_QUIT\n");
break;
}
case WM_CLOSE:
{
printf("WM_CLOSE\n");
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(handle, message, wParam, lParam);
}
So besides sending the WM_MOVE message to my window or using exit(0) in the WM_CLOSE case block, how can I ensure that my window can be closed immediately after being created?
New Question:
After passing NULL to the hWnd parameter of GetMessageW, instead of my window handle, the window now closes properly hence answering my previous question.
Therefore, why did the GetMessageW function previously not retrieve the WM_QUIT message with the provided handle of the only window, instead of using NULL as the hWnd parameter?
A properly written message loop will never dispatch a WM_QUIT message to a window procedure, it will simply break the loop when GetMessage() returns 0 when it receives a WM_QUIT message:
GetMessage function
If the function retrieves a message other than WM_QUIT, the return value is nonzero.
If the function retrieves the WM_QUIT message, the return value is zero.
If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.
In response to WM_CLOSE, you should be calling DestroyWindow() instead of PostQuitMessage() directly (the default behavior of DefWindowProc(WM_CLOSE) is to call DestroyWindow() for you). You need a WM_DESTROY handler that should then call PostQuitMessage():
LRESULT CALLBACK OnEvent(HWND handle, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
{
printf("WM_CLOSE\n");
DestroyWindow(handle);
return 0;
}
case WM_DESTROY:
{
printf("WM_DESTROY\n");
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(handle, message, wParam, lParam);
}
This is documented on MSDN:
Closing the Window
Here is the flow chart from that page:
As for the GetMessage() issue, if you read the documentation for GetMessage() and PostQuitMessage() more carefully, you will see that the WM_QUIT message posted by PostQuitMessage() is not a window message. Passing a non-NULL HWND to GetMessage() only retrieves messages from PostMessage() and SendMessage() that are intended for that specific HWND 1. GetMessage() will ignore any messages that are not intended for that HWND, which includes thread messages from PostThreadMessage() and PostQuitMessage(). Passing a NULL HWND to GetMessage() allows it to return any pending message, including WM_QUIT.
1 which is dangerous to do, BTW: The dangers of filtering window messages
I had this exact problem when I called GetMessage() in my loop with the handle of the main window as the second argument:
GetMessage(&msg, hMainWindow, 0, 0)
instead of NULL to specify that I want to handle messages for the whole thread / application instance:
GetMessage(&msg, NULL, 0, 0)
The WM_CLOSE messages would lead to WM_QUIT messages, but GetMessage() will ignore these when it's only looking for messages for a particular window. From the documentation:
If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.
What exactly causes moving of the window to have subsequent closing attempts succeed is however still beyond me.
Another issue that I had found was that when you had set your window classes (WNDCLASSEX) windows procedure (lpfnWndProc) I had set it to the default window procedure and had not used a custom built window procedure. So when you had closed it nothing would occur.
Is calling a function that does the same thing as the message you're sending faster than calling SendMessage()?
For example in the code below in case WM_RBUTTONUP:, which is faster, calling EnableTwoWindows(firstWnd, secondWnd);or sending SendMessage(hwnd, CUSTOM_MESSAGE_ENABLE_TWO_WINDOWS, 0, 0);? And what are the advantages and disadvantages of calling a function vs sending a message?
void EnableTwoWindows(HWND hwnd1, HWND hwnd2)
{
EnableWindow(hwnd1, TRUE);
EnableWindow(hwnd2, TRUE);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND firstWnd, secondWnd;
switch(msg)
{
case CUSTOM_MESSAGE_ENABLE_TWO_WINDOWS:
EnableWindow(firstWnd, TRUE);
EnableWindow(secondWnd, TRUE);
break;
case WM_RBUTTONUP:
//EnableTwoWindows(firstWnd, secondWnd); //Is calling this function faster? or
//SendMessage(hwnd, CUSTOM_MESSAGE_ENABLE_TWO_WINDOWS, 0, 0); //Is sending message faster?
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Calling the function directly is faster than going through a SendMessage call.
This can be easily inferred, when you look at what SendMessage does: It first needs to determine, whether the target window is owned by the same thread, and if so, retrieve the window procedure, and finally call that.
If the target window is owned by another thread, it gets more costly, as the SendMessage call has to queue up the message on the target thread's queue, wait for it to invoke a message retrieval function, and can only return, when the target thread has dispatched the message and produced a return value. In that case, however, it is mandatory to call SendMessage. Manipulating windows from threads other than the owning thread results in undefined behavior. SendMessage ensures, that the manipulations run on the same thread, that owns the window.
In addition, the compiler can inline the direct function call. This is not possible when calling SendMessage, since the compiler cannot see its implementation.
Performance is not an issue: You are responding to user input. The user is the bottleneck. Correctness, on the other hand, is essential. If you need to decide, use the following rule: If the windows you are manipulating are owned by a thread other than the calling thread, use SendMessage. If the windows are owned by the calling thread, go for a direct function call.
Yes.
It costs more to call multiple functions than it does to call one function.
Note: I am working in plain C. Not C++, not C#.
I am working on a mod. I've already written a working DLL-injector, as well as the DLL to be injected. Everything is going well, apart from the userinput.
I want to be able to use hotkeys, so I tried to setup a keyboardhook using SetWindowsHookEx.
The following is my callback function:
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
printf("key touched\n");
if (wParam == VK_F5)
{
keyEvent = VK_F5;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
And this is how I set it up:
HHOOK kbHookHandle = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)keyboardHook, NULL, GetCurrentThreadId());
if (kbHookHandle != NULL)
{
printf("keyboard hook successful!\n");
}
else
{
printf("keyboard hook failed!\n");
}
As far as I can tell, the hook gets setup well (I used to have a problem with an invalid parameter, but fixed that by using GetCurrentThreadID). It returns a handle which is not NULL.
But whenever I press a key, there is no output.
To further clarify:
The code above is from the injected DLL. So it effectively 'belongs' to the game process. I have allocated a console using AllocConsole, to print debug messages to.
What am I doing wrong?
EDIT:
To clarify (even more): the listed code is from the injected DLL. It is not the approach I use to inject the DLL - I wrote a seperate (working!) program to do just that.
It surprises some that I use printf(), since that wouldn't show up, considering I call it from inside the host process. Yes, I do call it from inside the host process, but that is not an issue, because I have already allocated a working console. I used an approach very similar to the one mentioned here
EDIT2:
I am not asking why printf() isn't working (because it is), I am asking why this keyboardhook isn't working.
Here's how I detect key down and key up in my NoQuake ID Tech games minimizer. Hope it helps.
I'm currently trying to integrate some animation drawing code of mine into a third party application, under the form of an external plugin.
This animation code in realtime 3d, based on OpenGL, and is supposed to render as fast as it can, usually at 60 frames per second.
In my base application, where I'm the king of the world, I control the application message pump, so that drawing occurs whenever possible. Like that :
for (;;)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
do
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
}
draw();
}
Now that I'm no more king in the world, I have to play nice with the application messages, so that it keeps being responsive. To my knowledge, as I'm a plugin, I can't hijack the whole application message pump ; so I tried various things, doing my drawing in WM_PAINT message handler :
Use WM_TIMER, which doesn't work :I don't know in advance which time step I need (often not fixed) and the timing in not accurate.
Call InvalidateRect as soon as I'm done drawing, doesn't work : completely prevents the rest of the application of being responsive and doing its own refreshing.
Create a 'worker' thread, whose only job is to post a user message to the plugin window. This message is posted as soon as the drawing is finished (signaled by an event). The user message handler, in turn, calls InvalidateRect (see there).
So far, my last attempt is the better, and sometimes work fine.
DWORD WINAPI PaintCommandThreadProc(LPVOID lpParameter)
{
Plugin* plugin = static_cast<Plugin*>(lpParameter);
HANDLE updateEvent = plugin->updateEvent();
while (updateEvent == plugin->updateEvent())
{
::WaitForSingleObject(updateEvent, 100);
::Sleep(0);
if (updateEvent == plugin->updateEvent())
{
::PostMessage(plugin->hwnd(), WM_USER+0x10, 0, 0);
}
}
return 0;
}
...
LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
bool processDefault = true;
LRESULT result = 0;
Plugin* plugin = reinterpret_cast<Plugin*>( GetWindowLong(hWnd, GWL_USERDATA) );
switch (msg) {
...
case WM_GL_MESSAGE:
{
::InvalidateRect( hWnd, NULL, FALSE );
processDefault = false;
result = TRUE;
}
break;
case WM_PAINT:
{
draw(hWnd);
::SetEvent( plugin->updateEvent() );
processDefault = false;
result = TRUE;
}
break;
...
}
if (processDefault && plugin && plugin->m_wndOldProc)
result = ::CallWindowProc(plugin->m_wndOldProc, hWnd, msg, wParam, lParam);
return result;
}
On some occasions, the host application still seems to miss messages. The main characteristics of the problem are that I have to press the 'Alt' key for modal dialogs to show up ; and I have to move the mouse to give some processing time to the host application !...
Is there any 'industry standard' solution for this kind of as-often-as-you-can animation repaint problem ?
Each thread has its own message queue, and messages sent to a window arrive in the queue of the thread that created the window. If you create your plugin window yourself, you can create it in a separate thread, and that way you will have complete control over its message pump.
An alternative solution (which imho is better), is to only have OpenGL rendering in a separate thread. All OpenGL calls must occur in the thread that created the OpenGL context. However, you can create a window in one thread (your application main thread), but create the OpenGL context in another thread. That way the original application message pumps stays intact, and in your rendering thread you can loop forever doing rendering (with calls to SwapBuffers to vsync).
The main problem with that second solution is that communication between the plugin WindowProc and the rendering loop must take into account threading (ie. use locks when accessing shared memory). However since the message pump is separate from rendering, it can be simultaneous, and your message handling is as responsive as it can get.