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);
Related
I try to get some messages from other windows. I need to find window by name, I used FindWindowW(). How can I get active window and get message from its?
When I add hwnd in GetMessage(), it doesn't work.
//WndProc
case WM_KEYDOWN:
OutputDebugStringW(L"Key down");
break;
//main.c
//WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
hwnd = FindWindowW(NULL, L"Sublime Text");
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
Only the thread that creates a window can directly receive and dispatch messages for that window. GetMessage() retrieves messages from the calling thread's message queue, so it can only be used with windows that are owned by the calling thread.
Since you are trying to catch messages for a window that is not yours, you will have to use SetWindowsHookEx() or SetWinEventHook() to install a hook callback into that window's owning thread, and then that callback can intercept the desired messages/events for that window. You can use GetWindowThreadProcessId() to get the IDs of the Process and Thread that own the window.
If you use SetWindowsHookEx() and are trying to hook a window in another process, your callback must reside in a DLL so it can be injected into that process. You do not need to do that with SetWinEventHook().
I write simple windows c program to display the bitmap at the place where the left mouse button is pressed. At the first time when I click my left mouse button, the bitmap appears. But on the second time on words the bitmap is not getting displayed on the place where I made a left mouse click.
Here is my code.........
LRESULT CALLBACK myHandler(HWND hw, UINT m, UINT mextra, long co_ord)
{
HDC hdc, hmemdc;
PAINTSTRUCT ps;
HBITMAP hbmp;
RECT r;
HGDIOBJ holdbmp;
int x, y;
switch(m)
{
case WM_LBUTTONDOWN:
hdc = BeginPaint(hw,&ps);
hmemdc = CreateCompatibleDC(hdc);
hbmp = LoadBitmap(h, MAKEINTRESOURCE(IDB_BITMAP1));
holdbmp = SelectObject(hmemdc, hbmp);
x = LOWORD(co_ord);
y = HIWORD(co_ord);
BitBlt(hdc, x, y, 190, 220, hmemdc, 0, 0, SRCCOPY);
EndPaint(hw,&ps);
SelectObject(hmemdc, holdbmp);
DeleteObject(hbmp);
DeleteDC(hmemdc);
DeleteDC(hdc);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hw,m,mextra,co_ord);
}
return 0L;
}
The code is wrong about seven different ways from Sunday. Even after the changes you've made in response to WhozCraig's comments, it is still wrong.
For starters, the only place you are allowed to call BeginPaint and EndPaint is in response to a WM_PAINT message. You are trying to call these functions in response to a WM_LBUTTONDOWN message. That cannot work. What you'll want to do is trigger a WM_PAINT message from within your WM_LBUTTONDOWN message handler, which you can do by calling the InvalidateRect() function, passing your window handle and NULL for the rectangle to invalidate (to invalidate your entire window). Then, inside of the WM_PAINT message handler, you can call BeginPaint/EndPaint and do your drawing. If you want the drawing to be different depending on whether the left mouse button is down, you can either set a flag inside of the WM_LBUTTONDOWN message handler and test the value of that flag inside of your WM_PAINT message handler, or you can use something like GetKeyState to determine whether the mouse button is down (VK_LBUTTON).
You are also leaking GDI objects because you are not correctly releasing/destroying them. A bitmap that has been loaded with LoadBitmap needs to be destroyed by calling DeleteObject. (However, loading a bitmap repeatedly inside of a WM_PAINT message handler will lead to poor performance. Instead, prefer to load the bitmap a single time in response to the WM_CREATE message, cache its handle in a global or class-level variable, use it when necessary, and destroy the bitmap via that handle in response to the WM_DESTROY message.)
The LOWORD and HIWORD macros should never be used for extracting cursor coordinates. These are liable to return the wrong result on multiple monitor systems. Instead, you should use GET_X_LPARAM and GET_Y_LPARAM. This is specifically mentioned in the MSDN documentation for the WM_LBUTTONDOWN message. Always read the documentation for things you are unfamiliar with!
Finally, the signature for your window procedure is also completely wrong. I have no idea where you got that signature, but not only do you have non-standard parameter names that obscures the actual meaning of those arguments, but you have the wrong types. A window procedure looks like this:
LRESULT CALLBACK myHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
...
}
It is extremely difficult to learn Windows API programming by just hacking around, especially if you aren't disciplined about reading the MSDN documentation. If you really want to learn it, consider purchasing a book, like Charles Petzold's classic Programming Windows, 5th edition (yes, you need the 5th edition, not a newer edition).
I'm trying to destroy the HWND I'm currently using, and open up a new window...
this is my code :
PostMessage(MainHwnd, WM_DESTROY, NULL, NULL); // Destroy the window
getClient() -> StartClient(); // Opening the client
where in StartClient I have:
RegisterMainClass(MainInstance); // Registaring the class
//Creating the Window
MainHwnd = CreateWindowEx(WS_EX_CLIENTEDGE ,"MainClient", "Client", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 600, 500, NULL, NULL, MainInstance, NULL);
ShowWindow(MainHwnd, SW_SHOW);
UpdateWindow(MainHwnd);
So the window is destroyed, and the Client window shows up.. but the client window only shows up for about a second, and then dissapears again! Why is that? I have checked it couple of times, it actually creates the client window well, but it somehow hides it...
Also, I've checked with the WndProc of the Client Window,and it does reach the line of return DefWindowProc(hwnd, msg, wParam, lParam);
So what is going on? why is the window dissapearing right away?
I also have those 2 sections in my RoomProc :
case WM_CLOSE:
PostQuitMessage(1);
break;
case WM_DESTROY:
DestroyWindow(hwnd);
Don't post WM_DESTROY manually. Use DestroyWindow() instead:
//PostMessage(MainHwnd, WM_DESTROY, NULL, NULL);
DestroyWindow(MainHwnd); // Destroy the window
Since you are going through the message queue, your WM_DESTROY message is delayed until new messages are processed at a later time, but by then your MainHwnd variable has changed value to point at the new window. Besides, posting WM_DESTROY does not actually destroy the window, it merely notifies the window that it is being destroyed.
I've used both lines (destroying the window and create a new window) inside a thread, and therefor, it did problems to me. once I called the function normally and not in a different thread, everything went smooth...
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.
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.