[INETCOMM_WINETEST] Sync with Wine Staging 2.2. CORE-12823
[reactos.git] / rostests / winetests / kernel32 / change.c
1 /*
2 * Tests for file change notification functions
3 *
4 * Copyright (c) 2004 Hans Leidekker
5 * Copyright 2006 Mike McCormack for CodeWeavers
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 /* TODO: - security attribute changes
23 * - compound filter and multiple notifications
24 * - subtree notifications
25 * - non-documented flags FILE_NOTIFY_CHANGE_LAST_ACCESS and
26 * FILE_NOTIFY_CHANGE_CREATION
27 */
28
29 #include <stdarg.h>
30 #include <stdio.h>
31
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "wine/test.h"
35 #include <windef.h>
36 #include <winbase.h>
37 #include <wine/winternl.h>
38
39 static DWORD CALLBACK NotificationThread(LPVOID arg)
40 {
41 HANDLE change = arg;
42 BOOL notified = FALSE;
43 BOOL ret = FALSE;
44 DWORD status;
45
46 status = WaitForSingleObject(change, 100);
47
48 if (status == WAIT_OBJECT_0 ) {
49 notified = TRUE;
50 FindNextChangeNotification(change);
51 }
52
53 ret = FindCloseChangeNotification(change);
54 ok( ret, "FindCloseChangeNotification error: %d\n",
55 GetLastError());
56
57 ExitThread((DWORD)notified);
58 }
59
60 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
61 {
62 HANDLE change, thread;
63 DWORD threadId;
64
65 change = FindFirstChangeNotificationA(path, subtree, flags);
66 ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
67
68 thread = CreateThread(NULL, 0, NotificationThread, change, 0, &threadId);
69 ok(thread != NULL, "CreateThread error: %d\n", GetLastError());
70
71 return thread;
72 }
73
74 static DWORD FinishNotificationThread(HANDLE thread)
75 {
76 DWORD status, exitcode;
77
78 status = WaitForSingleObject(thread, 5000);
79 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\n", status, GetLastError());
80
81 ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
82 CloseHandle(thread);
83
84 return exitcode;
85 }
86
87 static void test_FindFirstChangeNotification(void)
88 {
89 HANDLE change, file, thread;
90 DWORD attributes, count;
91 BOOL ret;
92
93 char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
94 char filename1[MAX_PATH], filename2[MAX_PATH];
95 static const char prefix[] = "FCN";
96 char buffer[2048];
97
98 /* pathetic checks */
99
100 change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
101 ok(change == INVALID_HANDLE_VALUE, "Expected INVALID_HANDLE_VALUE, got %p\n", change);
102 ok(GetLastError() == ERROR_FILE_NOT_FOUND ||
103 GetLastError() == ERROR_NO_MORE_FILES, /* win95 */
104 "FindFirstChangeNotification error: %d\n", GetLastError());
105
106 if (0) /* This documents win2k behavior. It crashes on win98. */
107 {
108 change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
109 ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
110 "FindFirstChangeNotification error: %d\n", GetLastError());
111 }
112
113 ret = FindNextChangeNotification(NULL);
114 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n",
115 GetLastError());
116
117 ret = FindCloseChangeNotification(NULL);
118 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
119 GetLastError());
120
121 ret = GetTempPathA(MAX_PATH, workdir);
122 ok(ret, "GetTempPathA error: %d\n", GetLastError());
123
124 lstrcatA(workdir, "testFileChangeNotification");
125
126 ret = CreateDirectoryA(workdir, NULL);
127 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
128
129 ret = GetTempFileNameA(workdir, prefix, 0, filename1);
130 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
131
132 file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
133 FILE_ATTRIBUTE_NORMAL, 0);
134 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
135 ret = CloseHandle(file);
136 ok( ret, "CloseHandle error: %d\n", GetLastError());
137
138 /* Try to register notification for a file. win98 and win2k behave differently here */
139 change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
140 ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
141 GetLastError() == ERROR_FILE_NOT_FOUND),
142 "FindFirstChangeNotification error: %d\n", GetLastError());
143
144 lstrcpyA(dirname1, filename1);
145 lstrcatA(dirname1, "dir");
146
147 lstrcpyA(dirname2, dirname1);
148 lstrcatA(dirname2, "new");
149
150 ret = CreateDirectoryA(dirname1, NULL);
151 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
152
153 /* What if we move the directory we registered notification for? */
154 thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
155 ret = MoveFileA(dirname1, dirname2);
156 ok(ret, "MoveFileA error: %d\n", GetLastError());
157 /* win9x and win2k behave differently here, don't check result */
158 FinishNotificationThread(thread);
159
160 /* What if we remove the directory we registered notification for? */
161 thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
162 ret = RemoveDirectoryA(dirname2);
163 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
164 /* win9x and win2k behave differently here, don't check result */
165 FinishNotificationThread(thread);
166
167 /* functional checks */
168
169 /* Create a directory */
170 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
171 ret = CreateDirectoryA(dirname1, NULL);
172 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
173 ok(FinishNotificationThread(thread), "Missed notification\n");
174
175 /* Rename a directory */
176 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
177 ret = MoveFileA(dirname1, dirname2);
178 ok(ret, "MoveFileA error: %d\n", GetLastError());
179 ok(FinishNotificationThread(thread), "Missed notification\n");
180
181 /* Delete a directory */
182 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
183 ret = RemoveDirectoryA(dirname2);
184 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
185 ok(FinishNotificationThread(thread), "Missed notification\n");
186
187 lstrcpyA(filename2, filename1);
188 lstrcatA(filename2, "new");
189
190 /* Rename a file */
191 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
192 ret = MoveFileA(filename1, filename2);
193 ok(ret, "MoveFileA error: %d\n", GetLastError());
194 ok(FinishNotificationThread(thread), "Missed notification\n");
195
196 /* Delete a file */
197 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
198 ret = DeleteFileA(filename2);
199 ok(ret, "DeleteFileA error: %d\n", GetLastError());
200 ok(FinishNotificationThread(thread), "Missed notification\n");
201
202 /* Create a file */
203 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
204 file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
205 FILE_ATTRIBUTE_NORMAL, 0);
206 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
207 ret = CloseHandle(file);
208 ok( ret, "CloseHandle error: %d\n", GetLastError());
209 ok(FinishNotificationThread(thread), "Missed notification\n");
210
211 attributes = GetFileAttributesA(filename2);
212 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError());
213 attributes &= FILE_ATTRIBUTE_READONLY;
214
215 /* Change file attributes */
216 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
217 ret = SetFileAttributesA(filename2, attributes);
218 ok(ret, "SetFileAttributesA error: %d\n", GetLastError());
219 ok(FinishNotificationThread(thread), "Missed notification\n");
220
221 /* Change last write time by writing to a file */
222 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
223 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
224 FILE_ATTRIBUTE_NORMAL, 0);
225 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
226 memset(buffer, 0, sizeof(buffer));
227 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
228 ok(ret && count == sizeof(buffer), "WriteFile error: %d\n", GetLastError());
229 ret = CloseHandle(file);
230 ok( ret, "CloseHandle error: %d\n", GetLastError());
231 ok(FinishNotificationThread(thread), "Missed notification\n");
232
233 /* Change file size by truncating a file */
234 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
235 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
236 FILE_ATTRIBUTE_NORMAL, 0);
237 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
238 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
239 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %d\n", GetLastError());
240 ret = CloseHandle(file);
241 ok( ret, "CloseHandle error: %d\n", GetLastError());
242 ok(FinishNotificationThread(thread), "Missed notification\n");
243
244 /* clean up */
245
246 ret = DeleteFileA(filename2);
247 ok(ret, "DeleteFileA error: %d\n", GetLastError());
248
249 ret = RemoveDirectoryA(workdir);
250 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
251 }
252
253 /* this test concentrates more on the wait behaviour of the handle */
254 static void test_ffcn(void)
255 {
256 DWORD filter;
257 HANDLE handle;
258 LONG r;
259 WCHAR path[MAX_PATH], subdir[MAX_PATH];
260 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
261 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
262
263 SetLastError(0xdeadbeef);
264 r = GetTempPathW( MAX_PATH, path );
265 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
266 {
267 win_skip("GetTempPathW is not implemented\n");
268 return;
269 }
270 ok( r != 0, "temp path failed\n");
271 if (!r)
272 return;
273
274 lstrcatW( path, szBoo );
275 lstrcpyW( subdir, path );
276 lstrcatW( subdir, szHoo );
277
278 RemoveDirectoryW( subdir );
279 RemoveDirectoryW( path );
280
281 r = CreateDirectoryW(path, NULL);
282 ok( r == TRUE, "failed to create directory\n");
283
284 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
285 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
286
287 handle = FindFirstChangeNotificationW( path, 1, filter);
288 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
289
290 r = WaitForSingleObject( handle, 0 );
291 ok( r == STATUS_TIMEOUT, "should time out\n");
292
293 r = CreateDirectoryW( subdir, NULL );
294 ok( r == TRUE, "failed to create subdir\n");
295
296 r = WaitForSingleObject( handle, 0 );
297 ok( r == WAIT_OBJECT_0, "should be ready\n");
298
299 r = WaitForSingleObject( handle, 0 );
300 ok( r == WAIT_OBJECT_0, "should be ready\n");
301
302 r = FindNextChangeNotification(handle);
303 ok( r == TRUE, "find next failed\n");
304
305 r = WaitForSingleObject( handle, 0 );
306 ok( r == STATUS_TIMEOUT, "should time out\n");
307
308 r = RemoveDirectoryW( subdir );
309 ok( r == TRUE, "failed to remove subdir\n");
310
311 r = WaitForSingleObject( handle, 0 );
312 ok( r == WAIT_OBJECT_0, "should be ready\n");
313
314 r = WaitForSingleObject( handle, 0 );
315 ok( r == WAIT_OBJECT_0, "should be ready\n");
316
317 r = FindNextChangeNotification(handle);
318 ok( r == TRUE, "find next failed\n");
319
320 r = FindNextChangeNotification(handle);
321 ok( r == TRUE, "find next failed\n");
322
323 r = FindCloseChangeNotification(handle);
324 ok( r == TRUE, "should succeed\n");
325
326 r = RemoveDirectoryW( path );
327 ok( r == TRUE, "failed to remove dir\n");
328 }
329
330 /* this test concentrates on the wait behavior when multiple threads are
331 * waiting on a change notification handle. */
332 static void test_ffcnMultipleThreads(void)
333 {
334 LONG r;
335 DWORD filter, threadId, status, exitcode;
336 HANDLE handles[2];
337 char path[MAX_PATH];
338
339 r = GetTempPathA(MAX_PATH, path);
340 ok(r, "GetTempPathA error: %d\n", GetLastError());
341
342 lstrcatA(path, "ffcnTestMultipleThreads");
343
344 RemoveDirectoryA(path);
345
346 r = CreateDirectoryA(path, NULL);
347 ok(r, "CreateDirectoryA error: %d\n", GetLastError());
348
349 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
350 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
351
352 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
353 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
354
355 /* Test behavior if a waiting thread holds the last reference to a change
356 * directory object with an empty wine user APC queue for this thread (bug #7286) */
357
358 /* Create our notification thread */
359 handles[1] = CreateThread(NULL, 0, NotificationThread, handles[0], 0,
360 &threadId);
361 ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
362
363 status = WaitForMultipleObjects(2, handles, FALSE, 5000);
364 ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError());
365 ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
366
367 /* Clean up */
368 r = RemoveDirectoryA( path );
369 ok( r == TRUE, "failed to remove dir\n");
370 }
371
372 static BOOL (WINAPI *pReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
373 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
374
375 static void test_readdirectorychanges(void)
376 {
377 HANDLE hdir;
378 char buffer[0x1000];
379 DWORD fflags, filter = 0, r, dwCount;
380 OVERLAPPED ov;
381 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
382 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
383 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
384 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
385 PFILE_NOTIFY_INFORMATION pfni;
386
387 if (!pReadDirectoryChangesW)
388 {
389 win_skip("ReadDirectoryChangesW is not available\n");
390 return;
391 }
392
393 SetLastError(0xdeadbeef);
394 r = GetTempPathW( MAX_PATH, path );
395 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
396 {
397 win_skip("GetTempPathW is not implemented\n");
398 return;
399 }
400 ok( r != 0, "temp path failed\n");
401 if (!r)
402 return;
403
404 lstrcatW( path, szBoo );
405 lstrcpyW( subdir, path );
406 lstrcatW( subdir, szHoo );
407
408 lstrcpyW( subsubdir, path );
409 lstrcatW( subsubdir, szGa );
410
411 RemoveDirectoryW( subsubdir );
412 RemoveDirectoryW( subdir );
413 RemoveDirectoryW( path );
414
415 r = CreateDirectoryW(path, NULL);
416 ok( r == TRUE, "failed to create directory\n");
417
418 SetLastError(0xd0b00b00);
419 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
420 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
421 ok(r==FALSE, "should return false\n");
422
423 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
424 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
425 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
426 OPEN_EXISTING, fflags, NULL);
427 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
428
429 ov.hEvent = CreateEventW( NULL, 1, 0, NULL );
430
431 SetLastError(0xd0b00b00);
432 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
433 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
434 ok(r==FALSE, "should return false\n");
435
436 SetLastError(0xd0b00b00);
437 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
438 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
439 ok(r==FALSE, "should return false\n");
440
441 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
442 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
443 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
444 filter |= FILE_NOTIFY_CHANGE_SIZE;
445 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
446 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
447 filter |= FILE_NOTIFY_CHANGE_CREATION;
448 filter |= FILE_NOTIFY_CHANGE_SECURITY;
449
450 SetLastError(0xd0b00b00);
451 ov.Internal = 0;
452 ov.InternalHigh = 0;
453 memset( buffer, 0, sizeof buffer );
454
455 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
456 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
457 ok(r==FALSE, "should return false\n");
458
459 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
460 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
461 ok(r==FALSE, "should return false\n");
462
463 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
464 ok(r==TRUE, "should return true\n");
465
466 r = WaitForSingleObject( ov.hEvent, 10 );
467 ok( r == STATUS_TIMEOUT, "should timeout\n" );
468
469 r = CreateDirectoryW( subdir, NULL );
470 ok( r == TRUE, "failed to create directory\n");
471
472 r = WaitForSingleObject( ov.hEvent, 1000 );
473 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
474
475 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
476 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
477
478 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
479 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
480 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
481 ok( pfni->FileNameLength == 6, "len wrong\n" );
482 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
483
484 ResetEvent(ov.hEvent);
485 SetLastError(0xd0b00b00);
486 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
487 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
488 ok(r==FALSE, "should return false\n");
489
490 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
491 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
492 ok(r==FALSE, "should return false\n");
493
494 filter = FILE_NOTIFY_CHANGE_SIZE;
495
496 SetEvent(ov.hEvent);
497 ov.Internal = 1;
498 ov.InternalHigh = 1;
499 S(U(ov)).Offset = 0;
500 S(U(ov)).OffsetHigh = 0;
501 memset( buffer, 0, sizeof buffer );
502 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
503 ok(r==TRUE, "should return true\n");
504
505 ok( (NTSTATUS)ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
506 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
507
508 r = WaitForSingleObject( ov.hEvent, 0 );
509 ok( r == STATUS_TIMEOUT, "should timeout\n" );
510
511 r = RemoveDirectoryW( subdir );
512 ok( r == TRUE, "failed to remove directory\n");
513
514 r = WaitForSingleObject( ov.hEvent, 1000 );
515 ok( r == WAIT_OBJECT_0, "should be ready\n" );
516
517 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
518 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
519
520 if ((NTSTATUS)ov.Internal == STATUS_SUCCESS)
521 {
522 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
523 ok( r == TRUE, "getoverlappedresult failed\n");
524 ok( dwCount == 0x12, "count wrong\n");
525 }
526
527 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
528 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
529 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
530 ok( pfni->FileNameLength == 6, "len wrong\n" );
531 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
532
533 /* what happens if the buffer is too small? */
534 r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
535 ok(r==TRUE, "should return true\n");
536
537 r = CreateDirectoryW( subdir, NULL );
538 ok( r == TRUE, "failed to create directory\n");
539
540 r = WaitForSingleObject( ov.hEvent, 1000 );
541 ok( r == WAIT_OBJECT_0, "should be ready\n" );
542
543 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
544 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
545
546 /* test the recursive watch */
547 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
548 ok(r==TRUE, "should return true\n");
549
550 r = CreateDirectoryW( subsubdir, NULL );
551 ok( r == TRUE, "failed to create directory\n");
552
553 r = WaitForSingleObject( ov.hEvent, 1000 );
554 ok( r == WAIT_OBJECT_0, "should be ready\n" );
555
556 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
557 ok( ov.InternalHigh == 0x18 || ov.InternalHigh == 0x12 + 0x18,
558 "ov.InternalHigh wrong %lx\n", ov.InternalHigh);
559
560 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
561 if (pfni->NextEntryOffset) /* we may get a modified event on the parent dir */
562 {
563 ok( pfni->NextEntryOffset == 0x12, "offset wrong %x\n", pfni->NextEntryOffset );
564 ok( pfni->Action == FILE_ACTION_MODIFIED, "action wrong %d\n", pfni->Action );
565 ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong\n" );
566 ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n");
567 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
568 }
569 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
570 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
571 ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong\n" );
572 ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
573
574 r = RemoveDirectoryW( subsubdir );
575 ok( r == TRUE, "failed to remove directory\n");
576
577 ov.Internal = 1;
578 ov.InternalHigh = 1;
579 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
580 ok(r==TRUE, "should return true\n");
581
582 r = RemoveDirectoryW( subdir );
583 ok( r == TRUE, "failed to remove directory\n");
584
585 r = WaitForSingleObject( ov.hEvent, 1000 );
586 ok( r == WAIT_OBJECT_0, "should be ready\n" );
587
588 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
589 /* we may get a notification for the parent dir too */
590 if (pfni->Action == FILE_ACTION_MODIFIED && pfni->NextEntryOffset)
591 {
592 ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
593 ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n" );
594 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
595 }
596 ok( pfni->NextEntryOffset == 0, "offset wrong %u\n", pfni->NextEntryOffset );
597 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong %u\n", pfni->Action );
598 ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
599 ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
600
601 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
602 dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer;
603 ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %lu/%u\n",ov.InternalHigh, dwCount );
604
605 CloseHandle(hdir);
606
607 r = RemoveDirectoryW( path );
608 ok( r == TRUE, "failed to remove directory\n");
609 }
610
611 /* show the behaviour when a null buffer is passed */
612 static void test_readdirectorychanges_null(void)
613 {
614 NTSTATUS r;
615 HANDLE hdir;
616 char buffer[0x1000];
617 DWORD fflags, filter = 0;
618 OVERLAPPED ov;
619 WCHAR path[MAX_PATH], subdir[MAX_PATH];
620 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
621 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
622 PFILE_NOTIFY_INFORMATION pfni;
623
624 if (!pReadDirectoryChangesW)
625 {
626 win_skip("ReadDirectoryChangesW is not available\n");
627 return;
628 }
629 SetLastError(0xdeadbeef);
630 r = GetTempPathW( MAX_PATH, path );
631 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
632 {
633 win_skip("GetTempPathW is not implemented\n");
634 return;
635 }
636 ok( r != 0, "temp path failed\n");
637 if (!r)
638 return;
639
640 lstrcatW( path, szBoo );
641 lstrcpyW( subdir, path );
642 lstrcatW( subdir, szHoo );
643
644 RemoveDirectoryW( subdir );
645 RemoveDirectoryW( path );
646
647 r = CreateDirectoryW(path, NULL);
648 ok( r == TRUE, "failed to create directory\n");
649
650 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
651 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
652 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
653 OPEN_EXISTING, fflags, NULL);
654 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
655
656 ov.hEvent = CreateEventW( NULL, 1, 0, NULL );
657
658 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
659 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
660
661 SetLastError(0xd0b00b00);
662 ov.Internal = 0;
663 ov.InternalHigh = 0;
664 memset( buffer, 0, sizeof buffer );
665
666 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
667 ok(r==TRUE, "should return true\n");
668
669 r = WaitForSingleObject( ov.hEvent, 0 );
670 ok( r == STATUS_TIMEOUT, "should timeout\n" );
671
672 r = CreateDirectoryW( subdir, NULL );
673 ok( r == TRUE, "failed to create directory\n");
674
675 r = WaitForSingleObject( ov.hEvent, 0 );
676 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
677
678 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
679 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
680
681 ov.Internal = 0;
682 ov.InternalHigh = 0;
683 S(U(ov)).Offset = 0;
684 S(U(ov)).OffsetHigh = 0;
685 memset( buffer, 0, sizeof buffer );
686
687 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
688 ok(r==TRUE, "should return true\n");
689
690 r = WaitForSingleObject( ov.hEvent, 0 );
691 ok( r == STATUS_TIMEOUT, "should timeout\n" );
692
693 r = RemoveDirectoryW( subdir );
694 ok( r == TRUE, "failed to remove directory\n");
695
696 r = WaitForSingleObject( ov.hEvent, 1000 );
697 ok( r == WAIT_OBJECT_0, "should be ready\n" );
698
699 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
700 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
701
702 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
703 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
704
705 CloseHandle(hdir);
706
707 r = RemoveDirectoryW( path );
708 ok( r == TRUE, "failed to remove directory\n");
709 }
710
711 static void test_readdirectorychanges_filedir(void)
712 {
713 NTSTATUS r;
714 HANDLE hdir, hfile;
715 char buffer[0x1000];
716 DWORD fflags, filter = 0;
717 OVERLAPPED ov;
718 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
719 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
720 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
721 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
722 PFILE_NOTIFY_INFORMATION pfni;
723
724 SetLastError(0xdeadbeef);
725 r = GetTempPathW( MAX_PATH, path );
726 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
727 {
728 win_skip("GetTempPathW is not implemented\n");
729 return;
730 }
731 ok( r != 0, "temp path failed\n");
732 if (!r)
733 return;
734
735 lstrcatW( path, szBoo );
736 lstrcpyW( subdir, path );
737 lstrcatW( subdir, szHoo );
738
739 lstrcpyW( file, path );
740 lstrcatW( file, szFoo );
741
742 DeleteFileW( file );
743 RemoveDirectoryW( subdir );
744 RemoveDirectoryW( path );
745
746 r = CreateDirectoryW(path, NULL);
747 ok( r == TRUE, "failed to create directory\n");
748
749 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
750 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
751 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
752 OPEN_EXISTING, fflags, NULL);
753 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
754
755 ov.hEvent = CreateEventW( NULL, 0, 0, NULL );
756
757 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
758
759 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
760 ok(r==TRUE, "should return true\n");
761
762 r = WaitForSingleObject( ov.hEvent, 10 );
763 ok( r == WAIT_TIMEOUT, "should timeout\n" );
764
765 r = CreateDirectoryW( subdir, NULL );
766 ok( r == TRUE, "failed to create directory\n");
767
768 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
769 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
770 ok( CloseHandle(hfile), "failed to close file\n");
771
772 r = WaitForSingleObject( ov.hEvent, 1000 );
773 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
774
775 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
776 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
777
778 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
779 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
780 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
781 ok( pfni->FileNameLength == 6, "len wrong\n" );
782 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
783
784 r = DeleteFileW( file );
785 ok( r == TRUE, "failed to delete file\n");
786
787 r = RemoveDirectoryW( subdir );
788 ok( r == TRUE, "failed to remove directory\n");
789
790 CloseHandle(hdir);
791
792 r = RemoveDirectoryW( path );
793 ok( r == TRUE, "failed to remove directory\n");
794 }
795
796 static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov)
797 {
798 ok(error == 0, "ReadDirectoryChangesW error %d\n", error);
799 ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n");
800 }
801
802 static void test_readdirectorychanges_cr(void)
803 {
804 static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 };
805 static const WCHAR szDir[] = { 'd','i','r',0 };
806 static const WCHAR szFile[] = { 'f','i','l','e',0 };
807 static const WCHAR szBackslash[] = { '\\',0 };
808
809 WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH];
810 FILE_NOTIFY_INFORMATION fni[1024], *fni_next;
811 OVERLAPPED ov;
812 HANDLE hdir, hfile;
813 NTSTATUS r;
814
815 if (!pReadDirectoryChangesW)
816 {
817 win_skip("ReadDirectoryChangesW is not available\n");
818 return;
819 }
820
821 SetLastError(0xdeadbeef);
822 r = GetTempPathW(MAX_PATH, path);
823 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
824 {
825 win_skip("GetTempPathW is not implemented\n");
826 return;
827 }
828 ok(r != 0, "temp path failed\n");
829 if (!r)
830 return;
831
832 lstrcatW(path, szBoo);
833 lstrcpyW(dir, path);
834 lstrcatW(dir, szDir);
835 lstrcpyW(file, path);
836 lstrcatW(file, szFile);
837 lstrcpyW(sub_file, dir);
838 lstrcatW(sub_file, szBackslash);
839 lstrcatW(sub_file, szFile);
840
841 DeleteFileW(file);
842 RemoveDirectoryW(dir);
843 RemoveDirectoryW(path);
844
845 r = CreateDirectoryW(path, NULL);
846 ok(r == TRUE, "failed to create directory\n");
847
848 hdir = CreateFileW(path, GENERIC_READ,
849 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
850 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
851 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
852
853 memset(&ov, 0, sizeof(ov));
854 ov.hEvent = (void*)0xdeadbeef;
855 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
856 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
857 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
858
859 hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
860 ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
861 CloseHandle(hfile);
862
863 r = SleepEx(1000, TRUE);
864 ok(r != 0, "failed to receive file creation event\n");
865 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
866 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
867 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
868 "FileNameLength = %d\n", fni->FileNameLength);
869 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
870 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
871
872 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
873 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
874 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
875
876 /* This event will not be reported */
877 r = CreateDirectoryW(dir, NULL);
878 ok(r == TRUE, "failed to create directory\n");
879
880 r = MoveFileW(file, sub_file);
881 ok(r == TRUE, "failed to move file\n");
882
883 r = SleepEx(1000, TRUE);
884 ok(r != 0, "failed to receive file move event\n");
885 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
886 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
887 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
888 "FileNameLength = %d\n", fni->FileNameLength);
889 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
890 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
891
892 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
893 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
894 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
895
896 r = MoveFileW(sub_file, file);
897 ok(r == TRUE, "failed to move file\n");
898
899 r = SleepEx(1000, TRUE);
900 ok(r != 0, "failed to receive file move event\n");
901 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
902 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
903 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
904 "FileNameLength = %d\n", fni->FileNameLength);
905 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
906 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
907
908 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
909 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
910 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
911
912 r = DeleteFileW(file);
913 ok(r == TRUE, "failed to delete file\n");
914
915 r = SleepEx(1000, TRUE);
916 ok(r != 0, "failed to receive file removal event\n");
917 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
918 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
919 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
920 "FileNameLength = %d\n", fni->FileNameLength);
921 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
922 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
923
924 CloseHandle(hdir);
925
926 hdir = CreateFileW(path, GENERIC_READ,
927 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
928 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
929 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
930
931 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
932 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
933 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
934
935 r = MoveFileW(dir, file);
936 ok(r == TRUE, "failed to move directory\n");
937
938 r = SleepEx(1000, TRUE);
939 ok(r != 0, "failed to receive directory move event\n");
940 if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME)
941 {
942 ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %d\n", fni->Action);
943 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
944 "FileNameLength = %d\n", fni->FileNameLength);
945 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
946 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
947 ok(fni->NextEntryOffset != 0, "no next entry in movement event\n");
948 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
949 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
950 ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %d\n", fni_next->Action);
951 ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
952 "FileNameLength = %d\n", fni_next->FileNameLength);
953 ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
954 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR)));
955 }
956 else
957 {
958 todo_wine ok(0, "Expected rename event\n");
959
960 if (fni->NextEntryOffset == 0)
961 {
962 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
963 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
964 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
965
966 r = SleepEx(1000, TRUE);
967 ok(r != 0, "failed to receive directory move event\n");
968 }
969 }
970
971 r = CreateDirectoryW(dir, NULL);
972 ok(r == TRUE, "failed to create directory\n");
973
974 r = RemoveDirectoryW(dir);
975 ok(r == TRUE, "failed to remove directory\n");
976
977 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
978 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
979 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
980
981 r = SleepEx(1000, TRUE);
982 ok(r != 0, "failed to receive directory creation event\n");
983 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
984 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
985 "FileNameLength = %d\n", fni->FileNameLength);
986 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
987 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
988 if (fni->NextEntryOffset)
989 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
990 else
991 {
992 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
993 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
994 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
995
996 r = SleepEx(1000, TRUE);
997 ok(r != 0, "failed to receive directory removal event\n");
998 fni_next = fni;
999 }
1000 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
1001 ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni_next->Action);
1002 ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
1003 "FileNameLength = %d\n", fni_next->FileNameLength);
1004 ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
1005 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR)));
1006
1007 CloseHandle(hdir);
1008 RemoveDirectoryW(file);
1009 RemoveDirectoryW(path);
1010 }
1011
1012 static void test_ffcn_directory_overlap(void)
1013 {
1014 HANDLE parent_watch, child_watch, parent_thread, child_thread;
1015 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH];
1016 char tempfile[MAX_PATH];
1017 DWORD threadId;
1018 BOOL ret;
1019
1020 /* Setup directory hierarchy */
1021 ret = GetTempPathA(MAX_PATH, workdir);
1022 ok((ret > 0) && (ret <= MAX_PATH),
1023 "GetTempPathA error: %d\n", GetLastError());
1024
1025 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile);
1026 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1027 ret = DeleteFileA(tempfile);
1028 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1029
1030 lstrcpyA(parentdir, tempfile);
1031 ret = CreateDirectoryA(parentdir, NULL);
1032 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
1033
1034 lstrcpyA(childdir, parentdir);
1035 lstrcatA(childdir, "\\c");
1036 ret = CreateDirectoryA(childdir, NULL);
1037 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
1038
1039
1040 /* When recursively watching overlapping directories, changes in child
1041 * should trigger notifications for both child and parent */
1042 parent_thread = StartNotificationThread(parentdir, TRUE,
1043 FILE_NOTIFY_CHANGE_FILE_NAME);
1044 child_thread = StartNotificationThread(childdir, TRUE,
1045 FILE_NOTIFY_CHANGE_FILE_NAME);
1046
1047 /* Create a file in child */
1048 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1049 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1050
1051 /* Both watches should trigger */
1052 ret = FinishNotificationThread(parent_thread);
1053 ok(ret, "Missed parent notification\n");
1054 ret = FinishNotificationThread(child_thread);
1055 ok(ret, "Missed child notification\n");
1056
1057 ret = DeleteFileA(tempfile);
1058 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1059
1060
1061 /* Removing a recursive parent watch should not affect child watches. Doing
1062 * so used to crash wineserver. */
1063 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE,
1064 FILE_NOTIFY_CHANGE_FILE_NAME);
1065 ok(parent_watch != INVALID_HANDLE_VALUE,
1066 "FindFirstChangeNotification error: %d\n", GetLastError());
1067 child_watch = FindFirstChangeNotificationA(childdir, TRUE,
1068 FILE_NOTIFY_CHANGE_FILE_NAME);
1069 ok(child_watch != INVALID_HANDLE_VALUE,
1070 "FindFirstChangeNotification error: %d\n", GetLastError());
1071
1072 ret = FindCloseChangeNotification(parent_watch);
1073 ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError());
1074
1075 child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0,
1076 &threadId);
1077 ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError());
1078
1079 /* Create a file in child */
1080 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1081 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1082
1083 /* Child watch should trigger */
1084 ret = FinishNotificationThread(child_thread);
1085 ok(ret, "Missed child notification\n");
1086
1087 /* clean up */
1088 ret = DeleteFileA(tempfile);
1089 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1090
1091 ret = RemoveDirectoryA(childdir);
1092 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1093
1094 ret = RemoveDirectoryA(parentdir);
1095 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1096 }
1097
1098 START_TEST(change)
1099 {
1100 HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
1101 pReadDirectoryChangesW = (void *)GetProcAddress(hkernel32, "ReadDirectoryChangesW");
1102
1103 test_ffcnMultipleThreads();
1104 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
1105 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
1106 placed first. */
1107 test_FindFirstChangeNotification();
1108 test_ffcn();
1109 test_readdirectorychanges();
1110 test_readdirectorychanges_null();
1111 test_readdirectorychanges_filedir();
1112 test_readdirectorychanges_cr();
1113 test_ffcn_directory_overlap();
1114 }