Why does DestroyWindow close my application? - c++

I'v created a window after creating my main one but calling DestroyWindow on its handle closes the entire application, how can I simply get rid of it?
it looks like this:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
HWND fakehandle;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW | WS_EX_LAYERED,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
fakehandle = CreateWindow(szWindowClass, "FAKE WINDOW", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd || !fakehandle)
{
return FALSE;
}
//some code
DestroyWindow(fakehandle);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
how can I destroy this window without destroying my main one? I'm creating a dummy window to check for multisampling in OpenGL.
Thanks

I've just found this comment:
If the specified window is a parent or owner window, DestroyWindow automatically destroys the associated child or owned windows when it destroys the parent or owner window. The function first destroys child or owned windows, and then it destroys the parent or owner window.
on the DestroyWindow MSDN page.
Could this have some bearing on your problem? Could you be setting the parent of hWnd where you've got //some code?

DestroyWindow() sends a WM_DESTROY to the window in question. If the WndProc passes WM_DESTROY on to DefWindowProc(), then DefWindowProc() will kill your app.
So, in your WndProc, create a handler for WM_DESTROY (if you don't already have one), and check the window handle. You should be able to differentiate between the two and take action from there.
// assuming you have the two window handles as hwnd1 and hwnd2
case WM_DESTROY:
if( hwnd == hwnd1 ) {
// this will kill the app
PostQuitMessage(0);
} else if( hwnd == hwnd2 ) {
// chucking WM_DESTROY on the floor
// means this window will just close,
// and the other one will stay up.
return;
}
break;
Be aware that if you do PostQuitMessage() on behalf of either window, it will take down your application, because PostQuitMessage() will terminate the message loop.

I suspect that specifying a parent (Fourth param from the end) for the "fakehandle" window instead of NULL, may help.
Also, you might check whether or not this quote "If the window being destroyed is a child window that does not have the WS_EX_NOPARENTNOTIFY style, a WM_PARENTNOTIFY message is sent to the parent." (From: msdn.microsoft.com) applies to your case.

Does the class referred to by szWindowClass call PostQuitMessage on receipt of WM_CLOSE or WM_DESTROY? That would stop your message loop first time round, I should think. (But if you're using the debugger, presumably you'd have spotted this?)
In any event, for your dumb window, for best results you'd need a second window class with a dumb WndProc. (I think DefWindowProc would be suitable.)

Related

Clicks on window stop working after SetParent(NULL)

Given two windows, a parent and child, if I call SetParent() on the child passing NULL as the last parameter, the child window doesn't respond to clicks anymore.
I noticed that the child window responds when I use PostMessage() to send WM_LBUTTONDOWN/UP to it.
What am I missing?
HWND hWnd = FindWindow(NULL, L"");
HWND child = GetWindow(hWnd, GW_CHILD);
SetParent(child, NULL);
Per the SetParent documentation:
For compatibility reasons, SetParent does not modify the WS_CHILD or WS_POPUP window styles of the window whose parent is being changed. Therefore, if hWndNewParent is NULL, you should also clear the WS_CHILD bit and set the WS_POPUP style after calling SetParent. Conversely, if hWndNewParent is not NULL and the window was previously a child of the desktop, you should clear the WS_POPUP style and set the WS_CHILD style before calling SetParent.
When you change the parent of a window, you should synchronize the UISTATE of both windows. For more information, see WM_CHANGEUISTATE and WM_UPDATEUISTATE.
Try this:
HWND hWnd = FindWindow(NULL, L"");
HWND child = GetWindow(hWnd, GW_CHILD);
SetParent(child, NULL);
LONG_PTR style = GetWindowLongPtr(child, GWL_STYLE);
SetWindowLongPtr(child, GWL_STYLE, (style & ~WS_CHILD) | WS_POPUP);

Where is the best place to create a child control of a child window in a pure WinAPI GUI App?

I am working on a WinAPI only application. I want to create a STATIC child window that will be used as a "container" to child controls. In the examples that I have found so far, the "container" window is created catching the WM_CREATE in the main window procedure. I assumed the best place to create the child controls of the child window was by catching the WM_CREATE for the child window. To do this, I first need to subclass the child window to point to the new window procedure. The thing is the window is created before it is subclassed, therefore the WM_CREATE is sent to the original procedure rather than to my user created procedure. I can of course create the child controls after creating the child static window inside the main window procedure, but I do not believe this to be the best way to do it. What is the best option?
Working sample code:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
HWND hContainer = CreateWindow(WC_STATIC, L"Container", WS_CHILD, 0, 0, 100, 50, hWnd, (HMENU)ID_CONTAINER, NULL, NULL);
WNDPROC wpOldProc = (WNDPROC)SetWindowLongPtr(hContainer, GWLP_WNDPROC, (LONG_PTR)ChildWindowProc);
HWND hButton = CreateWindow(WC_BUTTON, L"Button", WS_CHILD | BS_PUSHBUTTON, 0, 0, 20, 10, hContainer, (HMENU)ID_BUTTON, NULL, NULL); // This works well
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK ChildWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
// HWND hButton = CreateWindow(WC_BUTTON, L"Button", WS_CHILD | BS_PUSHBUTTON, 0, 0, 20, 10, hContainer, (HMENU)ID_BUTTON, NULL, NULL);
// This point is never reached because the WM_CREATE message is sent before subclassing of the window
}
break;
default:
break;
}
return CallWindowProc(gsHdl.wpTE, hWnd, message, wParam, lParam);
}
The main principal here is separation of concerns: A window will typically create its children in response to its WM_CREATE message as:
This ensures the window "owns" the creation of its children. It also guarantees that when the parent window is created directly by classname, all the children get automatically created. And lastly, because WM_CREATE is sent before a window is made visible/painted, it ensures that the child windows are all created by the time the window is first painted.
A lot of these considerations fall away when using child windows to organise child windows :- If you are making a proper abstracted control window that is one thing, but if you are just arranging some relatively standard windows controls that you want the main window to be able to interact with then deep hierarchies are annoying to navigate, there are some weirdness in how mouse and painting happen where you might want to actually arrange the container and its children in the "wrong" z-order that you could not achieve if they were strictly arranged as child/parents.
Which is to say there is nothing wrong with creating a static window, and using the returned HWND as a parent window on subsequent lines - especially if that reduces the number of window classes, lines of code / creation convolution - and simplifies the parent windows relationship to its controls.
Creating sub-windows/controls under WM_CREATE messages does ensure that sub-windows/controls are created after successful window creation and that all sub-windows/sub-controls are automatically created when creating the main window.
But I don't think it's much different from the below:
HWND hContainer = CreateWindow (WC_STATIC, L "Container", WS_CHILD, 0, 0, 100, 50, hWnd, (HMENU) ID_CONTAINER, NULL, NULL);
if(hContainer)
{
HWND hButton = CreateWindow (WC_BUTTON, L "Button", WS_CHILD | BS_PUSHBUTTON, 0, 0, 20, 10, hContainer, (HMENU) ID_BUTTON, NULL, NULL);
}
It also ensures the creation order and preconditions.
If you really need to modify WinProc and capture WM_CREATE messages in a standard windows controls, SetWindowsLongPtr does not meet your requirements. Use SetClassLongPtr before creating "Container" instead.

How to hide a window, instead of exiting, when the top right cross button is pressed?

By default, a standard window created with:
HWND hwnd = CreateWindowEx(0, CLASS_NAME, L"Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
will be closed, causing the program to exit, when we click on the top right cross:
My program has a single such window, but also an always-present icon in the system tray notification area.
How to make that clicking on the top right cross makes this window hidden, but does not exit the program?
This didn't work in the main window message loop, the app is still exiting.
switch (msg)
{
case WM_CLOSE:
ShowWindow(hWnd, SW_HIDE);
break;
}
DefWindowProc calls DestroyWindow when it gets WM_CLOSE so all you have to do is not pass the message to DefWindowProc:
switch (msg)
{
case WM_CLOSE:
ShowWindow(hWnd, SW_HIDE);
return 0;
}
DestroyWindow alone will not exit a program with a "normal" message loop. It is usually the WM_QUIT message you generate in your main windows WM_DESTROY with PostQuitMessage that causes your message loop to stop and exit the program...

Set full focus on a button (`SetFocus()` is not enough)

Is there a way to set true full focus on a push button (button window class) in WinAPI?
SetFocus() somewhat sets focus (the button gets an inner dotted border), but the button is actually partially focused and still cannot be pressed with the Enter key, only the Spacebar key works. At the same time, if I move focus to a sibling button with the Tab key, then this sibling button (as well as the first button if I then return focus to it using Shift+Tab) gets a true focus (visually, not just inner dotted focus border is added to the really focused button, but its main outer border becomes blue [Windows 7]), and now it reacts to Enter as intended.
How to make a button such fully focused programmatically?
Screenshot of the three button states:
Some background for clarity: there is a window (created with the regular combination of WNDCLASSEX / RegisterClassEx / CreateWindowEx() with WS_OVERLAPPEDWINDOW as its style) with a multiline edit box (edit window class with ES_MULTILINE style) and several push buttons. To implement keyboard navigation using the Tab key, I process the WM_KEYDOWN event in the edit box's procedure (subclassed via SetWindowLong()), otherwise I could navigate between buttons and from buttons to the edit box, but not from the edit box to a button. All the controls have WS_TABSTOP style. The issue with the button focus takes place when I set focus using SetFocus() when the Tab key is pressed on the edit box having focus and caret.
Minimal relevant C++ code:
HWND mainWindow;
WNDPROC defaultEditCallback = NULL;
int WINAPI WinMain(HINSTANCE instance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEXW wc;
wchar_t windowClass[] = L"testcase";
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = 0;
wc.lpfnWndProc = mainWindowCallback;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instance;
wc.hIcon = NULL;
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = (LPCWSTR)windowClass;
wc.hIconSm = NULL;
RegisterClassExW(&wc);
mainWindow = CreateWindowW(
(LPCWSTR)windowClass, (LPCWSTR)windowClass, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, NULL, NULL, instance, NULL
);
HWND edit = CreateWindowExW(
WS_EX_CLIENTEDGE, (LPCWSTR)L"edit", NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | WS_TABSTOP,
0, 0, 0, 0, mainWindow, (HMENU) 10,
(HINSTANCE) GetWindowLongPtrW(mainWindow, GWLP_HINSTANCE),
NULL
);
defaultEditCallback = (WNDPROC)SetWindowLongPtrW(edit, GWLP_WNDPROC, (LONG)editCallback);
HWND firstButton = createButton(mainWindow, 20, L"First", buttonWidth, buttonHeight);
HWND secondButton = createButton(mainWindow, 30, L"Second", buttonWidth, buttonHeight);
HWND thirdButton = createButton(mainWindow, 40, L"Third", buttonWidth, buttonHeight);
// [Skipped] Sizing and positioning controls.
ShowWindow(mainWindow, nCmdShow);
UpdateWindow(mainWindow);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
if (!IsDialogMessage(mainWindow, &msg)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
return (int)msg.wParam;
}
LRESULT CALLBACK mainWindowCallback(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
if (WM_DESTROY == msg) {
PostQuitMessage(0);
}
return DefWindowProcW(window, msg, wParam, lParam);
}
LRESULT CALLBACK editCallback(HWND control, UINT msg, WPARAM wParam, LPARAM lParam) {
if (WM_KEYDOWN == msg && VK_TAB == wParam) {
HWND next = GetNextDlgTabItem(mainWindow, control, (int)(GetKeyState(VK_SHIFT) & 0x8000));
SetFocus(next);
return 0;
}
return CallWindowProc(defaultEditCallback, control, msg, wParam, lParam);
}
HWND createButton(HWND parentWindow, int id, wchar_t* caption, int width, int height) {
return CreateWindowW(
(LPCWSTR)L"button", (LPCWSTR)caption, WS_VISIBLE | WS_CHILD | WS_TABSTOP,
0, 0, width, height, parentWindow, (HMENU)id, NULL, NULL
);
}
Thanks.
The question does not make it clear whether the WS_OVERLAPPEDWINDOW main window is either a dialog, or subclassed to work like a dialog (i.e. based on DefDlgProc). Some hints about TAB navigation and DM_SETDEFID in the OP and following comments appear to indicate that it's a dialog(-styled) window.
For dialogs, the correct way to move the input focus between child controls is by sending the WM_NEXTDLGCTL message, rather than calling SetFocus directly. As noted in the docs:
This message performs additional dialog box management operations beyond those performed by the SetFocus function WM_NEXTDLGCTL updates the default pushbutton border, sets the default control identifier, and automatically selects the text of an edit control (if the target window is an edit control).
More details at How to set focus in a dialog box, including this part:
As the remarks to the DM_SETDEFID function note, messing directly with the default ID carelessly can lead to odd cases like a dialog box with two default buttons. Fortunately, you rarely need to change the default ID for a dialog.
A bigger problem is using SetFocus to shove focus around a dialog. If you do this, you are going directly to the window manager, bypassing the dialog manager. This means that you can create “impossible” situations like having focus on a pushbutton without that button being the default!
To avoid this problem, don’t use SetFocus to change focus on a dialog. Instead, use the WM_NEXTDLGCTL message.
[EDIT] After the OP edit, the main window turns out to be a regular window, not a dialog. The newly posted code, however, does not remember the focused child (nor does it remove/restore the default pushbutton style) after the main window is deactivated and reactivated, relegates keyboard navigation such as VK_TAB to child controls etc. Those introduce inconsistencies with the default dialog-like behaviors.
In order to make the main window behave like a dialog (and do it right), the WNDPROC would need to mimic the relevant parts of DefDlgProc, at least those that pertain to navigation. From Dialog Box Programming Considerations, among the messages such a WNDPROC would need special handling for are DM_GETDEFID, DM_SETDEFID, WM_ACTIVATE, WM_NEXTDLGCTL, WM_SHOWWINDOW, WM_SYSCOMMAND. Once that's done, my original answer still applies.

Adding statusbar in Win32 application

I want to add a statusbar to my Win32 application. I found out that I can use CreateStatusWindow function. I works fine until I re-size my window. See a part of my block of code:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
CreateStatusWindow(WS_CHILD | WS_VISIBLE, _T("Welcome to SpyWindows"), hWnd, 9000);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
Here are two printscreens of my application main window:
What can I do to have a good status bar? (I also want to divide it in more areas)
The documentation mentions that the status bar will recompute its appropriate position and size when it receives a WM_SIZE message:
The window procedure automatically adjusts the size of the status bar
whenever it receives a WM_SIZE message. Typically, when the size of
the parent window changes, the parent sends a WM_SIZE message to the
status bar.
So, the simplest way to achieve this is to relay to the status bar the WM_SIZE messages received by the parent (with SendMessage(), from its window procedure). The message parameters do not matter, as the status bar does not use them in its computations.