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 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 6 */ 7 8 #include "shelltest.h" 9 #include "shell32_apitest_sub.h" 10 11 static HWND s_hwnd = NULL; 12 static UINT s_uRegID = 0; 13 static BOOL s_fRecursive = FALSE; 14 static DIRTYPE s_iWatchDir = DIRTYPE_NULL; 15 static INT s_nSources = 0; 16 static LPITEMIDLIST s_pidl = NULL; 17 static WCHAR s_path1[MAX_PATH], s_path2[MAX_PATH]; 18 static BYTE s_counters[TYPE_MAX + 1]; 19 static HANDLE s_hEvent = NULL; 20 21 static BOOL 22 OnCreate(HWND hwnd) 23 { 24 s_hwnd = hwnd; 25 s_pidl = DoGetPidl(s_iWatchDir); 26 27 SHChangeNotifyEntry entry; 28 entry.pidl = s_pidl; 29 entry.fRecursive = s_fRecursive; 30 s_uRegID = SHChangeNotifyRegister(hwnd, s_nSources, SHCNE_ALLEVENTS, WM_SHELL_NOTIFY, 1, &entry); 31 return s_uRegID != 0; 32 } 33 34 static void 35 OnCommand(HWND hwnd, UINT id) 36 { 37 switch (id) 38 { 39 case IDOK: 40 case IDCANCEL: 41 DestroyWindow(hwnd); 42 break; 43 } 44 } 45 46 static void 47 OnDestroy(HWND hwnd) 48 { 49 SHChangeNotifyDeregister(s_uRegID); 50 s_uRegID = 0; 51 52 CoTaskMemFree(s_pidl); 53 s_pidl = NULL; 54 55 PostQuitMessage(0); 56 s_hwnd = NULL; 57 } 58 59 static BOOL DoPathes(PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2) 60 { 61 WCHAR path[MAX_PATH]; 62 if (!SHGetPathFromIDListW(pidl1, path)) 63 { 64 s_path1[0] = s_path2[0] = 0; 65 return FALSE; 66 } 67 68 if (wcsstr(path, L"Recent") != NULL) 69 return FALSE; 70 71 StringCchCopyW(s_path1, _countof(s_path1), path); 72 73 if (!SHGetPathFromIDListW(pidl2, s_path2)) 74 s_path2[0] = 0; 75 76 return TRUE; 77 } 78 79 static VOID DoPathesAndFlags(UINT type, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2) 80 { 81 if (DoPathes(pidl1, pidl2)) 82 { 83 s_counters[type] = 1; 84 SetEvent(s_hEvent); 85 } 86 } 87 88 static void 89 DoShellNotify(HWND hwnd, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2, LONG lEvent) 90 { 91 switch (lEvent) 92 { 93 case SHCNE_RENAMEITEM: 94 DoPathesAndFlags(TYPE_RENAMEITEM, pidl1, pidl2); 95 break; 96 case SHCNE_CREATE: 97 DoPathesAndFlags(TYPE_CREATE, pidl1, pidl2); 98 break; 99 case SHCNE_DELETE: 100 DoPathesAndFlags(TYPE_DELETE, pidl1, pidl2); 101 break; 102 case SHCNE_MKDIR: 103 DoPathesAndFlags(TYPE_MKDIR, pidl1, pidl2); 104 break; 105 case SHCNE_RMDIR: 106 DoPathesAndFlags(TYPE_RMDIR, pidl1, pidl2); 107 break; 108 case SHCNE_MEDIAINSERTED: 109 break; 110 case SHCNE_MEDIAREMOVED: 111 break; 112 case SHCNE_DRIVEREMOVED: 113 break; 114 case SHCNE_DRIVEADD: 115 break; 116 case SHCNE_NETSHARE: 117 break; 118 case SHCNE_NETUNSHARE: 119 break; 120 case SHCNE_ATTRIBUTES: 121 break; 122 case SHCNE_UPDATEDIR: 123 DoPathesAndFlags(TYPE_UPDATEDIR, pidl1, pidl2); 124 break; 125 case SHCNE_UPDATEITEM: 126 break; 127 case SHCNE_SERVERDISCONNECT: 128 break; 129 case SHCNE_UPDATEIMAGE: 130 break; 131 case SHCNE_DRIVEADDGUI: 132 break; 133 case SHCNE_RENAMEFOLDER: 134 DoPathesAndFlags(TYPE_RENAMEFOLDER, pidl1, pidl2); 135 break; 136 case SHCNE_FREESPACE: 137 break; 138 case SHCNE_EXTENDED_EVENT: 139 break; 140 case SHCNE_ASSOCCHANGED: 141 break; 142 default: 143 break; 144 } 145 } 146 147 static INT_PTR 148 OnShellNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) 149 { 150 LONG lEvent; 151 PIDLIST_ABSOLUTE *pidlAbsolute; 152 HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &pidlAbsolute, &lEvent); 153 if (hLock) 154 { 155 DoShellNotify(hwnd, pidlAbsolute[0], pidlAbsolute[1], lEvent); 156 SHChangeNotification_Unlock(hLock); 157 } 158 else 159 { 160 pidlAbsolute = (PIDLIST_ABSOLUTE *)wParam; 161 DoShellNotify(hwnd, pidlAbsolute[0], pidlAbsolute[1], lParam); 162 } 163 return TRUE; 164 } 165 166 static LRESULT 167 OnGetNotifyFlags(HWND hwnd) 168 { 169 if (s_uRegID == 0) 170 return 0xFFFFFFFF; 171 172 DWORD dwFlags = 0; 173 for (size_t i = 0; i < _countof(s_counters); ++i) 174 { 175 if (s_counters[i]) 176 dwFlags |= (1 << i); 177 } 178 return dwFlags; 179 } 180 181 static void 182 DoSetPaths(HWND hwnd) 183 { 184 WCHAR szText[MAX_PATH * 2]; 185 StringCchCopyW(szText, _countof(szText), s_path1); 186 StringCchCatW(szText, _countof(szText), L"|"); 187 StringCchCatW(szText, _countof(szText), s_path2); 188 189 FILE *fp = _wfopen(TEMP_FILE, L"wb"); 190 if (fp) 191 { 192 fwrite(szText, (wcslen(szText) + 1) * sizeof(WCHAR), 1, fp); 193 fflush(fp); 194 fclose(fp); 195 } 196 } 197 198 static LRESULT CALLBACK 199 WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 200 { 201 switch (uMsg) 202 { 203 case WM_CREATE: 204 return (OnCreate(hwnd) ? 0 : -1); 205 206 case WM_COMMAND: 207 OnCommand(hwnd, LOWORD(wParam)); 208 break; 209 210 case WM_SHELL_NOTIFY: 211 return OnShellNotify(hwnd, wParam, lParam); 212 213 case WM_DESTROY: 214 OnDestroy(hwnd); 215 break; 216 217 case WM_GET_NOTIFY_FLAGS: 218 return OnGetNotifyFlags(hwnd); 219 220 case WM_CLEAR_FLAGS: 221 ZeroMemory(&s_counters, sizeof(s_counters)); 222 s_path1[0] = s_path2[0] = 0; 223 break; 224 225 case WM_SET_PATHS: 226 DoSetPaths(hwnd); 227 break; 228 229 default: 230 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 231 } 232 return 0; 233 } 234 235 static BOOL ParseCommandLine(LPWSTR lpCmdLine) 236 { 237 LPWSTR pch = lpCmdLine; // fRecursive,iWatchDir,nSources 238 s_fRecursive = !!wcstoul(pch, NULL, 0); 239 pch = wcschr(pch, L','); 240 if (!pch) 241 return FALSE; 242 ++pch; 243 244 s_iWatchDir = (DIRTYPE)wcstoul(pch, NULL, 0); 245 pch = wcschr(pch, L','); 246 if (!pch) 247 return FALSE; 248 ++pch; 249 250 s_nSources = wcstoul(pch, NULL, 0); 251 return TRUE; 252 } 253 254 INT APIENTRY 255 wWinMain(HINSTANCE hInstance, 256 HINSTANCE hPrevInstance, 257 LPWSTR lpCmdLine, 258 INT nCmdShow) 259 { 260 if (lstrcmpiW(lpCmdLine, L"") == 0 || lstrcmpiW(lpCmdLine, L"TEST") == 0) 261 return 0; 262 263 if (!ParseCommandLine(lpCmdLine)) 264 return -1; 265 266 s_hEvent = OpenEventW(EVENT_ALL_ACCESS, TRUE, EVENT_NAME); 267 268 WNDCLASSW wc; 269 ZeroMemory(&wc, sizeof(wc)); 270 wc.lpfnWndProc = WindowProc; 271 wc.hInstance = GetModuleHandleW(NULL); 272 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 273 wc.hCursor = LoadCursor(NULL, IDC_ARROW); 274 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); 275 wc.lpszClassName = CLASSNAME; 276 if (!RegisterClassW(&wc)) 277 return -1; 278 279 HWND hwnd = CreateWindowW(CLASSNAME, CLASSNAME, WS_OVERLAPPEDWINDOW, 280 CW_USEDEFAULT, CW_USEDEFAULT, 400, 100, 281 NULL, NULL, GetModuleHandleW(NULL), NULL); 282 if (!hwnd) 283 return -1; 284 285 ShowWindow(hwnd, SW_SHOWNORMAL); 286 UpdateWindow(hwnd); 287 288 MSG msg; 289 while (GetMessageW(&msg, NULL, 0, 0)) 290 { 291 TranslateMessage(&msg); 292 DispatchMessageW(&msg); 293 } 294 295 CloseHandle(s_hEvent); 296 297 return 0; 298 } 299