2 * Tests for file change notification functions
4 * Copyright (c) 2004 Hans Leidekker
5 * Copyright 2006 Mike McCormack for CodeWeavers
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.
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.
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
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
33 #define WIN32_NO_STATUS
34 #include "wine/test.h"
39 static DWORD CALLBACK NotificationThread(LPVOID arg)
42 BOOL notified = FALSE;
46 status = WaitForSingleObject(change, 100);
48 if (status == WAIT_OBJECT_0 ) {
50 ret = FindNextChangeNotification(change);
53 ret = FindCloseChangeNotification(change);
54 ok( ret, "FindCloseChangeNotification error: %d\n",
57 ExitThread((DWORD)notified);
60 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
62 HANDLE change, thread;
65 change = FindFirstChangeNotificationA(path, subtree, flags);
66 ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
68 thread = CreateThread(NULL, 0, NotificationThread, change, 0, &threadId);
69 ok(thread != NULL, "CreateThread error: %d\n", GetLastError());
74 static DWORD FinishNotificationThread(HANDLE thread)
76 DWORD status, exitcode;
78 status = WaitForSingleObject(thread, 5000);
79 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\n", status, GetLastError());
81 ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
87 static void test_FindFirstChangeNotification(void)
89 HANDLE change, file, thread;
90 DWORD attributes, count;
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";
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());
106 if (0) /* This documents win2k behavior. It crashes on win98. */
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());
113 ret = FindNextChangeNotification(NULL);
114 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n",
117 ret = FindCloseChangeNotification(NULL);
118 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
121 ret = GetTempPathA(MAX_PATH, workdir);
122 ok(ret, "GetTempPathA error: %d\n", GetLastError());
124 lstrcatA(workdir, "testFileChangeNotification");
126 ret = CreateDirectoryA(workdir, NULL);
127 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
129 ret = GetTempFileNameA(workdir, prefix, 0, filename1);
130 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
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());
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());
144 lstrcpyA(dirname1, filename1);
145 lstrcatA(dirname1, "dir");
147 lstrcpyA(dirname2, dirname1);
148 lstrcatA(dirname2, "new");
150 ret = CreateDirectoryA(dirname1, NULL);
151 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
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);
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);
167 /* functional checks */
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");
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");
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");
187 lstrcpyA(filename2, filename1);
188 lstrcatA(filename2, "new");
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");
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");
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");
211 attributes = GetFileAttributesA(filename2);
212 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError());
213 attributes &= FILE_ATTRIBUTE_READONLY;
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");
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");
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");
246 ret = DeleteFileA(filename2);
247 ok(ret, "DeleteFileA error: %d\n", GetLastError());
249 ret = RemoveDirectoryA(workdir);
250 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
253 /* this test concentrates more on the wait behaviour of the handle */
254 static void test_ffcn(void)
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 };
263 SetLastError(0xdeadbeef);
264 r = GetTempPathW( MAX_PATH, path );
265 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
267 win_skip("GetTempPathW is not implemented\n");
270 ok( r != 0, "temp path failed\n");
274 lstrcatW( path, szBoo );
275 lstrcpyW( subdir, path );
276 lstrcatW( subdir, szHoo );
278 RemoveDirectoryW( subdir );
279 RemoveDirectoryW( path );
281 r = CreateDirectoryW(path, NULL);
282 ok( r == TRUE, "failed to create directory\n");
284 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
285 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
287 handle = FindFirstChangeNotificationW( path, 1, filter);
288 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
290 r = WaitForSingleObject( handle, 0 );
291 ok( r == STATUS_TIMEOUT, "should time out\n");
293 r = CreateDirectoryW( subdir, NULL );
294 ok( r == TRUE, "failed to create subdir\n");
296 r = WaitForSingleObject( handle, 0 );
297 ok( r == WAIT_OBJECT_0, "should be ready\n");
299 r = WaitForSingleObject( handle, 0 );
300 ok( r == WAIT_OBJECT_0, "should be ready\n");
302 r = FindNextChangeNotification(handle);
303 ok( r == TRUE, "find next failed\n");
305 r = WaitForSingleObject( handle, 0 );
306 ok( r == STATUS_TIMEOUT, "should time out\n");
308 r = RemoveDirectoryW( subdir );
309 ok( r == TRUE, "failed to remove subdir\n");
311 r = WaitForSingleObject( handle, 0 );
312 ok( r == WAIT_OBJECT_0, "should be ready\n");
314 r = WaitForSingleObject( handle, 0 );
315 ok( r == WAIT_OBJECT_0, "should be ready\n");
317 r = FindNextChangeNotification(handle);
318 ok( r == TRUE, "find next failed\n");
320 r = FindNextChangeNotification(handle);
321 ok( r == TRUE, "find next failed\n");
323 r = FindCloseChangeNotification(handle);
324 ok( r == TRUE, "should succeed\n");
326 r = RemoveDirectoryW( path );
327 ok( r == TRUE, "failed to remove dir\n");
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)
335 DWORD filter, threadId, status, exitcode;
339 r = GetTempPathA(MAX_PATH, path);
340 ok(r, "GetTempPathA error: %d\n", GetLastError());
342 lstrcatA(path, "ffcnTestMultipleThreads");
344 RemoveDirectoryA(path);
346 r = CreateDirectoryA(path, NULL);
347 ok(r, "CreateDirectoryA error: %d\n", GetLastError());
349 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
350 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
352 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
353 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
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) */
358 /* Create our notification thread */
359 handles[1] = CreateThread(NULL, 0, NotificationThread, handles[0], 0,
361 ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
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");
368 r = RemoveDirectoryA( path );
369 ok( r == TRUE, "failed to remove dir\n");
372 static BOOL (WINAPI *pReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
373 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
375 static void test_readdirectorychanges(void)
379 DWORD fflags, filter = 0, r, dwCount;
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;
387 if (!pReadDirectoryChangesW)
389 win_skip("ReadDirectoryChangesW is not available\n");
393 SetLastError(0xdeadbeef);
394 r = GetTempPathW( MAX_PATH, path );
395 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
397 win_skip("GetTempPathW is not implemented\n");
400 ok( r != 0, "temp path failed\n");
404 lstrcatW( path, szBoo );
405 lstrcpyW( subdir, path );
406 lstrcatW( subdir, szHoo );
408 lstrcpyW( subsubdir, path );
409 lstrcatW( subsubdir, szGa );
411 RemoveDirectoryW( subsubdir );
412 RemoveDirectoryW( subdir );
413 RemoveDirectoryW( path );
415 r = CreateDirectoryW(path, NULL);
416 ok( r == TRUE, "failed to create directory\n");
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");
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");
429 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
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");
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");
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;
450 SetLastError(0xd0b00b00);
453 memset( buffer, 0, sizeof buffer );
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");
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");
463 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
464 ok(r==TRUE, "should return true\n");
466 r = WaitForSingleObject( ov.hEvent, 10 );
467 ok( r == STATUS_TIMEOUT, "should timeout\n" );
469 r = CreateDirectoryW( subdir, NULL );
470 ok( r == TRUE, "failed to create directory\n");
472 r = WaitForSingleObject( ov.hEvent, 1000 );
473 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
475 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
476 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
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" );
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");
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");
494 filter = FILE_NOTIFY_CHANGE_SIZE;
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");
505 ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
506 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
508 r = WaitForSingleObject( ov.hEvent, 0 );
509 ok( r == STATUS_TIMEOUT, "should timeout\n" );
511 r = RemoveDirectoryW( subdir );
512 ok( r == TRUE, "failed to remove directory\n");
514 r = WaitForSingleObject( ov.hEvent, 1000 );
515 ok( r == WAIT_OBJECT_0, "should be ready\n" );
517 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
518 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
520 if (ov.Internal == STATUS_SUCCESS)
522 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
523 ok( r == TRUE, "getoverlappedresult failed\n");
524 ok( dwCount == 0x12, "count wrong\n");
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" );
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");
537 r = CreateDirectoryW( subdir, NULL );
538 ok( r == TRUE, "failed to create directory\n");
540 r = WaitForSingleObject( ov.hEvent, 1000 );
541 ok( r == WAIT_OBJECT_0, "should be ready\n" );
543 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
544 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
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");
550 r = CreateDirectoryW( subsubdir, NULL );
551 ok( r == TRUE, "failed to create directory\n");
553 r = WaitForSingleObject( ov.hEvent, 1000 );
554 ok( r == WAIT_OBJECT_0, "should be ready\n" );
556 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
557 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
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" );
565 r = RemoveDirectoryW( subsubdir );
566 ok( r == TRUE, "failed to remove directory\n");
570 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
571 ok(r==TRUE, "should return true\n");
573 r = RemoveDirectoryW( subdir );
574 ok( r == TRUE, "failed to remove directory\n");
576 r = WaitForSingleObject( ov.hEvent, 1000 );
577 ok( r == WAIT_OBJECT_0, "should be ready\n" );
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)
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);
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" );
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 );
598 r = RemoveDirectoryW( path );
599 ok( r == TRUE, "failed to remove directory\n");
602 /* show the behaviour when a null buffer is passed */
603 static void test_readdirectorychanges_null(void)
608 DWORD fflags, filter = 0;
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;
615 if (!pReadDirectoryChangesW)
617 win_skip("ReadDirectoryChangesW is not available\n");
620 SetLastError(0xdeadbeef);
621 r = GetTempPathW( MAX_PATH, path );
622 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
624 win_skip("GetTempPathW is not implemented\n");
627 ok( r != 0, "temp path failed\n");
631 lstrcatW( path, szBoo );
632 lstrcpyW( subdir, path );
633 lstrcatW( subdir, szHoo );
635 RemoveDirectoryW( subdir );
636 RemoveDirectoryW( path );
638 r = CreateDirectoryW(path, NULL);
639 ok( r == TRUE, "failed to create directory\n");
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");
647 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
649 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
650 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
652 SetLastError(0xd0b00b00);
655 memset( buffer, 0, sizeof buffer );
657 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
658 ok(r==TRUE, "should return true\n");
660 r = WaitForSingleObject( ov.hEvent, 0 );
661 ok( r == STATUS_TIMEOUT, "should timeout\n" );
663 r = CreateDirectoryW( subdir, NULL );
664 ok( r == TRUE, "failed to create directory\n");
666 r = WaitForSingleObject( ov.hEvent, 0 );
667 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
669 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
670 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
675 S(U(ov)).OffsetHigh = 0;
676 memset( buffer, 0, sizeof buffer );
678 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
679 ok(r==TRUE, "should return true\n");
681 r = WaitForSingleObject( ov.hEvent, 0 );
682 ok( r == STATUS_TIMEOUT, "should timeout\n" );
684 r = RemoveDirectoryW( subdir );
685 ok( r == TRUE, "failed to remove directory\n");
687 r = WaitForSingleObject( ov.hEvent, 1000 );
688 ok( r == WAIT_OBJECT_0, "should be ready\n" );
690 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
691 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
693 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
694 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
698 r = RemoveDirectoryW( path );
699 ok( r == TRUE, "failed to remove directory\n");
702 static void test_readdirectorychanges_filedir(void)
707 DWORD fflags, filter = 0;
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;
715 SetLastError(0xdeadbeef);
716 r = GetTempPathW( MAX_PATH, path );
717 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
719 win_skip("GetTempPathW is not implemented\n");
722 ok( r != 0, "temp path failed\n");
726 lstrcatW( path, szBoo );
727 lstrcpyW( subdir, path );
728 lstrcatW( subdir, szHoo );
730 lstrcpyW( file, path );
731 lstrcatW( file, szFoo );
734 RemoveDirectoryW( subdir );
735 RemoveDirectoryW( path );
737 r = CreateDirectoryW(path, NULL);
738 ok( r == TRUE, "failed to create directory\n");
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");
746 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
748 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
750 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
751 ok(r==TRUE, "should return true\n");
753 r = WaitForSingleObject( ov.hEvent, 10 );
754 ok( r == WAIT_TIMEOUT, "should timeout\n" );
756 r = CreateDirectoryW( subdir, NULL );
757 ok( r == TRUE, "failed to create directory\n");
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");
763 r = WaitForSingleObject( ov.hEvent, 1000 );
764 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
766 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
767 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
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" );
775 r = DeleteFileW( file );
776 ok( r == TRUE, "failed to delete file\n");
778 r = RemoveDirectoryW( subdir );
779 ok( r == TRUE, "failed to remove directory\n");
783 r = RemoveDirectoryW( path );
784 ok( r == TRUE, "failed to remove directory\n");
787 static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov)
789 ok(error == 0, "ReadDirectoryChangesW error %d\n", error);
790 ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n");
793 static void test_readdirectorychanges_cr(void)
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 };
799 WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH];
800 FILE_NOTIFY_INFORMATION fni[1024], *fni_next;
805 if (!pReadDirectoryChangesW)
807 win_skip("ReadDirectoryChangesW is not available\n");
811 SetLastError(0xdeadbeef);
812 r = GetTempPathW(MAX_PATH, path);
813 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
815 win_skip("GetTempPathW is not implemented\n");
818 ok(r != 0, "temp path failed\n");
822 lstrcatW(path, szBoo);
824 lstrcatW(dir, szDir);
825 lstrcpyW(file, path);
826 lstrcatW(file, szFile);
829 RemoveDirectoryW(dir);
830 RemoveDirectoryW(path);
832 r = CreateDirectoryW(path, NULL);
833 ok(r == TRUE, "failed to create directory\n");
835 hdir = CreateFileW(path, GENERIC_READ,
836 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
837 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
838 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
840 memset(&ov, 0, sizeof(ov));
841 ov.hEvent = (void*)0xdeadbeef;
842 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
843 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
844 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
846 hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
847 ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
850 r = SleepEx(1000, TRUE);
851 ok(r != 0, "failed to receive file creation event\n");
852 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
853 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
854 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
855 "FileNameLength = %d\n", fni->FileNameLength);
856 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
857 "FileName = %s\n", wine_dbgstr_w(fni->FileName));
859 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
860 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
861 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
863 /* This event will not be reported */
864 r = CreateDirectoryW(dir, NULL);
865 ok(r == TRUE, "failed to create directory\n");
867 r = DeleteFileW(file);
868 ok(r == TRUE, "failed to delete file\n");
870 r = SleepEx(1000, TRUE);
871 ok(r != 0, "failed to receive file removal event\n");
872 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
873 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
874 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
875 "FileNameLength = %d\n", fni->FileNameLength);
876 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
877 "FileName = %s\n", wine_dbgstr_w(fni->FileName));
881 hdir = CreateFileW(path, GENERIC_READ,
882 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
883 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
884 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
886 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
887 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
888 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
890 r = MoveFileW(dir, file);
891 ok(r == TRUE, "failed to move directory\n");
893 r = SleepEx(1000, TRUE);
894 ok(r != 0, "failed to receive directory move event\n");
895 ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %d\n", fni->Action);
896 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
897 "FileNameLength = %d\n", fni->FileNameLength);
898 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
899 "FileName = %s\n", wine_dbgstr_w(fni->FileName));
900 if (fni->NextEntryOffset)
901 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
904 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
905 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
906 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
908 r = SleepEx(1000, TRUE);
909 ok(r != 0, "failed to receive directory move event\n");
912 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
913 ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %d\n", fni_next->Action);
914 ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
915 "FileNameLength = %d\n", fni_next->FileNameLength);
916 ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
917 "FileName = %s\n", wine_dbgstr_w(fni_next->FileName));
919 r = CreateDirectoryW(dir, NULL);
920 ok(r == TRUE, "failed to create directory\n");
922 r = RemoveDirectoryW(dir);
923 ok(r == TRUE, "failed to remove directory\n");
925 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
926 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
927 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
929 r = SleepEx(1000, TRUE);
930 ok(r != 0, "failed to receive directory creation event\n");
931 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
932 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
933 "FileNameLength = %d\n", fni->FileNameLength);
934 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
935 "FileName = %s\n", wine_dbgstr_w(fni->FileName));
936 if (fni->NextEntryOffset)
937 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
940 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
941 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
942 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
944 r = SleepEx(1000, TRUE);
945 ok(r != 0, "failed to receive directory removal event\n");
948 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
949 ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni_next->Action);
950 ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
951 "FileNameLength = %d\n", fni_next->FileNameLength);
952 ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
953 "FileName = %s\n", wine_dbgstr_w(fni_next->FileName));
956 RemoveDirectoryW(file);
957 RemoveDirectoryW(path);
960 static void test_ffcn_directory_overlap(void)
962 HANDLE parent_watch, child_watch, parent_thread, child_thread;
963 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH];
964 char tempfile[MAX_PATH];
968 /* Setup directory hierarchy */
969 ret = GetTempPathA(MAX_PATH, workdir);
970 ok((ret > 0) && (ret <= MAX_PATH),
971 "GetTempPathA error: %d\n", GetLastError());
973 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile);
974 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
975 ret = DeleteFileA(tempfile);
976 ok(ret, "DeleteFileA error: %d\n", GetLastError());
978 lstrcpyA(parentdir, tempfile);
979 ret = CreateDirectoryA(parentdir, NULL);
980 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
982 lstrcpyA(childdir, parentdir);
983 lstrcatA(childdir, "\\c");
984 ret = CreateDirectoryA(childdir, NULL);
985 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
988 /* When recursively watching overlapping directories, changes in child
989 * should trigger notifications for both child and parent */
990 parent_thread = StartNotificationThread(parentdir, TRUE,
991 FILE_NOTIFY_CHANGE_FILE_NAME);
992 child_thread = StartNotificationThread(childdir, TRUE,
993 FILE_NOTIFY_CHANGE_FILE_NAME);
995 /* Create a file in child */
996 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
997 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
999 /* Both watches should trigger */
1000 ret = FinishNotificationThread(parent_thread);
1001 ok(ret, "Missed parent notification\n");
1002 ret = FinishNotificationThread(child_thread);
1003 ok(ret, "Missed child notification\n");
1005 ret = DeleteFileA(tempfile);
1006 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1009 /* Removing a recursive parent watch should not affect child watches. Doing
1010 * so used to crash wineserver. */
1011 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE,
1012 FILE_NOTIFY_CHANGE_FILE_NAME);
1013 ok(parent_watch != INVALID_HANDLE_VALUE,
1014 "FindFirstChangeNotification error: %d\n", GetLastError());
1015 child_watch = FindFirstChangeNotificationA(childdir, TRUE,
1016 FILE_NOTIFY_CHANGE_FILE_NAME);
1017 ok(child_watch != INVALID_HANDLE_VALUE,
1018 "FindFirstChangeNotification error: %d\n", GetLastError());
1020 ret = FindCloseChangeNotification(parent_watch);
1021 ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError());
1023 child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0,
1025 ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError());
1027 /* Create a file in child */
1028 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1029 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1031 /* Child watch should trigger */
1032 ret = FinishNotificationThread(child_thread);
1033 ok(ret, "Missed child notification\n");
1036 ret = DeleteFileA(tempfile);
1037 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1039 ret = RemoveDirectoryA(childdir);
1040 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1042 ret = RemoveDirectoryA(parentdir);
1043 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1048 HMODULE hkernel32 = GetModuleHandle("kernel32");
1049 pReadDirectoryChangesW = (void *)GetProcAddress(hkernel32, "ReadDirectoryChangesW");
1051 test_ffcnMultipleThreads();
1052 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
1053 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
1055 test_FindFirstChangeNotification();
1057 test_readdirectorychanges();
1058 test_readdirectorychanges_null();
1059 test_readdirectorychanges_filedir();
1060 test_readdirectorychanges_cr();
1061 test_ffcn_directory_overlap();