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-2021 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 
8 // NOTE: This test program closes the Explorer windows before tests.
9 
10 #include "shelltest.h"
11 #include "shell32_apitest_sub.h"
12 #include <time.h>
13 #include <process.h>
14 #include <versionhelpers.h>
15 
16 // --- The selection of tests ---
17 #define DISABLE_THIS_TESTCASE
18 #define NO_TRIVIAL
19 //#define NO_INTERRUPT_LEVEL
20 //#define NO_SHELL_LEVEL
21 #define NEW_DELIVERY_ONLY
22 //#define RANDOM_HALF
23 #define RANDOM_QUARTER
24 
25 // --- Show the elapsed time by GetTickCount() ---
26 //#define ENTRY_TICK
27 #define GROUP_TICK
28 #define TOTAL_TICK
29 
30 static HWND s_hwnd = NULL;
31 static WCHAR s_szSubProgram[MAX_PATH];
32 static HANDLE s_hThread = NULL;
33 static HANDLE s_hEvent = NULL;
34 
35 static BOOL DoCreateEmptyFile(LPCWSTR pszFileName)
36 {
37     FILE *fp = _wfopen(pszFileName, L"wb");
38     if (fp)
39         fclose(fp);
40     return fp != NULL;
41 }
42 
43 struct TEST_ENTRY;
44 
45 typedef BOOL (*ACTION)(const struct TEST_ENTRY *entry);
46 
47 typedef struct TEST_ENTRY
48 {
49     INT line;
50     DIRTYPE iWriteDir;
51     LPCSTR pattern;
52     LPCWSTR path1;
53     LPCWSTR path2;
54     ACTION action;
55 } TEST_ENTRY;
56 
57 #define TEST_FILE      L"_TEST_.txt"
58 #define TEST_FILE_KAI  L"_TEST_KAI_.txt"
59 #define TEST_DIR       L"_TESTDIR_"
60 #define TEST_DIR_KAI   L"_TESTDIR_KAI_"
61 #define MOVE_FILE(from, to) MoveFileW((from), (to))
62 
63 static BOOL DoAction1(const TEST_ENTRY *entry)
64 {
65     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
66     PathAppendW(pszPath, TEST_FILE);
67     ok(DoCreateEmptyFile(pszPath), "Line %d: DoCreateEmptyFile failed\n", entry->line);
68     return TRUE;
69 }
70 
71 static BOOL DoAction2(const TEST_ENTRY *entry)
72 {
73     LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir);
74     PathAppendW(pszPath1, TEST_FILE);
75     PathAppendW(pszPath2, TEST_FILE_KAI);
76     ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n",
77        entry->line, pszPath1, pszPath2, GetLastError());
78     return TRUE;
79 }
80 
81 static BOOL DoAction3(const TEST_ENTRY *entry)
82 {
83     LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir);
84     PathAppendW(pszPath1, TEST_FILE_KAI);
85     PathAppendW(pszPath2, TEST_FILE);
86     ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n",
87        entry->line, pszPath1, pszPath2, GetLastError());
88     return TRUE;
89 }
90 
91 static BOOL DoAction4(const TEST_ENTRY *entry)
92 {
93     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
94     PathAppendW(pszPath, TEST_FILE);
95     ok(DeleteFileW(pszPath), "Line %d: DeleteFileW(%ls) failed (%ld)\n",
96        entry->line, pszPath, GetLastError());
97     return TRUE;
98 }
99 
100 static BOOL DoAction5(const TEST_ENTRY *entry)
101 {
102     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
103     PathAppendW(pszPath, TEST_DIR);
104     ok(CreateDirectoryW(pszPath, NULL), "Line %d: CreateDirectoryW(%ls) failed (%ld)\n",
105        entry->line, pszPath, GetLastError());
106     return TRUE;
107 }
108 
109 static BOOL DoAction6(const TEST_ENTRY *entry)
110 {
111     LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir);
112     PathAppendW(pszPath1, TEST_DIR);
113     PathAppendW(pszPath2, TEST_DIR_KAI);
114     ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n",
115        entry->line, pszPath1, pszPath2, GetLastError());
116     return TRUE;
117 }
118 
119 static BOOL DoAction7(const TEST_ENTRY *entry)
120 {
121     LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir);
122     PathAppendW(pszPath1, TEST_DIR_KAI);
123     PathAppendW(pszPath2, TEST_DIR);
124     ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n",
125        entry->line, pszPath1, pszPath2, GetLastError());
126     return TRUE;
127 }
128 
129 static BOOL DoAction8(const TEST_ENTRY *entry)
130 {
131     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
132     PathAppendW(pszPath, TEST_DIR);
133     ok(RemoveDirectoryW(pszPath), "Line %d: RemoveDirectoryW(%ls) failed (%ld)\n",
134        entry->line, pszPath, GetLastError());
135     return TRUE;
136 }
137 
138 static BOOL DoAction9(const TEST_ENTRY *entry)
139 {
140     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
141     PathAppendW(pszPath, TEST_FILE);
142     SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL);
143     return FALSE;
144 }
145 
146 static BOOL DoAction10(const TEST_ENTRY *entry)
147 {
148     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
149     PathAppendW(pszPath, TEST_FILE);
150     SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL);
151     return FALSE;
152 }
153 
154 static BOOL DoAction11(const TEST_ENTRY *entry)
155 {
156     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
157     PathAppendW(pszPath, TEST_DIR);
158     SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL);
159     return FALSE;
160 }
161 
162 static BOOL DoAction12(const TEST_ENTRY *entry)
163 {
164     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
165     PathAppendW(pszPath, TEST_DIR);
166     SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL);
167     return FALSE;
168 }
169 
170 #define WRITEDIR_0 DIRTYPE_DESKTOP
171 static WCHAR s_szDesktop[MAX_PATH];
172 static WCHAR s_szTestFile0[MAX_PATH];
173 static WCHAR s_szTestFile0Kai[MAX_PATH];
174 static WCHAR s_szTestDir0[MAX_PATH];
175 static WCHAR s_szTestDir0Kai[MAX_PATH];
176 
177 #define WRITEDIR_1 DIRTYPE_MYDOCUMENTS
178 static WCHAR s_szDocuments[MAX_PATH];
179 static WCHAR s_szTestFile1[MAX_PATH];
180 static WCHAR s_szTestFile1Kai[MAX_PATH];
181 static WCHAR s_szTestDir1[MAX_PATH];
182 static WCHAR s_szTestDir1Kai[MAX_PATH];
183 
184 static void DoDeleteFilesAndDirs(void)
185 {
186     DeleteFileW(TEMP_FILE);
187     DeleteFileW(s_szTestFile0);
188     DeleteFileW(s_szTestFile0Kai);
189     DeleteFileW(s_szTestFile1);
190     DeleteFileW(s_szTestFile1Kai);
191     RemoveDirectoryW(s_szTestDir0);
192     RemoveDirectoryW(s_szTestDir0Kai);
193     RemoveDirectoryW(s_szTestDir1);
194     RemoveDirectoryW(s_szTestDir1Kai);
195 }
196 
197 static const TEST_ENTRY s_group_00[] =
198 {
199     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
200     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
201     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
202     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
203     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
204     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
205     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
206     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
207     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
208     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
209     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
210     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
211 
212     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
213     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
214     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
215     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
216     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
217     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
218     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
219     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
220     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
221     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
222     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
223     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
224 };
225 
226 static const TEST_ENTRY s_group_01[] =
227 {
228     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
229     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
230     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
231     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
232     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
233     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
234     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
235     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
236     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 },
237     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 },
238     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 },
239     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 },
240 
241     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
242     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
243     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
244     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
245     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
246     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
247     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
248     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
249     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 },
250     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 },
251     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 },
252     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 },
253 };
254 
255 static const TEST_ENTRY s_group_02[] =
256 {
257     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
258     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
259     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
260     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
261     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
262     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
263     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
264     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
265     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
266     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
267     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
268     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
269 
270     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
271     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
272     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
273     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
274     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
275     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
276     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
277     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
278     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 },
279     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 },
280     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 },
281     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 },
282 };
283 
284 static const TEST_ENTRY s_group_03[] =
285 {
286     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
287     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
288     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
289     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
290     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
291     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
292     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
293     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
294     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 },
295     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 },
296     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 },
297     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 },
298 
299     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
300     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
301     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
302     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
303     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
304     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
305     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
306     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
307     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
308     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
309     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
310     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
311 };
312 
313 static const TEST_ENTRY s_group_04[] =
314 {
315     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 },
316     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 },
317     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 },
318     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 },
319     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 },
320     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 },
321     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 },
322     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 },
323     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
324     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
325     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
326     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
327 
328     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
329     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
330     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
331     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
332     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
333     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
334     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
335     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
336     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
337     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
338     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
339     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
340 };
341 
342 static const TEST_ENTRY s_group_05[] =
343 {
344     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
345     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
346     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
347     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
348     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
349     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
350     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
351     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
352     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
353     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
354     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
355     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
356 
357     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction1 },
358     { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1, s_szTestFile1Kai, DoAction2 },
359     { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1Kai, s_szTestFile1, DoAction3 },
360     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction4 },
361     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction5 },
362     { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1, s_szTestDir1Kai, DoAction6 },
363     { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1Kai, s_szTestDir1, DoAction7 },
364     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction8 },
365     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
366     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
367     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
368     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
369 };
370 
371 static const TEST_ENTRY s_group_06[] =
372 {
373     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 },
374     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 },
375     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 },
376     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 },
377     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 },
378     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 },
379     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 },
380     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 },
381     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 },
382     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 },
383     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 },
384     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 },
385 
386     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
387     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
388     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
389     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
390     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
391     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
392     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
393     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
394     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 },
395     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 },
396     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 },
397     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 },
398 };
399 
400 static const TEST_ENTRY s_group_07[] =
401 {
402     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 },
403     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 },
404     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 },
405     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 },
406     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 },
407     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 },
408     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 },
409     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 },
410     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 },
411     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 },
412     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 },
413     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 },
414 
415     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
416     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
417     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
418     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
419     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
420     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
421     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
422     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
423     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
424     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
425     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
426     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
427 };
428 
429 static const TEST_ENTRY s_group_08[] =
430 {
431     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
432     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
433     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
434     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
435     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
436     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
437     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
438     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
439     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
440     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
441     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
442     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
443 
444     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction1 },
445     { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1, s_szTestFile1Kai, DoAction2 },
446     { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1Kai, s_szTestFile1, DoAction3 },
447     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction4 },
448     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction5 },
449     { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1, s_szTestDir1Kai, DoAction6 },
450     { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1Kai, s_szTestDir1, DoAction7 },
451     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction8 },
452     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 },
453     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 },
454     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 },
455     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 },
456 };
457 
458 static LPCSTR PatternFromFlags(DWORD flags)
459 {
460     static CHAR s_buf[(TYPE_MAX + 1) + 1];
461     for (INT i = 0; i < (TYPE_MAX + 1); ++i)
462         s_buf[i] = (char)('0' + !!(flags & (1 << i)));
463 
464     s_buf[TYPE_MAX + 1] = 0;
465     return s_buf;
466 }
467 
468 static BOOL DoGetPaths(LPWSTR pszPath1, LPWSTR pszPath2)
469 {
470     pszPath1[0] = pszPath2[0] = 0;
471 
472     WCHAR szText[MAX_PATH * 2];
473     szText[0] = 0;
474     FILE *fp = _wfopen(TEMP_FILE, L"rb");
475     if (fp)
476     {
477         fread(szText, 1, sizeof(szText), fp);
478         fclose(fp);
479     }
480 
481     LPWSTR pch = wcschr(szText, L'|');
482     if (pch == NULL)
483         return FALSE;
484 
485     *pch = 0;
486     StringCchCopyW(pszPath1, MAX_PATH, szText);
487     StringCchCopyW(pszPath2, MAX_PATH, pch + 1);
488     return TRUE;
489 }
490 
491 static void DoTestEntry(INT iEntry, const TEST_ENTRY *entry, INT nSources)
492 {
493 #ifdef ENTRY_TICK
494     DWORD dwOldTick = GetTickCount();
495 #endif
496 
497     BOOL bInterrupting = FALSE;
498     if (entry->action)
499         bInterrupting = entry->action(entry);
500 
501     DWORD flags;
502     LPCSTR pattern;
503     if ((nSources & SHCNRF_InterruptLevel) && bInterrupting)
504     {
505         // The event won't work at here. Manually waiting...
506         UINT cTry = ((iEntry == 0) ? 100 : 60);
507         for (UINT iTry = 0; iTry < cTry; ++iTry)
508         {
509             flags = SendMessageW(s_hwnd, WM_GET_NOTIFY_FLAGS, 0, 0);
510             pattern = PatternFromFlags(flags);
511             if (strcmp(pattern, "0000000") != 0)
512                 break;
513 
514             Sleep(1);
515         }
516     }
517     else
518     {
519         if (WaitForSingleObject(s_hEvent, 100) == WAIT_OBJECT_0)
520             Sleep(1);
521 
522         flags = SendMessageW(s_hwnd, WM_GET_NOTIFY_FLAGS, 0, 0);
523         pattern = PatternFromFlags(flags);
524     }
525 
526     SendMessageW(s_hwnd, WM_SET_PATHS, 0, 0);
527 
528     WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH];
529     szPath1[0] = szPath2[0] = 0;
530     BOOL bOK = DoGetPaths(szPath1, szPath2);
531 
532     static UINT s_cCalmDown = 0;
533 
534     if (pattern[TYPE_UPDATEDIR] == '1')
535     {
536         trace("Line %d: SHCNE_UPDATEDIR: Calm down (%u)...\n", entry->line, s_cCalmDown);
537 
538         if (++s_cCalmDown < 3)
539             Sleep(3000);
540 
541         if (entry->pattern)
542             ok(TRUE, "Line %d:\n", entry->line);
543         if (entry->path1)
544             ok(TRUE, "Line %d:\n", entry->line);
545         if (entry->path2)
546             ok(TRUE, "Line %d:\n", entry->line);
547     }
548     else
549     {
550         s_cCalmDown = 0;
551         if (entry->pattern)
552         {
553             ok(strcmp(pattern, entry->pattern) == 0,
554                "Line %d: pattern mismatch '%s', tick=0x%08lX\n",
555                entry->line, pattern, GetTickCount());
556         }
557         if (entry->path1)
558             ok(bOK && lstrcmpiW(entry->path1, szPath1) == 0,
559                "Line %d: path1 mismatch '%S' (%d)\n", entry->line, szPath1, bOK);
560         if (entry->path2)
561             ok(bOK && lstrcmpiW(entry->path2, szPath2) == 0,
562                "Line %d: path2 mismatch '%S' (%d)\n", entry->line, szPath2, bOK);
563     }
564 
565     SendMessageW(s_hwnd, WM_CLEAR_FLAGS, 0, 0);
566     ResetEvent(s_hEvent);
567 
568 #ifdef ENTRY_TICK
569     DWORD dwNewTick = GetTickCount();
570     DWORD dwTick = dwNewTick - dwOldTick;
571     trace("DoTestEntry: Line %d: tick=%lu.%lu sec\n", entry->line,
572           (dwTick / 1000), (dwTick / 100 % 10));
573 #endif
574 }
575 
576 static void DoQuitTest(BOOL bForce)
577 {
578     PostMessageW(s_hwnd, WM_COMMAND, IDOK, 0);
579 
580     DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, bForce);
581     s_hwnd = NULL;
582 
583     if (s_hEvent)
584     {
585         CloseHandle(s_hEvent);
586         s_hEvent = NULL;
587     }
588 
589     DoDeleteFilesAndDirs();
590 }
591 
592 static void DoAbortThread(void)
593 {
594     skip("Aborting the thread...\n");
595     if (s_hThread)
596     {
597         TerminateThread(s_hThread, -1);
598         s_hThread = NULL;
599     }
600 }
601 
602 static BOOL CALLBACK HandlerRoutine(DWORD dwCtrlType)
603 {
604     switch (dwCtrlType)
605     {
606         case CTRL_C_EVENT:
607         case CTRL_BREAK_EVENT:
608             DoAbortThread();
609             DoQuitTest(TRUE);
610             return TRUE;
611     }
612     return FALSE;
613 }
614 
615 static BOOL DoInitTest(void)
616 {
617     // DIRTYPE_DESKTOP
618     LPWSTR psz = DoGetDir(DIRTYPE_DESKTOP);
619     StringCchCopyW(s_szDesktop, _countof(s_szDesktop), psz);
620 
621     PathAppendW(psz, TEST_FILE);
622     StringCchCopyW(s_szTestFile0, _countof(s_szTestFile0), psz);
623 
624     PathRemoveFileSpecW(psz);
625     PathAppendW(psz, TEST_FILE_KAI);
626     StringCchCopyW(s_szTestFile0Kai, _countof(s_szTestFile0Kai), psz);
627 
628     PathRemoveFileSpecW(psz);
629     PathAppendW(psz, TEST_DIR);
630     StringCchCopyW(s_szTestDir0, _countof(s_szTestDir0), psz);
631 
632     PathRemoveFileSpecW(psz);
633     PathAppendW(psz, TEST_DIR_KAI);
634     StringCchCopyW(s_szTestDir0Kai, _countof(s_szTestDir0Kai), psz);
635 
636     // DIRTYPE_MYDOCUMENTS
637     psz = DoGetDir(DIRTYPE_MYDOCUMENTS);
638     StringCchCopyW(s_szDocuments, _countof(s_szDocuments), psz);
639 
640     PathAppendW(psz, TEST_FILE);
641     StringCchCopyW(s_szTestFile1, _countof(s_szTestFile1), psz);
642 
643     PathRemoveFileSpecW(psz);
644     PathAppendW(psz, TEST_FILE_KAI);
645     StringCchCopyW(s_szTestFile1Kai, _countof(s_szTestFile1Kai), psz);
646 
647     PathRemoveFileSpecW(psz);
648     PathAppendW(psz, TEST_DIR);
649     StringCchCopyW(s_szTestDir1, _countof(s_szTestDir1), psz);
650 
651     PathRemoveFileSpecW(psz);
652     PathAppendW(psz, TEST_DIR_KAI);
653     StringCchCopyW(s_szTestDir1Kai, _countof(s_szTestDir1Kai), psz);
654 
655     // prepare for files and dirs
656     DoDeleteFilesAndDirs();
657     DoCreateEmptyFile(TEMP_FILE);
658 
659     // Ctrl+C
660     SetConsoleCtrlHandler(HandlerRoutine, TRUE);
661 
662     // close Explorer windows
663     trace("Closing Explorer windows...\n");
664     DoWaitForWindow(L"CabinetWClass", NULL, TRUE, TRUE);
665 
666     // close the CLASSNAME windows
667     return DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE) == NULL;
668 }
669 
670 static BOOL
671 GetSubProgramPath(void)
672 {
673     GetModuleFileNameW(NULL, s_szSubProgram, _countof(s_szSubProgram));
674     PathRemoveFileSpecW(s_szSubProgram);
675     PathAppendW(s_szSubProgram, L"shell32_apitest_sub.exe");
676 
677     if (!PathFileExistsW(s_szSubProgram))
678     {
679         PathRemoveFileSpecW(s_szSubProgram);
680         PathAppendW(s_szSubProgram, L"testdata\\shell32_apitest_sub.exe");
681 
682         if (!PathFileExistsW(s_szSubProgram))
683             return FALSE;
684     }
685 
686     return TRUE;
687 }
688 
689 #define SRC_00 0
690 #define SRC_01 SHCNRF_ShellLevel
691 #define SRC_02 (SHCNRF_NewDelivery)
692 #define SRC_03 (SHCNRF_NewDelivery | SHCNRF_ShellLevel)
693 #define SRC_04 SHCNRF_InterruptLevel
694 #define SRC_05 (SHCNRF_InterruptLevel | SHCNRF_ShellLevel)
695 #define SRC_06 (SHCNRF_InterruptLevel | SHCNRF_NewDelivery)
696 #define SRC_07 (SHCNRF_InterruptLevel | SHCNRF_NewDelivery | SHCNRF_ShellLevel)
697 #define SRC_08 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel)
698 #define SRC_09 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_ShellLevel)
699 #define SRC_10 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_NewDelivery)
700 #define SRC_11 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_NewDelivery | SHCNRF_ShellLevel)
701 
702 #define WATCHDIR_0 DIRTYPE_NULL
703 #define WATCHDIR_1 DIRTYPE_DESKTOP
704 #define WATCHDIR_2 DIRTYPE_MYCOMPUTER
705 #define WATCHDIR_3 DIRTYPE_MYDOCUMENTS
706 
707 static void
708 DoTestGroup(INT line, UINT cEntries, const TEST_ENTRY *pEntries, BOOL fRecursive,
709             INT nSources, DIRTYPE iWatchDir)
710 {
711 #ifdef NO_INTERRUPT_LEVEL
712     if (nSources & SHCNRF_InterruptLevel)
713         return;
714 #endif
715 #ifdef NO_SHELL_LEVEL
716     if (nSources & SHCNRF_ShellLevel)
717         return;
718 #endif
719 #ifdef NEW_DELIVERY_ONLY
720     if (!(nSources & SHCNRF_NewDelivery))
721         return;
722 #endif
723 #ifdef GROUP_TICK
724     DWORD dwOldTick = GetTickCount();
725 #endif
726 #ifdef RANDOM_QUARTER
727     if ((rand() & 3) == 0)
728         return;
729 #elif defined(RANDOM_HALF)
730     if (rand() & 1)
731         return;
732 #endif
733 
734     trace("DoTestGroup: Line %d: fRecursive:%u, iWatchDir:%u, nSources:0x%X\n",
735           line, fRecursive, iWatchDir, nSources);
736 
737     if (s_hEvent)
738     {
739         CloseHandle(s_hEvent);
740         s_hEvent = NULL;
741     }
742     s_hEvent = CreateEventW(NULL, TRUE, FALSE, EVENT_NAME);
743 
744     WCHAR szParams[64];
745     StringCchPrintfW(szParams, _countof(szParams), L"%u,%u,%u", fRecursive, iWatchDir, nSources);
746 
747     HINSTANCE hinst = ShellExecuteW(NULL, NULL, s_szSubProgram, szParams, NULL, SW_SHOWNORMAL);
748     if ((INT_PTR)hinst <= 32)
749     {
750         skip("Unable to run shell32_apitest_sub.exe.\n");
751         return;
752     }
753 
754     s_hwnd = DoWaitForWindow(CLASSNAME, CLASSNAME, FALSE, FALSE);
755     if (!s_hwnd)
756     {
757         skip("Unable to find window.\n");
758         return;
759     }
760 
761     for (UINT i = 0; i < cEntries; ++i)
762     {
763         if (!IsWindow(s_hwnd))
764         {
765             DoAbortThread();
766             DoQuitTest(TRUE);
767             break;
768         }
769 
770         DoTestEntry(i, &pEntries[i], nSources);
771     }
772 
773     DoQuitTest(FALSE);
774 
775 #ifdef GROUP_TICK
776     DWORD dwNewTick = GetTickCount();
777     DWORD dwTick = dwNewTick - dwOldTick;
778     trace("DoTestGroup: Line %d: %lu.%lu sec\n", line, (dwTick / 1000), (dwTick / 100 % 10));
779 #endif
780 }
781 
782 static unsigned __stdcall TestThreadProc(void *)
783 {
784     srand(time(NULL));
785 #ifdef RANDOM_QUARTER
786     skip("RANDOM_QUARTER\n");
787 #elif defined(RANDOM_HALF)
788     skip("RANDOM_HALF\n");
789 #endif
790 
791     // fRecursive == FALSE.
792     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_0);
793     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, FALSE, SRC_01, WATCHDIR_0);
794     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_0);
795     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, FALSE, SRC_03, WATCHDIR_0);
796     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_04, WATCHDIR_0);
797     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_05, WATCHDIR_0);
798     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_06, WATCHDIR_0);
799     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_07, WATCHDIR_0);
800     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_08, WATCHDIR_0);
801     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_09, WATCHDIR_0);
802     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_10, WATCHDIR_0);
803     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_11, WATCHDIR_0);
804 
805     BOOL bTarget = IsWindowsXPOrGreater() && !IsWindowsVistaOrGreater();
806 
807 #define SWITCH(x, y) (bTarget ? (x) : (y))
808     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_1);
809     DoTestGroup(__LINE__, _countof(s_group_03), s_group_03, FALSE, SRC_01, WATCHDIR_1);
810     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_1);
811     DoTestGroup(__LINE__, _countof(s_group_03), s_group_03, FALSE, SRC_03, WATCHDIR_1);
812     DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_04, WATCHDIR_1);
813     DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_05, WATCHDIR_1);
814     DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_06, WATCHDIR_1);
815     DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_07, WATCHDIR_1);
816     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_08, WATCHDIR_1);
817     DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_09, WATCHDIR_1);
818     DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_06, WATCHDIR_1);
819     DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_11, WATCHDIR_1);
820 #undef SWITCH
821 
822 #ifndef NO_TRIVIAL
823     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_2);
824     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_01, WATCHDIR_2);
825     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_2);
826     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_03, WATCHDIR_2);
827     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_04, WATCHDIR_2);
828     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_05, WATCHDIR_2);
829     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_06, WATCHDIR_2);
830     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_07, WATCHDIR_2);
831     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_08, WATCHDIR_2);
832     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_09, WATCHDIR_2);
833     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_10, WATCHDIR_2);
834     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_11, WATCHDIR_2);
835 #endif
836 
837     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_3);
838     DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, FALSE, SRC_01, WATCHDIR_3);
839     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_3);
840     DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, FALSE, SRC_03, WATCHDIR_3);
841     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_04, WATCHDIR_3);
842     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_05, WATCHDIR_3);
843     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_06, WATCHDIR_3);
844     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_07, WATCHDIR_3);
845     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_08, WATCHDIR_3);
846     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_09, WATCHDIR_3);
847     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_10, WATCHDIR_3);
848     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_11, WATCHDIR_3);
849 
850     // fRecursive == TRUE.
851     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_0);
852     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_0);
853     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_0);
854     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_0);
855     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_0);
856     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_0);
857     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_0);
858     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_0);
859     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_0);
860     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_0);
861     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_0);
862     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_0);
863 
864     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_1);
865     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_1);
866     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_1);
867     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_1);
868     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_1);
869     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_1);
870     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_1);
871     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_1);
872     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_1);
873     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_1);
874     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_1);
875     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_1);
876 
877     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_2);
878     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_2);
879     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_2);
880     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_2);
881     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_2);
882     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_2);
883     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_2);
884     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_2);
885     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_2);
886     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_2);
887     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_2);
888     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_2);
889 
890     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_3);
891     DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, TRUE, SRC_01, WATCHDIR_3);
892     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_3);
893     DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, TRUE, SRC_03, WATCHDIR_3);
894     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_04, WATCHDIR_3);
895     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_05, WATCHDIR_3);
896     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_06, WATCHDIR_3);
897     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_07, WATCHDIR_3);
898     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_08, WATCHDIR_3);
899     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_09, WATCHDIR_3);
900     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_10, WATCHDIR_3);
901     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_11, WATCHDIR_3);
902 
903     return 0;
904 }
905 
906 START_TEST(SHChangeNotify)
907 {
908 #ifdef TOTAL_TICK
909     DWORD dwOldTick = GetTickCount();
910 #endif
911 #ifdef DISABLE_THIS_TESTCASE
912     skip("This testcase is disabled by DISABLE_THIS_TESTCASE macro.\n");
913     return;
914 #endif
915 
916     trace("Please don't operate your PC while testing...\n");
917 
918     if (!GetSubProgramPath())
919     {
920         skip("shell32_apitest_sub.exe not found\n");
921         return;
922     }
923 
924     if (!DoInitTest())
925     {
926         skip("Unable to initialize.\n");
927         DoQuitTest(TRUE);
928         return;
929     }
930 
931     s_hThread = (HANDLE)_beginthreadex(NULL, 0, TestThreadProc, NULL, 0, NULL);
932     WaitForSingleObject(s_hThread, INFINITE);
933     CloseHandle(s_hThread);
934 
935 #ifdef TOTAL_TICK
936     DWORD dwNewTick = GetTickCount();
937     DWORD dwTick = dwNewTick - dwOldTick;
938     trace("SHChangeNotify: Total %lu.%lu sec\n", (dwTick / 1000), (dwTick / 100 % 10));
939 #endif
940 
941     DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE);
942     Sleep(100);
943 }
944