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