Why doesn't the window set cursor correctly? - c

As MSDN describes:
When the mouse moves over a window, the window receives a WM_SETCURSOR message (unless another window has captured the mouse).
If the application passes WM_SETCURSOR to DefWindowProc, the DefWindowProc function uses the following algorithm to set the cursor image:
If the window has a parent, forward the WM_SETCURSOR message to the parent to handle.
Otherwise, if the window has a class cursor, set the cursor to the class cursor.
If there is no class cursor, set the cursor to the arrow cursor.
And here's my source code:
#include <tchar.h>
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInsTance, LPTSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex = { 0 };
HWND hWnd;
BOOL ret;
MSG msg;
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
// wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = TEXT("MainWindow");
wcex.hIconSm = wcex.hIcon;
RegisterClassEx(&wcex);
hWnd = CreateWindow(wcex.lpszClassName, TEXT("Test"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (ret = GetMessage(&msg, NULL, 0, 0))
{
if (ret == -1)
{
return EXIT_FAILURE;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(EXIT_SUCCESS);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
When I move my cursor quickly over the window, the cursor doesn't change to an arrow, it become a resize arrow; if I set wcex.hCursor to LoadCursor(NULL, IDC_ARROW), everything will be all right. My question is: why doesn't my code work as MSDN says?
I mean, if I don't set wcex.hCursor, and I don't handle the WM_SETCURSOR message, the DefWindowProc should "set the cursor to the arrow cursor", but it seems it doesn't. Why is that?

See description of the hCursor member of the WNDCLASSEX:
A handle to the class cursor. This member must be a handle to a cursor
resource. If this member is NULL, an application must explicitly set
the cursor shape whenever the mouse moves into the application's
window.
WNDCLASSEX structure

Related

What is the bare minimum I need to write in Win32 to get a Window to open?

Question:
I am trying to set up the Winapi with C to display a simple window with the bare minimum of code, how do I do that in the way that my source code is formatted?
Issue:
The following does not open a window, it simply closes, why is this?
/* window_s.h */
typedef struct {
WNDCLASS wc;
HINSTANCE hInstance;
HWND hwnd;
} WINDOW;
/* setUpWinProc.h */
LRESULT CALLBACK WindowProc( HWND hwnd,
UINT uMsg, WPARAM wParam,
LPARAM lParam) { }
/* registerWindow.c */
void registerWindow(WINDOW *window) {
const char CLASS_NAME[]
= "Window Class Name";
window->wc.lpfnWndProc = WindowProc;
window->wc.hInstance = window->wc.hInstance;
window->wc.lpszClassName = CLASS_NAME;
RegisterClass(&(window->wc));
}
/* createWindow.c */
int_fast64_t CreateWindow_(WINDOW *window) {
window->hwnd = CreateWindowEx(
0, // Optional window styles
window->wc.lpszClassName, // Window class
"Window", // Window text
WS_OVERLAPPEDWINDOW, //Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
window->hInstance, // Instance handle
NULL // Additional application data
);
if (window->hwnd == NULL)
return 0;
return window->hwnd;
}
/* Window_Main.c */
#include <windows.h>
#include "window_s.h"
#include "setUpWinProc.h"
#include "registerWindow.c"
#include "createWindow.c"
#include <stdio.h>
int WINAPI WinMain ( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR pCmdLine,
int nCmdShow ) {
WINDOW window = {{}, hInstance};
registerWindow(&window);
CreateWindow_(&window);
ShowWindow(window.hwnd, nCmdShow);
}
This is part of the issue:
int WINAPI WinMain ( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR pCmdLine,
int nCmdShow ) {
WINDOW window = {{}, hInstance};
registerWindow(&window);
CreateWindow_(&window);
ShowWindow(window.hwnd, nCmdShow);
}
What do you think happens after ShowWindow returns and WinMain itself returns? (Hint: the program exits).
Extend your WinMain to pump messages with TranslateMessage+DispatchMessage.
int WINAPI WinMain ( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR pCmdLine,
int nCmdShow ) {
MSG msg;
WINDOW window = {{}, hInstance};
registerWindow(&window);
CreateWindow_(&window);
ShowWindow(window.hwnd, nCmdShow);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Then your message proc needs to handle WM_CLOSE and WM_PAINT as a minimum and be able to forward to the default window proc for messages it doesn't handle.
LRESULT CALLBACK WindowProc( HWND hwnd,
UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default: {
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
return 0;
}
Your RegisterClass call looks suspicous as well. Let's initialize like this:
WNDCLASSEXW wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WindowProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszClassName = szWindowClass;
RegisterClassExW(&wcex);
If you have Visual Studio (and edition), there's a default Win32 application that generates the most minimal of stub applications that does exactly what you are trying to achieve. Look for the C++ project for "Default Windows Application" or similar.
The core issue is here:
LRESULT CALLBACK WindowProc( HWND hwnd,
UINT uMsg, WPARAM wParam,
LPARAM lParam) { }
As the compiler warned, this function needs to return a value (but isn't). The behavior of registering this as a window procedure is undefined. It will probably fail to create a window; CreateWindowEx() calls into the window procedure with WM_NCCREATE and WM_CREATE messages before it returns. Either message handler must return a particular value to continue window creation.
There's a similar issue with the window class name: It's using a local variable, but passes a pointer to it out. As registerWindow() returns, CLASS_NAME is gone. Class registration succeeds, but when it comes time to create the window, the call to CreateWindowEx() uses garbage as the window class name.
The first fix is thus:
LRESULT CALLBACK WindowProc( HWND hwnd,
UINT uMsg, WPARAM wParam,
LPARAM lParam) {
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
and
void registerWindow(WINDOW *window) {
static const char CLASS_NAME[] = "Window Class Name";
// ...
}
That solves the window creation, though you won't see the window for long (if at all) because the code immediately falls out of WinMain() (which also needs to return a value), causing the process to terminate.
To fix that, you'll have to dispatch messages on the thread that created the window. The following will address both of these issues:
int WINAPI WinMain ( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR pCmdLine,
int nCmdShow ) {
WINDOW window = {{}, hInstance};
registerWindow(&window);
CreateWindow_(&window);
ShowWindow(window.hwnd, nCmdShow);
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return msg.wParam;
}
That's the bare minimum required (unless you count MessageBox() as "getting a window to open"). But it won't allow you to exit the application. To add that functionality, you'll want to add a WM_DESTROY handler that will signal the message loop to end, like so:
LRESULT CALLBACK WindowProc( HWND hwnd,
UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}

In WIndows API, under what "event" do I put child control creation or why aren't my controls appearing?

It's been years since I last worked with Windows API and I'm trying my hand at it again. I have a simple Window (with title "Test"). I put in the Window message handler under WM_CREATE, 2 CreateWindow calls to create a static and an edit control. I'm pretty sure the coordinates are decent (not overlapping or off the window rectangle). Am I putting the calls under the wrong event? It's just a simple window - not MDI or SDI or anything like that. Here is my code for the entire program. I hope it's sufficient to figure out what I'm doing wrong. I'm using Eclipse CDT with Cygwin's G++ compiler to build it....:
#include <windows.h>
BOOL InitApplication(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow);
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "";
wc.lpszClassName = "MainWindow";
return(RegisterClass(&wc));
}
BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
HINSTANCE hCurInstance = hinstance;
HWND hWnd = CreateWindow("MainWindow", "Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND)(NULL), (HMENU)(NULL), hCurInstance, (LPVOID)(NULL));
if(!hWnd)
return(FALSE);
// Show the window and send a WM_PAINT message to the window
// procedure.
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return(TRUE);
}
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
CreateWindow("WC_STATIC", "&Test: ", SS_LEFT | WS_VISIBLE | WS_CHILD, 10, 10, 50, 20, hWnd, NULL, (HINSTANCE)(GetWindowLongPtr(hWnd, GWLP_HINSTANCE)), (LPVOID)(NULL));
CreateWindow("WC_EDIT", "", WS_BORDER | WS_TABSTOP | WS_VISIBLE | WS_CHILD | ES_LEFT, 60, 10, 50, 20, hWnd, NULL, (HINSTANCE)(GetWindowLongPtr(hWnd, GWLP_HINSTANCE)), (LPVOID)(NULL));
return(0);
default:
return(DefWindowProcA(hWnd, uMsg, wParam, lParam));
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmd, int nCmdShow)
{
BOOL fGotMessage;
MSG msg;
if(!InitApplication(hInstance))
return(FALSE);
if(!InitInstance(hInstance, nCmdShow))
return(FALSE);
while(((fGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0) && (fGotMessage != -1))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);
}
WM_CREATE is the correct "event" message to create the child controls in.
You are simply using the wrong class names for the child controls. That is why you are not seeing them. Had you checked the result of those CreateWindow() calls for failures, you would have noticed that CreateWindow() was returning NULL, and GetLastError() was reporting ERROR_CANNOT_FIND_WND_CLASS (1407).
You need to replace "WC_STATIC" with "Static", and replace "WC_EDIT" with "Edit". Or, you can use the pre-defined WC_STATIC and WC_EDIT constants that are defined in <commctrl.h>.
You can also replace GetWindowLongPtr(hWnd, GWLP_HINSTANCE) with NULL when creating system-defined classes, as they are registered globally, not per-module. The HINSTANCE parameter of CreateWindow/Ex() is ignored for them.
On a side note: your GetMessage() loop can be simplified to just:
while (GetMessage(&msg, (HWND) NULL, 0, 0))
See: When will GetMessage return -1?

Make only part of a window scrollable in win32 api

I have created a working application with pure win32 APIs and C. It has a shared "status area" on top. Rest of the area displays device information (multiple fields per device).
How do I make the device information area only scrollable?
All my widgets are currently attached to single main window handle. See here the picture about what I want :
There must be a simple way to like group the widgets of bottom page and attach scrollbar to only the group, but I cannot seem to find a working technique with google. I guess my problem is I do not know hot to create groups of widgets or something. Attaching scrollbar to to the full window works fine but I want only partial.
For those interested: I'm using Dev-C++ 5.11 with TDM-GCC 4.9.2 . I don't have and won't have a resource editor. It is all in C code.
Help!
Edit1: Ok, I believe the correct term I need to search for is "child windows" per https://learn.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows
Edi2: This old scavenged code worked somewhat: https://cboard.cprogramming.com/windows-programming/94197-creating-child-window-parent-window-post676608.html#post676608 . It creates ugly window inside window but it is what I need to get started.
Ok, i reduced the code into a minimum: https://gist.github.com/usvi/b39713e270f1f75880698baacd1774b4 or
#include <windows.h>
LRESULT CALLBACK HelloWndProc (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCMLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HelloApplication");
HWND hwnd,hwnd2;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = HelloWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = sizeof(long);
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows 95/98/NT"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName,
TEXT("Hello World for Windows"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
400,
400,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
hwnd2 = CreateWindow(szAppName,
TEXT("Hello World"),
WS_CHILD | WS_VISIBLE | WS_VSCROLL,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
300,
hwnd,
NULL,
hInstance,
NULL);
ShowWindow(hwnd2, iCmdShow);
UpdateWindow(hwnd2);
while (GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK HelloWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message){
case WM_CREATE:
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, TEXT("Hello, Windows"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
And it works, see:
My problem basically was that child window creation CreateWindow() first parameter was omitted or otherwise wrong. It needs to match named and registered WNDCLASS.
NOTE: This is a rough rough rough sketch, I might need to make 2 classes and second message handler. But this is a good start.

Cannot Create Window-based Application in Non-main Thread

In one of my projects, I need to create a window in a non-main thread. I have never done that so I don't much experience on that.
According to the MSDN documentation and the SO question, I should be able to create a window in other thread, but I cannot succeed. Even though, in thread start routine, I register a window class, create a window and provide a message loop, the thread starts and exits immediately. In addition, I cannot debug the thread start routine so I cannot hit the break points inside it.
Is there something I am missing? I hope I don't miss anything silly.
Please consider the following demo. Thank you for taking your time.
#include <Windows.h>
#include <tchar.h>
HANDLE hThread;
DWORD WINAPI OtherUIThreadFunc(LPVOID args);
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND m_hwnd;
MSG msg;
WNDCLASSEX m_wcx;
const int MESSAGE_PROCESSED = 0;
const TCHAR* m_szClassName = _T("DemoWndCls");
const TCHAR* m_szWindowTitle = _T("Demo Window");
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int nCmdShow)
{
hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)OtherUIThreadFunc, hInstance, 0, NULL);
/*MSG msg;
ZeroMemory(&m_wcx, sizeof(m_wcx));
m_wcx.cbSize = sizeof(m_wcx);
m_wcx.style = CS_VREDRAW | CS_HREDRAW;
m_wcx.hInstance = hInstance;
m_wcx.lpszClassName = m_szClassName;
m_wcx.lpfnWndProc = WndProc;
m_wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
m_wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
m_wcx.hbrBackground = (HBRUSH)COLOR_WINDOW;
if (!RegisterClassEx(&m_wcx))
return false;
m_hwnd = CreateWindowEx(0, m_wcx.lpszClassName, m_szWindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 480, 360, NULL, NULL, hInstance, NULL);
if (!m_hwnd)
return false;
ShowWindow(m_hwnd, SW_NORMAL);
UpdateWindow(m_hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;*/
}
DWORD WINAPI OtherUIThreadFunc(LPVOID args)
{
HINSTANCE hInstance = (HINSTANCE)args;
ZeroMemory(&m_wcx, sizeof(m_wcx));
m_wcx.cbSize = sizeof(m_wcx);
m_wcx.style = CS_VREDRAW | CS_HREDRAW;
m_wcx.hInstance = hInstance;
m_wcx.lpszClassName = m_szClassName;
m_wcx.lpfnWndProc = WndProc;
m_wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
m_wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
m_wcx.hbrBackground = (HBRUSH)COLOR_WINDOW;
if (!RegisterClassEx(&m_wcx))
return false;
m_hwnd = CreateWindowEx(0, m_wcx.lpszClassName, m_szWindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 480, 360, NULL, NULL, hInstance, NULL);
if (!m_hwnd)
return false;
ShowWindow(m_hwnd, SW_NORMAL);
UpdateWindow(m_hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
return MESSAGE_PROCESSED;
case WM_DESTROY:
PostQuitMessage(0);
return MESSAGE_PROCESSED;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
Window creation succeeds (in theory, anyway). The issue is that the primary thread moves one to return, which causes the runtime to terminate the process.
To solve the issue you will have to keep the primary thread alive. A call to WaitForSingleObject, or a message loop are possible options.
This is mostly a result of following the conventions of C and C++. In either case returning from the main function is equivalent to calling the exit() function. This explains why returning from the primary thread tears down the entire process.
Bonus reading: If you return from the main thread, does the process exit?

DestroyWindow() is not destroying child window

WndProc is the window procedure of the main window. ChildProc is the window procedure of the child window. ChildProc is not receiving WM_DESTROY. What am I doing wrong?
EDIT: If I remove the WS_CHILD window style from hChild = CreateWindowExW(...); so it's hChild = CreateWindowExW(..., WS_VISIBLE, ...); I do get WM_DESTROY in ChildProc.
Also, I'm using Windows 10 and Visual Studio 2008
#include <windows.h>
HINSTANCE g_hInst;
LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
if(hdc)
{
RECT rc;
GetClientRect(hwnd, &rc);
SetBkMode(hdc, TRANSPARENT);
FillRect(hdc, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
TextOut(hdc, 0, 0, TEXT("Child"), 5);
EndPaint(hwnd, &ps);
}
}
break;
case WM_DESTROY:
MessageBoxW(0, L"Child WM_DESTROY", 0, MB_OK);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hChild;
switch(msg)
{
case WM_CREATE:
{
WNDCLASSEXW wc;
SecureZeroMemory(&wc, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
wc.hCursor = LoadCursorW(0, IDC_ARROW);
wc.hInstance = g_hInst;
wc.lpfnWndProc = ChildProc;
wc.lpszClassName = L"Childclass////";
if(!RegisterClassExW(&wc)) return -1;
hChild = CreateWindowExW(0, L"Childclass////", 0, WS_VISIBLE | WS_CHILD,
0, 0, 200, 100, hwnd, 0, g_hInst, 0);
if(!hChild) return -1;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEXW wc;
HWND hwnd;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursorW(0, IDC_ARROW);
wc.hIcon = LoadIconW(0, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = L"Mainclass";
if(!RegisterClassExW(&wc)) return 0;
g_hInst = hInstance;
hwnd = CreateWindowExW(0, L"Mainclass", L"Main window", WS_OVERLAPPEDWINDOW, 240, 240, 400, 200, 0, 0, hInstance, 0);
if(!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessageW(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return (int)msg.wParam;
}
when you call DestroyWindow (assume with valid window handle) - all child windows of course will be destroyed. and all child windows of course received WM_DESTROY
ChildProc is not receiving WM_DESTROY.
this is false. i absolute sure it receive it.
What am I doing wrong?
debug diagnostic and call PostQuitMessage from wrong place.
you decide that ChildProc is "not receiving" WM_DESTROY only because you not view message box. but it will be just closed, even before shown, if you call PostQuitMessage(0); before it.
when a window is being destroyed WM_DESTROY is sent first to the owned windows (if any), then to window being destroyed and finally to the child windows (if any).
so in case you use child window - first parent window received WM_DESTROY and you call PostQuitMessage then child window call MessageBox which just returned without show due previous PostQuitMessage call.
if you use owned window - it receive WM_DESTROY first and show MessageBox normal. and only after you close it parent window receive WM_DESTROY finally and you call PostQuitMessage
for fix this, at first need call PostQuitMessage from WM_NCDESTROY - the parent window receive this message after all owned and child windows.
at second the MessageBox not the best for debug diagnostic. much better use DbgPrint, OutputDebugString or breakpoints in debugger
thank #RemyLebeau for link to Raymond Chen blog - why MessageBox() does not show anything if PostQuitMessage() was already called beforehand:
The other important thing about modality is that a WM_QUIT message
always breaks the modal loop.
So, if PostQuitMessage() is called before MessageBox(), the latter will receive the WM_QUIT message, cancel its UI, re-post WM_QUIT, and exit.

Resources