Clicks on window stop working after SetParent(NULL) - c++

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);

Related

Change external app window from tool to overlapped

I'm using Unity to build games. It has a main window and dockable windows. I have several screens. I undock some windows to other screens, however I'm also using other apps, and when I come back to the main Unity window, I don't want the child windows to go in front of my other app windows.
Using Spy++, I saw that the child windows have WS_POPUP and WS_EX_TOOLWINDOW styles, so I did this:
EnumWindows(FindUnityWindows, NULL);
And:
BOOL CALLBACK FindUnityWindows(HWND hwnd, LPARAM lParam)
{
char class_name[80];
GetClassName(hwnd, class_name, sizeof(class_name));
int processid = GetWindowThreadProcessId(hwnd, NULL);
if (!strcmp(class_name, "UnityContainerWndClass"))
{
long style = GetWindowLong(hwnd, GWL_STYLE);
style |= WS_OVERLAPPED;
style &= ~(WS_POPUP);
long exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
exstyle |= WS_EX_APPWINDOW;
exstyle &= ~(WS_EX_TOOLWINDOW);
ShowWindow(hwnd, SW_HIDE);
SetWindowLong(hwnd, GWL_STYLE, style);
SetWindowLong(hwnd, GWL_EXSTYLE, exstyle);
ShowWindow(hwnd, SW_SHOW);
}
return TRUE;
}
Which effectively changes the styles (I checked in Spy++), but the windows behaviour doesn't change. I don't have a tab in the taskbar, and the windows still appears when I activate the main window.
What more can I do?

How to get main window handle from a child window handle

I want to get handle to main window from foreground window.
hWnd = GetForegroundWindow();
hwndOwner = GetWindow(hWnd, GW_OWNER);
hwndParent = GetParent(hWnd);
hwndOwner and hwndParent are just one layer above the foreground window. How do I get main window handle without recursively calling above functions?
Use the GetAncestor with the GA_ROOT flag:
GetAncestor(hwndChild, GA_ROOT);
Note that I am not certain of the difference between GA_ROOT and GA_ROOTOWNER.

WinAPI Creating a second dialogbox after clicking a button control on the first dialogbox and destroying the first

I'm trying to make a second dialogbox prompt the user after they click OK on the first dialogbox while destroying the first dialogbox in the process.
This is my first dialogbox that pops up after the user click a button on the main window itself.
void displayDialogW(HWND hWnd)
{
HWND hDlg = CreateWindowW(L"myDialogClass", L"Enter Desired Width", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 150, 300, 150, hWnd, NULL, NULL, NULL);
CreateWindowW(L"static", L"Width: ", WS_VISIBLE | WS_CHILD | WS_BORDER, 30, 20, 100, 20, hDlg, NULL, NULL, NULL);
CreateWindowW(L"edit", L"...", WS_VISIBLE | WS_CHILD | WS_BORDER, 80, 20, 180, 20, hDlg, NULL, NULL, NULL);
CreateWindowW(L"button", L"OK", WS_VISIBLE | WS_CHILD, 120, 60, 30, 30, hDlg, (HMENU)5, NULL, NULL);
TCHAR buff[1024];
GetWindowText(hDlg, buff, 1024);
desiredWidth = _wtoi(buff);
EnableWindow(hWnd, false);
}
The second dialogbox is more or less the same as the first but I'm not sure how to manipulate the button on the first dialogbox to make sure it opens the second dialogbox and destroys the first window at the same time.
I found a function called DestroyWindow but it needs a hDlg input so I can't exactly put it in my dialogprodecure command function. So, I'm not too sure how I would go about this.
The second dialog cannot have the first dialog as a parent if that's going to be destroyed right away. You could, instead, open the second dialog with the parent set to the same parent as the first one, then the first dialog can be safely destroyed.
case IDOK: // assuming hWnd is first dialog
{
createSecondDialog(GetWindow(hWnd, GW_OWNER)); // open second dialog
DestroyWindow(hWnd); // close first dialog
}
This is using GetWindow(hWnd, GW_OWNER) rather than GetParent(hWnd) since displayDialogW creates a window with WS_OVERLAPPED style, and in that case what is being passed into the CreateWindowW call must be the owner of the new window (per the docs for owned windows).
DestroyWindow:
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.
You can hide the parent window after opening the child window, like this:
case ID_BUTTON1:
{
displayDialogW(hWnd);
SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW);
}
Updated:
From A window can have a parent or an owner but not both,
Note that changing a window’s parent or owner is not a normal
operation.
But if you must destroy the parent window, then you can implement it according to Reinstate Monica's comment, using SetParent.

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.

Why does DestroyWindow close my application?

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.)