参考自:
https://baijiahao.baidu.com/s?id=1751789480271344172
1 先说结论, 主窗口执行 DestroyWindow函数, 拥有窗口跟子窗口以及主窗口收到 WM_DESTROY, WM_NCDESTROY的顺序是有区别, 顺序如下:
main WM_CLOSE
owned WM_DESTROY
owned WM_NCDESTROY
main WM_DESTROY
sub WM_DESTROY
sub WM_NCDESTROY
main WM_NCDESTROY
2 验证核心代码如下, 在程序运行时, 点击主窗口的关闭按钮
1 #include "stdafx.h" 2 #include "msgTest.h" 3 4 #define MAX_LOADSTRING 100 5 #define SUB_WIN_CLASS L"subWinClass" 6 #define SUB_WIN_NAME L"subWinName" 7 8 #define OWNED_WIN_CLASS L"ownedWinClass" 9 #define OWNED_WIN_NAME L"ownedWinName" 10 11 // 全局变量: 12 HINSTANCE hInst = NULL; // 当前实例 13 WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 14 WCHAR szWindowClass[MAX_LOADSTRING];// 主窗口类名 15 16 HWND gHMainWnd = NULL; //主窗口句柄 17 HWND gSubWnd = NULL; // 子窗口句柄 18 HWND gOwnedWnd = NULL; // 拥有的窗口 19 20 // 此代码模块中包含的函数的前向声明: 21 ATOM MyRegisterClass(HINSTANCE hInstance); 22 BOOL InitInstance(HINSTANCE, int); 23 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); 24 INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); 25 26 LRESULT CALLBACK WndProcSub(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 27 { 28 switch (message) 29 { 30 case WM_CLOSE: 31 OutputDebugStringW(L"sub WM_CLOSE\n"); 32 DestroyWindow(hWnd); 33 break; 34 case WM_DESTROY: 35 OutputDebugStringW(L"sub WM_DESTROY\n"); 36 break; 37 case WM_NCDESTROY: 38 OutputDebugStringW(L"sub WM_NCDESTROY\n"); 39 break; 40 default: 41 return DefWindowProc(hWnd, message, wParam, lParam); 42 }; 43 44 return 0; 45 } 46 47 bool CreateSubWnd(HINSTANCE hInstance, int nCmdShow) 48 { 49 WNDCLASSEXW wcex; 50 wcex.cbSize = sizeof(WNDCLASSEX); 51 wcex.style = CS_HREDRAW | CS_VREDRAW; 52 wcex.lpfnWndProc = WndProc; 53 wcex.cbClsExtra = 0; 54 wcex.cbWndExtra = 0; 55 wcex.hInstance = hInstance; 56 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MSGTEST)); 57 wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); 58 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 59 wcex.lpszMenuName = NULL; 60 wcex.lpszClassName = SUB_WIN_CLASS; 61 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 62 63 if (!RegisterClassExW(&wcex)) 64 return false; 65 66 gSubWnd = CreateWindowExW(WS_EX_CLIENTEDGE,SUB_WIN_CLASS, SUB_WIN_NAME, WS_CHILDWINDOW, 10, 10, 100, 100, gHMainWnd, nullptr, hInstance, nullptr); 67 if (!gSubWnd) 68 return false; 69 70 ShowWindow(gSubWnd, nCmdShow); 71 UpdateWindow(gSubWnd); 72 73 return true; 74 } 75 76 LRESULT CALLBACK WndProcOwned(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 77 { 78 switch (message) 79 { 80 case WM_CLOSE: 81 OutputDebugStringW(L"owned WM_CLOSE\n"); 82 DestroyWindow(hWnd); 83 break; 84 case WM_DESTROY: 85 OutputDebugStringW(L"owned WM_DESTROY\n"); 86 break; 87 case WM_NCDESTROY: 88 OutputDebugStringW(L"owned WM_NCDESTROY\n"); 89 break; 90 default: 91 return DefWindowProc(hWnd, message, wParam, lParam); 92 }; 93 94 return 0; 95 } 96 97 bool CreateOwnedWnd(HINSTANCE hInstance, int nCmdShow) 98 { 99 WNDCLASSEXW wcex; 100 wcex.cbSize = sizeof(WNDCLASSEX); 101 wcex.style = CS_HREDRAW | CS_VREDRAW; 102 wcex.lpfnWndProc = WndProc; 103 wcex.cbClsExtra = 0; 104 wcex.cbWndExtra = 0; 105 wcex.hInstance = hInstance; 106 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MSGTEST)); 107 wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); 108 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 109 wcex.lpszMenuName = NULL; 110 wcex.lpszClassName = OWNED_WIN_CLASS; 111 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 112 113 if (!RegisterClassExW(&wcex)) 114 return false; 115 116 gOwnedWnd = CreateWindowExW(0, OWNED_WIN_CLASS, OWNED_WIN_NAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, gHMainWnd, nullptr, hInstance, nullptr); 117 if (!gOwnedWnd) 118 return false; 119 120 ShowWindow(gOwnedWnd, nCmdShow); 121 UpdateWindow(gOwnedWnd); 122 123 return true; 124 } 125 126 int APIENTRY wWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPWSTR lpCmdLine,int nCmdShow) 127 { 128 UNREFERENCED_PARAMETER(hPrevInstance); 129 UNREFERENCED_PARAMETER(lpCmdLine); 130 131 // TODO: 在此放置代码。 132 133 // 初始化全局字符串 134 LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 135 LoadStringW(hInstance, IDC_MSGTEST, szWindowClass, MAX_LOADSTRING); 136 MyRegisterClass(hInstance); 137 138 // 执行应用程序初始化: 139 if (!InitInstance(hInstance, nCmdShow)) 140 { 141 return FALSE; 142 } 143 144 HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MSGTEST)); 145 146 // 主消息循环: 147 MSG msg; 148 while (GetMessageW(&msg, NULL, 0, 0)) 149 { 150 //if (!TranslateAcceleratorW(msg.hwnd, hAccelTable, &msg)) 151 { 152 TranslateMessage(&msg); 153 DispatchMessageW(&msg); 154 } 155 } 156 157 return (int) msg.wParam; 158 } 159 160 ATOM MyRegisterClass(HINSTANCE hInstance) 161 { 162 WNDCLASSEXW wcex; 163 164 wcex.cbSize = sizeof(WNDCLASSEX); 165 166 wcex.style = CS_HREDRAW | CS_VREDRAW; 167 wcex.lpfnWndProc = WndProc; 168 wcex.cbClsExtra = 0; 169 wcex.cbWndExtra = 0; 170 wcex.hInstance = hInstance; 171 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MSGTEST)); 172 wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); 173 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 174 wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MSGTEST); 175 wcex.lpszClassName = szWindowClass; 176 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 177 178 return RegisterClassExW(&wcex); 179 } 180 181 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 182 { 183 hInst = hInstance; // 将实例句柄存储在全局变量中 184 185 gHMainWnd = CreateWindowExW(0,szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 400, 200, nullptr, nullptr, hInstance, nullptr); 186 if (!gHMainWnd) 187 { 188 return FALSE; 189 } 190 191 CreateSubWnd(hInstance, nCmdShow); 192 CreateOwnedWnd(hInstance, nCmdShow); 193 194 ShowWindow(gHMainWnd, nCmdShow); 195 UpdateWindow(gHMainWnd); 196 197 return TRUE; 198 } 199 200 201 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 202 { 203 if (hWnd == gSubWnd) 204 return WndProcSub(hWnd, message, wParam, lParam); 205 else if (hWnd == gOwnedWnd) 206 return WndProcOwned(hWnd, message, wParam, lParam); 207 208 switch (message) 209 { 210 case WM_CREATE: 211 break; 212 case WM_COMMAND: 213 { 214 int wmId = LOWORD(wParam); 215 // 分析菜单选择: 216 switch (wmId) 217 { 218 case IDM_ABOUT: 219 DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); 220 break; 221 case IDM_EXIT: 222 DestroyWindow(hWnd); 223 break; 224 default: 225 return DefWindowProc(hWnd, message, wParam, lParam); 226 } 227 } 228 break; 229 case WM_PAINT: 230 { 231 PAINTSTRUCT ps; 232 HDC hdc = BeginPaint(hWnd, &ps); 233 // TODO: 在此处添加使用 hdc 的任何绘图代码... 234 EndPaint(hWnd, &ps); 235 } 236 break; 237 case WM_CLOSE: 238 OutputDebugStringW(L"main WM_CLOSE\n"); 239 DestroyWindow(hWnd); 240 break; 241 case WM_DESTROY: 242 //PostQuitMessage(0); 243 OutputDebugStringW(L"main WM_DESTROY\n"); 244 break; 245 case WM_NCDESTROY: 246 PostQuitMessage(0); 247 OutputDebugStringW(L"main WM_NCDESTROY\n"); 248 break; 249 default: 250 return DefWindowProc(hWnd, message, wParam, lParam); 251 } 252 return 0; 253 } 254 255 // “关于”框的消息处理程序。 256 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 257 { 258 UNREFERENCED_PARAMETER(lParam); 259 switch (message) 260 { 261 case WM_INITDIALOG: 262 return (INT_PTR)TRUE; 263 264 case WM_COMMAND: 265 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 266 { 267 EndDialog(hDlg, LOWORD(wParam)); 268 return (INT_PTR)TRUE; 269 } 270 break; 271 } 272 return (INT_PTR)FALSE; 273 }