[KERNEL32_WINETEST]
[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 <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 = CreateEvent( 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( 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( 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( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
518 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
519
520 if (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( 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( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
557 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
558
559 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
560 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
561 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
562 ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong\n" );
563 ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
564
565 r = RemoveDirectoryW( subsubdir );
566 ok( r == TRUE, "failed to remove directory\n");
567
568 ov.Internal = 1;
569 ov.InternalHigh = 1;
570 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
571 ok(r==TRUE, "should return true\n");
572
573 r = RemoveDirectoryW( subdir );
574 ok( r == TRUE, "failed to remove directory\n");
575
576 r = WaitForSingleObject( ov.hEvent, 1000 );
577 ok( r == WAIT_OBJECT_0, "should be ready\n" );
578
579 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
580 /* we may get a notification for the parent dir too */
581 if (pfni->Action == FILE_ACTION_MODIFIED && pfni->NextEntryOffset)
582 {
583 ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
584 ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n" );
585 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
586 }
587 ok( pfni->NextEntryOffset == 0, "offset wrong %u\n", pfni->NextEntryOffset );
588 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong %u\n", pfni->Action );
589 ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
590 ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
591
592 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
593 dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer;
594 ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %lu/%u\n",ov.InternalHigh, dwCount );
595
596 CloseHandle(hdir);
597
598 r = RemoveDirectoryW( path );
599 ok( r == TRUE, "failed to remove directory\n");
600 }
601
602 /* show the behaviour when a null buffer is passed */
603 static void test_readdirectorychanges_null(void)
604 {
605 NTSTATUS r;
606 HANDLE hdir;
607 char buffer[0x1000];
608 DWORD fflags, filter = 0;
609 OVERLAPPED ov;
610 WCHAR path[MAX_PATH], subdir[MAX_PATH];
611 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
612 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
613 PFILE_NOTIFY_INFORMATION pfni;
614
615 if (!pReadDirectoryChangesW)
616 {
617 win_skip("ReadDirectoryChangesW is not available\n");
618 return;
619 }
620 SetLastError(0xdeadbeef);
621 r = GetTempPathW( MAX_PATH, path );
622 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
623 {
624 win_skip("GetTempPathW is not implemented\n");
625 return;
626 }
627 ok( r != 0, "temp path failed\n");
628 if (!r)
629 return;
630
631 lstrcatW( path, szBoo );
632 lstrcpyW( subdir, path );
633 lstrcatW( subdir, szHoo );
634
635 RemoveDirectoryW( subdir );
636 RemoveDirectoryW( path );
637
638 r = CreateDirectoryW(path, NULL);
639 ok( r == TRUE, "failed to create directory\n");
640
641 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
642 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
643 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
644 OPEN_EXISTING, fflags, NULL);
645 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
646
647 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
648
649 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
650 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
651
652 SetLastError(0xd0b00b00);
653 ov.Internal = 0;
654 ov.InternalHigh = 0;
655 memset( buffer, 0, sizeof buffer );
656
657 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
658 ok(r==TRUE, "should return true\n");
659
660 r = WaitForSingleObject( ov.hEvent, 0 );
661 ok( r == STATUS_TIMEOUT, "should timeout\n" );
662
663 r = CreateDirectoryW( subdir, NULL );
664 ok( r == TRUE, "failed to create directory\n");
665
666 r = WaitForSingleObject( ov.hEvent, 0 );
667 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
668
669 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
670 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
671
672 ov.Internal = 0;
673 ov.InternalHigh = 0;
674 S(U(ov)).Offset = 0;
675 S(U(ov)).OffsetHigh = 0;
676 memset( buffer, 0, sizeof buffer );
677
678 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
679 ok(r==TRUE, "should return true\n");
680
681 r = WaitForSingleObject( ov.hEvent, 0 );
682 ok( r == STATUS_TIMEOUT, "should timeout\n" );
683
684 r = RemoveDirectoryW( subdir );
685 ok( r == TRUE, "failed to remove directory\n");
686
687 r = WaitForSingleObject( ov.hEvent, 1000 );
688 ok( r == WAIT_OBJECT_0, "should be ready\n" );
689
690 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
691 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
692
693 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
694 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
695
696 CloseHandle(hdir);
697
698 r = RemoveDirectoryW( path );
699 ok( r == TRUE, "failed to remove directory\n");
700 }
701
702 static void test_readdirectorychanges_filedir(void)
703 {
704 NTSTATUS r;
705 HANDLE hdir, hfile;
706 char buffer[0x1000];
707 DWORD fflags, filter = 0;
708 OVERLAPPED ov;
709 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
710 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
711 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
712 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
713 PFILE_NOTIFY_INFORMATION pfni;
714
715 SetLastError(0xdeadbeef);
716 r = GetTempPathW( MAX_PATH, path );
717 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
718 {
719 win_skip("GetTempPathW is not implemented\n");
720 return;
721 }
722 ok( r != 0, "temp path failed\n");
723 if (!r)
724 return;
725
726 lstrcatW( path, szBoo );
727 lstrcpyW( subdir, path );
728 lstrcatW( subdir, szHoo );
729
730 lstrcpyW( file, path );
731 lstrcatW( file, szFoo );
732
733 DeleteFileW( file );
734 RemoveDirectoryW( subdir );
735 RemoveDirectoryW( path );
736
737 r = CreateDirectoryW(path, NULL);
738 ok( r == TRUE, "failed to create directory\n");
739
740 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
741 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
742 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
743 OPEN_EXISTING, fflags, NULL);
744 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
745
746 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
747
748 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
749
750 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
751 ok(r==TRUE, "should return true\n");
752
753 r = WaitForSingleObject( ov.hEvent, 10 );
754 ok( r == WAIT_TIMEOUT, "should timeout\n" );
755
756 r = CreateDirectoryW( subdir, NULL );
757 ok( r == TRUE, "failed to create directory\n");
758
759 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
760 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
761 ok( CloseHandle(hfile), "failed toc lose file\n");
762
763 r = WaitForSingleObject( ov.hEvent, 1000 );
764 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
765
766 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
767 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
768
769 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
770 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
771 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
772 ok( pfni->FileNameLength == 6, "len wrong\n" );
773 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
774
775 r = DeleteFileW( file );
776 ok( r == TRUE, "failed to delete file\n");
777
778 r = RemoveDirectoryW( subdir );
779 ok( r == TRUE, "failed to remove directory\n");
780
781 CloseHandle(hdir);
782
783 r = RemoveDirectoryW( path );
784 ok( r == TRUE, "failed to remove directory\n");
785 }
786
787 static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov)
788 {
789 ok(error == 0, "ReadDirectoryChangesW error %d\n", error);
790 ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n");
791 }
792
793 static void test_readdirectorychanges_cr(void)
794 {
795 static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 };
796 static const WCHAR szDir[] = { 'd','i','r',0 };
797 static const WCHAR szFile[] = { 'f','i','l','e',0 };
798 static const WCHAR szBackslash[] = { '\\',0 };
799
800 WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH];
801 FILE_NOTIFY_INFORMATION fni[1024], *fni_next;
802 OVERLAPPED ov;
803 HANDLE hdir, hfile;
804 NTSTATUS r;
805
806 if (!pReadDirectoryChangesW)
807 {
808 win_skip("ReadDirectoryChangesW is not available\n");
809 return;
810 }
811
812 SetLastError(0xdeadbeef);
813 r = GetTempPathW(MAX_PATH, path);
814 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
815 {
816 win_skip("GetTempPathW is not implemented\n");
817 return;
818 }
819 ok(r != 0, "temp path failed\n");
820 if (!r)
821 return;
822
823 lstrcatW(path, szBoo);
824 lstrcpyW(dir, path);
825 lstrcatW(dir, szDir);
826 lstrcpyW(file, path);
827 lstrcatW(file, szFile);
828 lstrcpyW(sub_file, dir);
829 lstrcatW(sub_file, szBackslash);
830 lstrcatW(sub_file, szFile);
831
832 DeleteFileW(file);
833 RemoveDirectoryW(dir);
834 RemoveDirectoryW(path);
835
836 r = CreateDirectoryW(path, NULL);
837 ok(r == TRUE, "failed to create directory\n");
838
839 hdir = CreateFileW(path, GENERIC_READ,
840 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
841 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
842 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
843
844 memset(&ov, 0, sizeof(ov));
845 ov.hEvent = (void*)0xdeadbeef;
846 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
847 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
848 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
849
850 hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
851 ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
852 CloseHandle(hfile);
853
854 r = SleepEx(1000, TRUE);
855 ok(r != 0, "failed to receive file creation event\n");
856 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
857 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
858 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
859 "FileNameLength = %d\n", fni->FileNameLength);
860 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
861 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
862
863 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
864 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
865 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
866
867 /* This event will not be reported */
868 r = CreateDirectoryW(dir, NULL);
869 ok(r == TRUE, "failed to create directory\n");
870
871 r = MoveFileW(file, sub_file);
872 ok(r == TRUE, "failed to move file\n");
873
874 r = SleepEx(1000, TRUE);
875 ok(r != 0, "failed to receive file move event\n");
876 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
877 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
878 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
879 "FileNameLength = %d\n", fni->FileNameLength);
880 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
881 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
882
883 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
884 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
885 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
886
887 r = MoveFileW(sub_file, file);
888 ok(r == TRUE, "failed to move file\n");
889
890 r = SleepEx(1000, TRUE);
891 ok(r != 0, "failed to receive file move event\n");
892 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
893 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
894 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
895 "FileNameLength = %d\n", fni->FileNameLength);
896 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
897 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
898
899 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
900 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
901 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
902
903 r = DeleteFileW(file);
904 ok(r == TRUE, "failed to delete file\n");
905
906 r = SleepEx(1000, TRUE);
907 ok(r != 0, "failed to receive file removal event\n");
908 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
909 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
910 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
911 "FileNameLength = %d\n", fni->FileNameLength);
912 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
913 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
914
915 CloseHandle(hdir);
916
917 hdir = CreateFileW(path, GENERIC_READ,
918 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
919 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
920 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
921
922 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
923 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
924 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
925
926 r = MoveFileW(dir, file);
927 ok(r == TRUE, "failed to move directory\n");
928
929 r = SleepEx(1000, TRUE);
930 ok(r != 0, "failed to receive directory move event\n");
931 if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME)
932 {
933 ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %d\n", fni->Action);
934 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
935 "FileNameLength = %d\n", fni->FileNameLength);
936 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
937 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
938 ok(fni->NextEntryOffset != 0, "no next entry in movement event\n");
939 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
940 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
941 ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %d\n", fni_next->Action);
942 ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
943 "FileNameLength = %d\n", fni_next->FileNameLength);
944 ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
945 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR)));
946 }
947 else
948 {
949 todo_wine ok(0, "Expected rename event\n");
950
951 if (fni->NextEntryOffset == 0)
952 {
953 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
954 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
955 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
956
957 r = SleepEx(1000, TRUE);
958 ok(r != 0, "failed to receive directory move event\n");
959 }
960 }
961
962 r = CreateDirectoryW(dir, NULL);
963 ok(r == TRUE, "failed to create directory\n");
964
965 r = RemoveDirectoryW(dir);
966 ok(r == TRUE, "failed to remove directory\n");
967
968 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
969 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
970 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
971
972 r = SleepEx(1000, TRUE);
973 ok(r != 0, "failed to receive directory creation event\n");
974 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
975 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
976 "FileNameLength = %d\n", fni->FileNameLength);
977 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
978 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
979 if (fni->NextEntryOffset)
980 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
981 else
982 {
983 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
984 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
985 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
986
987 r = SleepEx(1000, TRUE);
988 ok(r != 0, "failed to receive directory removal event\n");
989 fni_next = fni;
990 }
991 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
992 ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni_next->Action);
993 ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
994 "FileNameLength = %d\n", fni_next->FileNameLength);
995 ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
996 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR)));
997
998 CloseHandle(hdir);
999 RemoveDirectoryW(file);
1000 RemoveDirectoryW(path);
1001 }
1002
1003 static void test_ffcn_directory_overlap(void)
1004 {
1005 HANDLE parent_watch, child_watch, parent_thread, child_thread;
1006 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH];
1007 char tempfile[MAX_PATH];
1008 DWORD threadId;
1009 BOOL ret;
1010
1011 /* Setup directory hierarchy */
1012 ret = GetTempPathA(MAX_PATH, workdir);
1013 ok((ret > 0) && (ret <= MAX_PATH),
1014 "GetTempPathA error: %d\n", GetLastError());
1015
1016 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile);
1017 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1018 ret = DeleteFileA(tempfile);
1019 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1020
1021 lstrcpyA(parentdir, tempfile);
1022 ret = CreateDirectoryA(parentdir, NULL);
1023 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
1024
1025 lstrcpyA(childdir, parentdir);
1026 lstrcatA(childdir, "\\c");
1027 ret = CreateDirectoryA(childdir, NULL);
1028 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
1029
1030
1031 /* When recursively watching overlapping directories, changes in child
1032 * should trigger notifications for both child and parent */
1033 parent_thread = StartNotificationThread(parentdir, TRUE,
1034 FILE_NOTIFY_CHANGE_FILE_NAME);
1035 child_thread = StartNotificationThread(childdir, TRUE,
1036 FILE_NOTIFY_CHANGE_FILE_NAME);
1037
1038 /* Create a file in child */
1039 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1040 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1041
1042 /* Both watches should trigger */
1043 ret = FinishNotificationThread(parent_thread);
1044 ok(ret, "Missed parent notification\n");
1045 ret = FinishNotificationThread(child_thread);
1046 ok(ret, "Missed child notification\n");
1047
1048 ret = DeleteFileA(tempfile);
1049 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1050
1051
1052 /* Removing a recursive parent watch should not affect child watches. Doing
1053 * so used to crash wineserver. */
1054 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE,
1055 FILE_NOTIFY_CHANGE_FILE_NAME);
1056 ok(parent_watch != INVALID_HANDLE_VALUE,
1057 "FindFirstChangeNotification error: %d\n", GetLastError());
1058 child_watch = FindFirstChangeNotificationA(childdir, TRUE,
1059 FILE_NOTIFY_CHANGE_FILE_NAME);
1060 ok(child_watch != INVALID_HANDLE_VALUE,
1061 "FindFirstChangeNotification error: %d\n", GetLastError());
1062
1063 ret = FindCloseChangeNotification(parent_watch);
1064 ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError());
1065
1066 child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0,
1067 &threadId);
1068 ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError());
1069
1070 /* Create a file in child */
1071 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1072 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1073
1074 /* Child watch should trigger */
1075 ret = FinishNotificationThread(child_thread);
1076 ok(ret, "Missed child notification\n");
1077
1078 /* clean up */
1079 ret = DeleteFileA(tempfile);
1080 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1081
1082 ret = RemoveDirectoryA(childdir);
1083 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1084
1085 ret = RemoveDirectoryA(parentdir);
1086 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1087 }
1088
1089 START_TEST(change)
1090 {
1091 HMODULE hkernel32 = GetModuleHandle("kernel32");
1092 pReadDirectoryChangesW = (void *)GetProcAddress(hkernel32, "ReadDirectoryChangesW");
1093
1094 test_ffcnMultipleThreads();
1095 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
1096 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
1097 placed first. */
1098 test_FindFirstChangeNotification();
1099 test_ffcn();
1100 test_readdirectorychanges();
1101 test_readdirectorychanges_null();
1102 test_readdirectorychanges_filedir();
1103 test_readdirectorychanges_cr();
1104 test_ffcn_directory_overlap();
1105 }