Reactos
at master 377 lines 9.4 kB view raw
1/* 2 * PROJECT: ReactOS api tests 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Test for SHChangeNotify 5 * COPYRIGHT: Copyright 2020-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 6 */ 7 8// This program is used in SHChangeNotify and ShellExecCmdLine testcases. 9 10#include "shelltest.h" 11#include "shell32_apitest_sub.h" 12#include <assert.h> 13 14typedef enum DIRTYPE 15{ 16 DIRTYPE_DESKTOP = 0, 17 DIRTYPE_DESKTOP_DIR, 18 DIRTYPE_DRIVES, 19 DIRTYPE_PRINTERS, 20 DIRTYPE_DIR1, 21 DIRTYPE_MAX 22} DIRTYPE; 23 24static HWND s_hMainWnd = NULL, s_hSubWnd = NULL; 25static LPITEMIDLIST s_pidl[DIRTYPE_MAX]; 26static UINT s_uRegID = 0; 27static INT s_iStage = -1; 28 29#define EVENTS (SHCNE_CREATE | SHCNE_DELETE | SHCNE_MKDIR | SHCNE_RMDIR | \ 30 SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM) 31 32inline LPITEMIDLIST DoGetPidl(INT iDir) 33{ 34 LPITEMIDLIST ret = NULL; 35 36 switch (iDir) 37 { 38 case DIRTYPE_DESKTOP: 39 { 40 SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &ret); 41 break; 42 } 43 case DIRTYPE_DESKTOP_DIR: 44 { 45 WCHAR szPath1[MAX_PATH]; 46 SHGetSpecialFolderPathW(NULL, szPath1, CSIDL_DESKTOPDIRECTORY, FALSE); 47 ret = ILCreateFromPathW(szPath1); 48 break; 49 } 50 case DIRTYPE_DRIVES: 51 { 52 SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &ret); 53 break; 54 } 55 case DIRTYPE_PRINTERS: 56 { 57 SHGetSpecialFolderLocation(NULL, CSIDL_PRINTERS, &ret); 58 break; 59 } 60 case DIRTYPE_DIR1: 61 { 62 WCHAR szPath1[MAX_PATH]; 63 SHGetSpecialFolderPathW(NULL, szPath1, CSIDL_PERSONAL, FALSE); // My Documents 64 PathAppendW(szPath1, L"_TESTDIR_1_"); 65 ret = ILCreateFromPathW(szPath1); 66 break; 67 } 68 default: 69 { 70 assert(0); 71 break; 72 } 73 } 74 75 return ret; 76} 77 78static BOOL OnCreate(HWND hwnd) 79{ 80 s_hSubWnd = hwnd; 81 82 for (INT i = 0; i < DIRTYPE_MAX; ++i) 83 s_pidl[i] = DoGetPidl(i); 84 85 return TRUE; 86} 87 88static BOOL InitSHCN(HWND hwnd) 89{ 90 assert(0 <= s_iStage); 91 assert(s_iStage < NUM_STAGE); 92 93 SHChangeNotifyEntry entry; 94 INT sources = 0; 95 LONG events = EVENTS; 96 switch (s_iStage) 97 { 98 case 0: 99 { 100 entry.fRecursive = FALSE; 101 entry.pidl = NULL; 102 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 103 events = EVENTS; 104 break; 105 } 106 case 1: 107 { 108 entry.fRecursive = TRUE; 109 entry.pidl = NULL; 110 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 111 events = EVENTS; 112 break; 113 } 114 case 2: 115 { 116 entry.fRecursive = FALSE; 117 entry.pidl = s_pidl[DIRTYPE_DESKTOP]; 118 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 119 events = EVENTS; 120 break; 121 } 122 case 3: 123 { 124 entry.fRecursive = TRUE; 125 entry.pidl = s_pidl[DIRTYPE_DESKTOP]; 126 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 127 events = EVENTS; 128 break; 129 } 130 case 4: 131 { 132 entry.fRecursive = TRUE; 133 entry.pidl = s_pidl[DIRTYPE_DESKTOP_DIR]; 134 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 135 events = EVENTS; 136 break; 137 } 138 case 5: 139 { 140 entry.fRecursive = FALSE; 141 entry.pidl = s_pidl[DIRTYPE_DRIVES]; 142 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 143 events = EVENTS; 144 break; 145 } 146 case 6: 147 { 148 entry.fRecursive = TRUE; 149 entry.pidl = s_pidl[DIRTYPE_DRIVES]; 150 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 151 events = EVENTS; 152 break; 153 } 154 case 7: 155 { 156 entry.fRecursive = TRUE; 157 entry.pidl = s_pidl[DIRTYPE_PRINTERS]; 158 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 159 events = EVENTS; 160 break; 161 } 162 case 8: 163 { 164 entry.fRecursive = FALSE; 165 entry.pidl = s_pidl[DIRTYPE_DIR1]; 166 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; 167 events = EVENTS; 168 break; 169 } 170 case 9: 171 { 172 entry.fRecursive = TRUE; 173 entry.pidl = s_pidl[DIRTYPE_DIR1]; 174 sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel | 175 SHCNRF_RecursiveInterrupt; 176 events = EVENTS; 177 break; 178 } 179 DEFAULT_UNREACHABLE; 180 } 181 182 s_uRegID = SHChangeNotifyRegister(hwnd, sources, events, WM_SHELL_NOTIFY, 1, &entry); 183 if (s_uRegID == 0) 184 return FALSE; 185 186 return TRUE; 187} 188 189static void UnInitSHCN(HWND hwnd) 190{ 191 if (s_uRegID) 192 { 193 SHChangeNotifyDeregister(s_uRegID); 194 s_uRegID = 0; 195 } 196} 197 198static void OnCommand(HWND hwnd, UINT id) 199{ 200 switch (id) 201 { 202 case IDYES: // Start testing 203 { 204 s_hMainWnd = ::FindWindow(MAIN_CLASSNAME, MAIN_CLASSNAME); 205 if (!s_hMainWnd) 206 { 207 ::DestroyWindow(hwnd); 208 break; 209 } 210 s_iStage = 0; 211 InitSHCN(hwnd); 212 ::PostMessageW(s_hMainWnd, WM_COMMAND, IDYES, 0); 213 break; 214 } 215 case IDRETRY: // New stage 216 { 217 UnInitSHCN(hwnd); 218 ++s_iStage; 219 InitSHCN(hwnd); 220 ::PostMessageW(s_hMainWnd, WM_COMMAND, IDRETRY, 0); 221 break; 222 } 223 case IDNO: // Quit 224 { 225 s_iStage = -1; 226 UnInitSHCN(hwnd); 227 ::DestroyWindow(hwnd); 228 break; 229 } 230 } 231} 232 233static void OnDestroy(HWND hwnd) 234{ 235 UnInitSHCN(hwnd); 236 237 for (auto& pidl : s_pidl) 238 { 239 CoTaskMemFree(pidl); 240 pidl = NULL; 241 } 242 243 ::PostMessageW(s_hMainWnd, WM_COMMAND, IDNO, 0); 244 245 PostQuitMessage(0); 246} 247 248static BOOL DoSendData(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) 249{ 250 DWORD cbPidl1 = ILGetSize(pidl1), cbPidl2 = ILGetSize(pidl2); 251 DWORD cbTotal = sizeof(lEvent) + sizeof(cbPidl1) + sizeof(cbPidl2) + cbPidl1 + cbPidl2; 252 LPBYTE pbData = (LPBYTE)::LocalAlloc(LPTR, cbTotal); 253 if (!pbData) 254 return FALSE; 255 256 LPBYTE pb = pbData; 257 258 *(LONG*)pb = lEvent; 259 pb += sizeof(lEvent); 260 261 *(DWORD*)pb = cbPidl1; 262 pb += sizeof(cbPidl1); 263 264 *(DWORD*)pb = cbPidl2; 265 pb += sizeof(cbPidl2); 266 267 CopyMemory(pb, pidl1, cbPidl1); 268 pb += cbPidl1; 269 270 CopyMemory(pb, pidl2, cbPidl2); 271 pb += cbPidl2; 272 273 assert(INT(pb - pbData) == INT(cbTotal)); 274 275 COPYDATASTRUCT CopyData; 276 CopyData.dwData = 0xBEEFCAFE; 277 CopyData.cbData = cbTotal; 278 CopyData.lpData = pbData; 279 BOOL ret = (BOOL)::SendMessageW(s_hMainWnd, WM_COPYDATA, (WPARAM)s_hSubWnd, (LPARAM)&CopyData); 280 281 ::LocalFree(pbData); 282 return ret; 283} 284 285static void DoShellNotify(HWND hwnd, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2, LONG lEvent) 286{ 287 if (s_iStage < 0) 288 return; 289 290 DoSendData(lEvent, pidl1, pidl2); 291} 292 293static INT_PTR OnShellNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) 294{ 295 LONG lEvent; 296 PIDLIST_ABSOLUTE *pidlAbsolute; 297 HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &pidlAbsolute, &lEvent); 298 if (hLock) 299 { 300 DoShellNotify(hwnd, pidlAbsolute[0], pidlAbsolute[1], lEvent); 301 SHChangeNotification_Unlock(hLock); 302 } 303 else 304 { 305 pidlAbsolute = (PIDLIST_ABSOLUTE *)wParam; 306 DoShellNotify(hwnd, pidlAbsolute[0], pidlAbsolute[1], lParam); 307 } 308 return TRUE; 309} 310 311static LRESULT CALLBACK SubWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 312{ 313 switch (uMsg) 314 { 315 case WM_CREATE: 316 return (OnCreate(hwnd) ? 0 : -1); 317 318 case WM_COMMAND: 319 OnCommand(hwnd, LOWORD(wParam)); 320 break; 321 322 case WM_SHELL_NOTIFY: 323 return OnShellNotify(hwnd, wParam, lParam); 324 325 case WM_DESTROY: 326 OnDestroy(hwnd); 327 break; 328 329 default: 330 return ::DefWindowProcW(hwnd, uMsg, wParam, lParam); 331 } 332 return 0; 333} 334 335INT APIENTRY 336wWinMain( 337 HINSTANCE hInstance, 338 HINSTANCE hPrevInstance, 339 LPWSTR lpCmdLine, 340 INT nCmdShow) 341{ 342 if (lstrcmpiW(lpCmdLine, L"") == 0 || lstrcmpiW(lpCmdLine, L"TEST") == 0) 343 return 0; 344 345 WNDCLASSW wc = { 0, SubWindowProc }; 346 wc.hInstance = hInstance; 347 wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); 348 wc.hCursor = LoadCursorW(NULL, IDC_ARROW); 349 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); 350 wc.lpszClassName = SUB_CLASSNAME; 351 if (!RegisterClassW(&wc)) 352 { 353 assert(0); 354 return -1; 355 } 356 357 HWND hwnd = CreateWindowW(SUB_CLASSNAME, SUB_CLASSNAME, WS_OVERLAPPEDWINDOW, 358 CW_USEDEFAULT, CW_USEDEFAULT, 400, 100, 359 NULL, NULL, hInstance, NULL); 360 if (!hwnd) 361 { 362 assert(0); 363 return -2; 364 } 365 366 ShowWindow(hwnd, SW_SHOWNORMAL); 367 UpdateWindow(hwnd); 368 369 MSG msg; 370 while (GetMessageW(&msg, NULL, 0, 0)) 371 { 372 TranslateMessage(&msg); 373 DispatchMessageW(&msg); 374 } 375 376 return 0; 377}