How can I close a child window with WM_CLOSE? - c++

I create the child hwnd as popup with close button and WndchildProc for handle message from child window
m_childHwnd = CreateWindowEx(
NULL,
TEXT("STATIC"), TEXT("childW"),
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
m_parentHWnd, NULL,
GetModuleHandle(NULL), NULL
);
m_childhProc = (WNDPROC)SetWindowLongPtr(m_childHwnd , GWLP_WNDPROC, (LONG_PTR)WndChildProc);
static LRESULT CALLBACK WndChildProc(HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
DestroyWindow(hwnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
default:
return CallWindowProc(m_childhProc, hwnd, message, wParam, lParam);
}
I expected when I press close button of child window the message WM_CLOSE will trigger. And I can close child hwnd with this way.
But when close button of child hwnd pressed, nothing happen. If close button of parent-hwnd pressed, WM_DESTROY message trigger.
So, I can't close (just) the child window with its close button.
How can I close a child window with its close button?
Updated: missing text in copying, added: CallWindowProc for default case.

I found solution with event WM_NCLBUTTONUP trigger when close button clicked.
case WM_NCLBUTTONUP:
ShowWindow(hwnd, SW_HIDE);
return 0;

Related

C++ and Win32 WC_DIALOG close event problem

my English is bad. sorry
HWND DIALOG_0 = CreateWindowEx(0, WC_DIALOG, "Security Alert", DS_SETFONT | WS_OVERLAPPEDWINDOW | WS_VISIBLE, 600,300,300,200,Win.hwnd_0,NULL,NULL,NULL);
(WNDPROC)SetWindowLongPtr(DIALOG_0 , GWLP_WNDPROC, (INT_PTR)dede);
ShowWindow (DIALOG_0, SW_SHOW);
UpdateWindow(DIALOG_0);
How do I do the shut down event.
How do I make HWND click events
I use it, but it gets locked up.
(WNDPROC)SetWindowLongPtr(DIALOG_0 , GWLP_WNDPROC, (INT_PTR)dede);
WC_DIALOG create dede function
LONG_PTR __stdcall Win32::dede(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CTLCOLORDLG:
return (INT_PTR)GetStockObject(HOLLOW_BRUSH);
break;
default:
DefWindowProc(hDlg, message, wParam, lParam);
break;
}
return (INT_PTR)FALSE;
}
You usually create dialogs with DialogBox or CreateDialog, not with CreateWindow. If you use CreateWindow then you cannot use the DS_ styles.
GWLP_WNDPROC replaces the original window procedure and you normally should call CallWindowProc, not DefWindowProc. If you don't do this then you don't get the default IDCANCEL handling and all the other things a dialog usually provides.
Catch WM_CLOSE and WM_COMMAND to handle close and click events.
MSDN has a dialog guide here.

WinAPI - button cannot be clicked

I want to make a panel, which groups buttons by itself:
HWND my_panel = CreateWindow(
"STATIC",
"",
WS_VISIBLE | WS_CHILD | WS_BORDER,
30,
100,
300,
300,
main_window, // main dialog
NULL,
( HINSTANCE ) GetWindowLong( main_window, GWL_HINSTANCE ),
NULL
);
Then I add a button to this panel:
HWND button_in_a_group = CreateWindow(
"BUTTON",
"Hello world",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
20,
20,
50,
50,
my_panel, // as a child for above
NULL,
( HINSTANCE ) GetWindowLong( main_window, GWL_HINSTANCE ),
NULL
);
When I click the button, it doesn't send a WM_COMMAND but WM_PARENTNOTIFY to callback function. Then, if I press Enter, it works - WM_COMMAND is sent by the button.
How to enable mouse click on nested button, and why nested windows doesn't work as expected?
Messages are sent to parent window. In this case the static windows is the button's parent. So the main window is not receiving button messages, except WM_PARENTNOTIFY.
You can subclass the static window:
SetWindowSubclass(my_panel, ChildProc, 0, 0);
Define a ChildProc to catch the button messages. See also Subclassing Controls
The button also requires an identifier as follows:
CreateWindow("BUTTON", "Hello world", ... my_panel, HMENU(BUTTON_ID) ...);
WM_COMMAND message is sent to ChildProc when button is clicked. The BN_CLICKED notification carries BUTTON_ID
Note, SetWindowSubclass needs additional header and library:
#include <CommCtrl.h>
#pragma comment(lib, "Comctl32.lib") //Visual Studio option for adding libraries
...
LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR)
{
switch(msg) {
case WM_COMMAND:
switch(LOWORD(wParam)) {
case BUTTON_ID:
MessageBox(0, "hello world", 0, 0);
break;
}
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hwnd, ChildProc, 0);
break;
}
return DefSubclassProc(hwnd, msg, wParam, lParam);
}

Button Control disappears on click - Click Handler does not get fired on Clicking Button - Win32

I am working on Zoom SDK which is based on win32 gui.
I have created 3 buttons using CreateWindow method on the window handle, which is provided by the ZoomSDK.
Code + Screenshot - 1
Now there are two problems with this.
As soon as I click the buttons, they disappear.
See the Screen Shots BEFORE
See the Screen Shots AFTER
I want to know the reason why this is happening and how can I fix this?
HWND hFirstView, hSecondView;
cntrl->GetMeetingUIWnd(hFirstView, hSecondView);
cntrl->MoveFloatVideoWnd(100, 100);
HWND btnHwnd = CreateWindow(
TEXT("button"),
L"Open App",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
0, 0,
50, 25,
hFirstView,
(HMENU)100,
hInst,
NULL);
HWND btnHwnd2 = CreateWindow(
TEXT("button"),
L"Other",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
50, 0,
50, 25,
hFirstView,
(HMENU)101,
hInst,
NULL);
HWND btnHwnd3 = CreateWindow(
TEXT("button"),
L"Raise Hand",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
100, 0,
50, 25,
hFirstView,
(HMENU)103,
hInst,
NULL);
HDC hdc = GetDC(btnHwnd);
SetBkColor(hdc, GetSysColor(COLOR_BTNSHADOW));
SetTextColor(hdc, GetSysColor(COLOR_BACKGROUND));
ReleaseDC(btnHwnd, hdc);
int btnId = GetDlgCtrlID(btnHwnd);
//oldWndProc = (WNDPROC) GetWindowLong(hFirstView, GWL_WNDPROC);
oldWndProc = (WNDPROC) SetWindowLong(hFirstView,
GWL_WNDPROC, (LONG)WndProc);
SendMessage(btnHwnd, BM_SETSTATE, 1, 0);
SetWindowText(hFirstView, L"Title");
I want to handle click event for these buttons. I have tried to use SetWindowsLong to set another WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int id = GetWindowLong(hWnd, GWL_ID);
switch (message)
{
case WM_COMMAND: {
MessageBox(NULL, L"Sign of releaf!", L"Whoaa!", 0);
if (wParam == 1023) {
MessageBox(NULL, L"Sign of releaf!", L"Whoaa!", 0);
}
}
break;
}
return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
}
Now, this WndProc gets called for other events such mouse move etc. It is not working for my three buttons. I want to handle click event i.e WM_COMMAND or any other technique possible.
Because I can not go inside the sdk (they don't provide sources, only .lib) so I can not change their WndProc, nor their internal WM_PAINT. The buttons are sort of overlay on top.
You should not be calling SetBkColor() and SetTextColor() from outside a WM_PAINT handler. The correct way to color a button is to either:
have the parent window handle the WM_CTLCOLORBTN notification.
The WM_CTLCOLORBTN message is sent to the parent window of a button before drawing the button. The parent window can change the button's text and background colors. 
give the button the BS_OWNERDRAW style, and then have the parent window handle the WM_DRAWITEM notification.
Sent to the parent window of an owner-drawn button, combo box, list box, or menu when a visual aspect of the button, combo box, list box, or menu has changed.
Also, when a button sends a BN_CLICKED notification to its parent window, your subclass WndProc() doesn't need to use GetWindowLong(GWL_ID). First, you are calling it on the wrong HWND. And second, the button ID is carried in the message's wParam data.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case 100:
case 101:
case 103:
MessageBox(NULL, L"Sign of relief!", L"Whoaa!", 0);
break;
}
}
break;
}
}
return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
}

c++ win32 api notify mouse inside button area

I've a Button made using Win32Api that I want to be able to notify whenever the user put the mouse inside the button rectangle.
I noticed that when user does that the WM_NOTIFY is called but I don't know which flag to use for ensure that the user has the mouse inside it's area.
Here is my button:
HWND Button = CreateWindow("BUTTON", "Test",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_NOTIFY,
20, 240, 120, 20,
hwnd, (HMENU)101, NULL, NULL);
And my WndProc:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_NOTIFY:
{
//??? Here is where I want to do it
}
case WM_CREATE: //On Window Create
{
wHWND = hwnd;
if (onCreate != NULL)
onCreate(hwnd);
break;
}
case WM_COMMAND: //Command execution
{
//...
break;
}
case WM_DESTROY: //Form Destroyed
{
if (onDestroy != NULL)
onDestroy(hwnd);
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
I don't know if I'm in the right path using WM_NOTIFY or not, maybe there is an easier way to do that. Thank you!
Try catching the WM_SETFOCUS message instead if you want a generic way of tracking focus.
BN_SETFOCUS is sent as WM_COMMAND in the upper 16 bits of WPARAM (HIWORD):
The parent window of the button receives this notification code through the WM_COMMAND message
If you are not talking about focus but just the mouse position then you can use ChildWindowFromPointEx or RealChildWindowFromPoint and a timer.

Window handle doesn't save correctly

I don't really get how to use HWND in c++.
I want to press a button and it should start a thread with a code running.
But I never receive the command for button click in an other callback.
So I did some debugging and it seems like that _wndInstance->GetWndHWND() returns something not valid. The method returns a private field which has it stored.
If you look a case WM_CREATE, the window content added will not show up with _wndInstance->GetWndHWND(). But if I just use hwnd from the parameters, it does work. But how is that possible if my first test-check validates that they are the same??
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (_wndInstance->GetWndHWND() == hwnd)
cout << "same" << endl; // Code is getting here!
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
{
_wndInstance->CreateWndContent(_wndInstance->GetWndHWND()); // not working, but hwnd is!
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
EDIT:
_wndInstance is an instance of a mainwindow class I wrote.
Part of mainWindow header:
private:
HWND _wndHwnd;
public:
HWND GetWndHWND();
MainWindow cpp:
HWND MainWindow::GetWndHWND()
{
return _wndHwnd;
}
_wndHwnd is set in a private method which creates the window:
_wndHwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"\"Xbox controller on WINDOWS\" Manager",
WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, // no maximize box
CW_USEDEFAULT, CW_USEDEFAULT, 450, 370,
NULL, NULL, hinstance, NULL);
if (_wndHwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return;
}
ShowWindow(_wndHwnd, nCmdShow);
UpdateWindow(_wndHwnd);
WM_CREATE is sent before CreateWindowEx() returns, and thus before your assignment happens. You will need to restructure your code so that the HWND is only used after it is assigned to _wndHwnd.
If you don't need to do anything between WM_CREATE and the beginning of the message loop, you can just drop your WM_CREATE handler. That depends on how your code will work in the future.
But a safer approach would be to assign _wndHwnd in your WM_CREATE handler (or even WM_NCCREATE), since you have it available as the hwnd parameter to your window procedure, and it would handle other messages sent between the window creation and the variable assignment. You can even pass _wndInstance as the last parameter to CreateWindowEx() and access it from WM_CREATE; see this for details.