Win32 application exits immediately when dialog box is closed

c, c++

Solution

There's a bug in your WndProc() function. The WM_COMMAND case is missing a break. So when it executes, say, IDM_ABOUT then it falls through into the WM_DESTROY case. Sayonara.

I recommend PC-lint.

Problem

[EDITED to add: It turns out that the answer is very boring and has nothing to do with Win32, dialog boxes, etc. I just had an idiotic bug in my code. Thanks to Hans Passant for spotting it.] (This is kinda long. Executive summary: I have a simple Win32 application which creates an icon in the notification area, never shows its main window, and has an "about" box that can be displayed via a right-click on the notification-area icon. For no reason I can fathom, when the "about" box is shown and then closed, the application's main message loop gets a quit message and it exits. What could I have done wrong to cause this?) I'm writing a little program that sits in the notification area ("system tray") and does various not-relevant-here bits of processing in the background. Its UI is almost trivial: you can right-click on the notification-area icon to get a menu, with options "Exit" and "About"; the former quits, the latter pops up a little about-this-program modal dialog. The application is written in C++ and uses Win32 directly (no MFC or anything). My apologies for being stuck in the stone age. The only problem is this: when the "About" dialog is closed, the program exits! What could be causing this? I'm not sure what further information is most useful in figuring this out. Here are a few observations. - The sequence of Windows messages at the end of the application's life, after the dialog's "OK" button is clicked, is as follows. - Dialog box's proc gets WM_CTLCOLORBTN - Application's (invisible) main window gets WM_ENABLE(TRUE). - Dialog box gets WM_CTLCOLORBTN, WM_IMESETCONTEXT, WM_SETFOCUS, WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED, 3xWM_GETICON, WM_NCACTIVATE, 2xWM_GETICON, WM_ACTIVATE, WM_WINDOWPOSCHANGING. - Application's window ges WM_WINDOWPOSCHANGING, WM_NCACTIVATE, messages 0x93, 0x93, 0x91, 0x92, 0x92 (what are these?), WM_ACTIVATE. - Dialog box gets WM_KILLFOCUS, WM_IME_SETCONTEXT. - Application's window gets WM_IME_SETCONTEXT. - Dialog box gets WM_IME_NOTIFY; so does application's main window. - Application's window gets WM_SETFOCUS. - Dialog box gets message 0x90, WM_DESTROY, WM_NCDESTROY. - No further messages after these to either the dialog window's proc or the main application window's. - Then GetMessage returns 0 in the main message loop (the message is WM_QUIT) and it's all over. - The only call to PostQuitMessage in my code is in the main window's WndProc, it occurs when the main window gets WM_DESTROY, and it is not in fact called in this scenario. Perhaps there's something crazy, or something missing, in my code. Here are some extracts (with a few details, which may not be relevant, elided for brevity). The rough structure of my WinMain is as follows: ``` WNDCLASSEX wc; // fill in fields of wc RegisterClassEx(&wc); HWND w = CreateWindow(...); NOTIFYICONDATA nid; memset(&nid, 0, sizeof(nid)); // fill in fields of nid Shell_NotifyIcon(NIM_ADD, &nid); // (start a background thread to do the real work, // which is of no interest here) MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { if (TranslateAccelerator(msg,hwnd, accel, &msg)) continue; TranslateMesage(&msg); DispatchMessage(&msg); } Shell_NotifyIcon(NUM_DELETE, &nid); return (int)msg.wParam; ``` The main window's WndProc looks like this: ``` switch (message) { case WM_USER_SHELLICON: // my own, attached to the icon's menu if (LOWORD(lParam) == WM_RBUTTONDOWN) // ... create menu and return TRUE break; case WM_COMMAND: // menu item switch (LOWORD(wParam)) { case IDM_ABOUT: DialogBox(the_instance, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutProc); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; ``` and the dialog box's proc looks like this: ``` switch (message) { case WM_INITDIALOG: // fill in a version string return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam)==IDOK || LOWORD(wParam)==IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; ```

Original source