[SHELL32_APITEST] Strengthen SHChangeNotify testcase more and more (#4174)
[reactos.git] / modules / rostests / apitests / shell32 / shell32_apitest_sub.cpp
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 "SHChangeNotify.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 }