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)
41 HANDLE change = (HANDLE) arg;
45 status = WaitForSingleObject(change, 100);
47 if (status == WAIT_OBJECT_0 ) {
48 ret = FindNextChangeNotification(change);
51 ret = FindCloseChangeNotification(change);
52 ok( ret, "FindCloseChangeNotification error: %d\n",
55 ExitThread((DWORD)ret);
58 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
60 HANDLE change, thread;
63 change = FindFirstChangeNotificationA(path, subtree, flags);
64 ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
66 thread = CreateThread(NULL, 0, NotificationThread, (LPVOID)change,
68 ok(thread != INVALID_HANDLE_VALUE, "CreateThread error: %d\n", GetLastError());
73 static DWORD FinishNotificationThread(HANDLE thread)
75 DWORD status, exitcode;
77 status = WaitForSingleObject(thread, 5000);
78 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\n", status, GetLastError());
80 ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
85 static void test_FindFirstChangeNotification(void)
87 HANDLE change, file, thread;
88 DWORD attributes, count;
91 char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
92 char filename1[MAX_PATH], filename2[MAX_PATH];
93 static const char prefix[] = "FCN";
98 change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
99 ok(change == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND,
100 "FindFirstChangeNotification error: %d\n", GetLastError());
102 if (0) /* This documents win2k behavior. It crashes on win98. */
104 change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
105 ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
106 "FindFirstChangeNotification error: %d\n", GetLastError());
109 ret = FindNextChangeNotification(NULL);
110 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n",
113 ret = FindCloseChangeNotification(NULL);
114 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
117 ret = GetTempPathA(MAX_PATH, workdir);
118 ok(ret, "GetTempPathA error: %d\n", GetLastError());
120 lstrcatA(workdir, "testFileChangeNotification");
122 ret = CreateDirectoryA(workdir, NULL);
123 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
125 ret = GetTempFileNameA(workdir, prefix, 0, filename1);
126 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
128 file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
129 FILE_ATTRIBUTE_NORMAL, 0);
130 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
131 ret = CloseHandle(file);
132 ok( ret, "CloseHandle error: %d\n", GetLastError());
134 /* Try to register notification for a file. win98 and win2k behave differently here */
135 change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
136 ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
137 GetLastError() == ERROR_FILE_NOT_FOUND),
138 "FindFirstChangeNotification error: %d\n", GetLastError());
140 lstrcpyA(dirname1, filename1);
141 lstrcatA(dirname1, "dir");
143 lstrcpyA(dirname2, dirname1);
144 lstrcatA(dirname2, "new");
146 ret = CreateDirectoryA(dirname1, NULL);
147 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
149 /* What if we move the directory we registered notification for? */
150 thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
151 ret = MoveFileA(dirname1, dirname2);
152 ok(ret, "MoveFileA error: %d\n", GetLastError());
153 ok(FinishNotificationThread(thread), "Missed notification\n");
155 /* What if we remove the directory we registered notification for? */
156 thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
157 ret = RemoveDirectoryA(dirname2);
158 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
160 /* win98 and win2k behave differently here */
161 ret = FinishNotificationThread(thread);
162 ok(ret || !ret, "You'll never read this\n");
164 /* functional checks */
166 /* Create a directory */
167 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
168 ret = CreateDirectoryA(dirname1, NULL);
169 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
170 ok(FinishNotificationThread(thread), "Missed notification\n");
172 /* Rename a directory */
173 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
174 ret = MoveFileA(dirname1, dirname2);
175 ok(ret, "MoveFileA error: %d\n", GetLastError());
176 ok(FinishNotificationThread(thread), "Missed notification\n");
178 /* Delete a directory */
179 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
180 ret = RemoveDirectoryA(dirname2);
181 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
182 ok(FinishNotificationThread(thread), "Missed notification\n");
184 lstrcpyA(filename2, filename1);
185 lstrcatA(filename2, "new");
188 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
189 ret = MoveFileA(filename1, filename2);
190 ok(ret, "MoveFileA error: %d\n", GetLastError());
191 ok(FinishNotificationThread(thread), "Missed notification\n");
194 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
195 ret = DeleteFileA(filename2);
196 ok(ret, "DeleteFileA error: %d\n", GetLastError());
197 ok(FinishNotificationThread(thread), "Missed notification\n");
200 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
201 file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
202 FILE_ATTRIBUTE_NORMAL, 0);
203 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
204 ret = CloseHandle(file);
205 ok( ret, "CloseHandle error: %d\n", GetLastError());
206 ok(FinishNotificationThread(thread), "Missed notification\n");
208 attributes = GetFileAttributesA(filename2);
209 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError());
210 attributes &= FILE_ATTRIBUTE_READONLY;
212 /* Change file attributes */
213 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
214 ret = SetFileAttributesA(filename2, attributes);
215 ok(ret, "SetFileAttributesA error: %d\n", GetLastError());
216 ok(FinishNotificationThread(thread), "Missed notification\n");
218 /* Change last write time by writing to a file */
219 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
220 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
221 FILE_ATTRIBUTE_NORMAL, 0);
222 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
223 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
224 ok(ret && count == sizeof(buffer), "WriteFile error: %d\n", GetLastError());
225 ret = CloseHandle(file);
226 ok( ret, "CloseHandle error: %d\n", GetLastError());
227 ok(FinishNotificationThread(thread), "Missed notification\n");
229 /* Change file size by truncating a file */
230 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
231 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
232 FILE_ATTRIBUTE_NORMAL, 0);
233 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
234 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
235 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %d\n", GetLastError());
236 ret = CloseHandle(file);
237 ok( ret, "CloseHandle error: %d\n", GetLastError());
238 ok(FinishNotificationThread(thread), "Missed notification\n");
242 ret = DeleteFileA(filename2);
243 ok(ret, "DeleteFileA error: %d\n", GetLastError());
245 ret = RemoveDirectoryA(workdir);
246 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
249 /* this test concentrates more on the wait behaviour of the handle */
250 static void test_ffcn(void)
255 WCHAR path[MAX_PATH], subdir[MAX_PATH];
256 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
257 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
259 r = GetTempPathW( MAX_PATH, path );
260 ok( r != 0, "temp path failed\n");
264 lstrcatW( path, szBoo );
265 lstrcpyW( subdir, path );
266 lstrcatW( subdir, szHoo );
268 RemoveDirectoryW( subdir );
269 RemoveDirectoryW( path );
271 r = CreateDirectoryW(path, NULL);
272 ok( r == TRUE, "failed to create directory\n");
274 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
275 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
277 handle = FindFirstChangeNotificationW( path, 1, filter);
278 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
280 r = WaitForSingleObject( handle, 0 );
281 ok( r == STATUS_TIMEOUT, "should time out\n");
283 r = CreateDirectoryW( subdir, NULL );
284 ok( r == TRUE, "failed to create subdir\n");
286 r = WaitForSingleObject( handle, 0 );
287 ok( r == WAIT_OBJECT_0, "should be ready\n");
289 r = WaitForSingleObject( handle, 0 );
290 ok( r == WAIT_OBJECT_0, "should be ready\n");
292 r = FindNextChangeNotification(handle);
293 ok( r == TRUE, "find next failed\n");
295 r = WaitForSingleObject( handle, 0 );
296 ok( r == STATUS_TIMEOUT, "should time out\n");
298 r = RemoveDirectoryW( subdir );
299 ok( r == TRUE, "failed to remove subdir\n");
301 r = WaitForSingleObject( handle, 0 );
302 ok( r == WAIT_OBJECT_0, "should be ready\n");
304 r = WaitForSingleObject( handle, 0 );
305 ok( r == WAIT_OBJECT_0, "should be ready\n");
307 r = FindNextChangeNotification(handle);
308 ok( r == TRUE, "find next failed\n");
310 r = FindNextChangeNotification(handle);
311 ok( r == TRUE, "find next failed\n");
313 r = FindCloseChangeNotification(handle);
314 ok( r == TRUE, "should succeed\n");
316 r = RemoveDirectoryW( path );
317 ok( r == TRUE, "failed to remove dir\n");
320 typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
321 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
322 fnReadDirectoryChangesW pReadDirectoryChangesW;
324 static void test_readdirectorychanges(void)
328 DWORD fflags, filter = 0, r, dwCount;
330 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
331 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
332 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
333 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
334 PFILE_NOTIFY_INFORMATION pfni;
336 if (!pReadDirectoryChangesW)
339 r = GetTempPathW( MAX_PATH, path );
340 ok( r != 0, "temp path failed\n");
344 lstrcatW( path, szBoo );
345 lstrcpyW( subdir, path );
346 lstrcatW( subdir, szHoo );
348 lstrcpyW( subsubdir, path );
349 lstrcatW( subsubdir, szGa );
351 RemoveDirectoryW( subsubdir );
352 RemoveDirectoryW( subdir );
353 RemoveDirectoryW( path );
355 r = CreateDirectoryW(path, NULL);
356 ok( r == TRUE, "failed to create directory\n");
358 SetLastError(0xd0b00b00);
359 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
360 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
361 ok(r==FALSE, "should return false\n");
363 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
364 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
365 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
366 OPEN_EXISTING, fflags, NULL);
367 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
369 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
371 SetLastError(0xd0b00b00);
372 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
373 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
374 ok(r==FALSE, "should return false\n");
376 SetLastError(0xd0b00b00);
377 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
378 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
379 ok(r==FALSE, "should return false\n");
381 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
382 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
383 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
384 filter |= FILE_NOTIFY_CHANGE_SIZE;
385 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
386 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
387 filter |= FILE_NOTIFY_CHANGE_CREATION;
388 filter |= FILE_NOTIFY_CHANGE_SECURITY;
390 SetLastError(0xd0b00b00);
393 memset( buffer, 0, sizeof buffer );
395 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
396 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
397 ok(r==FALSE, "should return false\n");
399 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
400 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
401 ok(r==FALSE, "should return false\n");
403 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
404 ok(r==TRUE, "should return true\n");
406 r = WaitForSingleObject( ov.hEvent, 10 );
407 ok( r == STATUS_TIMEOUT, "should timeout\n" );
409 r = CreateDirectoryW( subdir, NULL );
410 ok( r == TRUE, "failed to create directory\n");
412 r = WaitForSingleObject( ov.hEvent, 1000 );
413 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
415 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
416 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
418 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
419 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
420 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
421 ok( pfni->FileNameLength == 6, "len wrong\n" );
422 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
424 ResetEvent(ov.hEvent);
425 SetLastError(0xd0b00b00);
426 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
427 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
428 ok(r==FALSE, "should return false\n");
430 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
431 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
432 ok(r==FALSE, "should return false\n");
434 filter = FILE_NOTIFY_CHANGE_SIZE;
440 S(U(ov)).OffsetHigh = 0;
441 memset( buffer, 0, sizeof buffer );
442 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
443 ok(r==TRUE, "should return true\n");
445 ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
446 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
448 r = WaitForSingleObject( ov.hEvent, 0 );
449 ok( r == STATUS_TIMEOUT, "should timeout\n" );
451 r = RemoveDirectoryW( subdir );
452 ok( r == TRUE, "failed to remove directory\n");
454 r = WaitForSingleObject( ov.hEvent, 1000 );
455 ok( r == WAIT_OBJECT_0, "should be ready\n" );
457 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
458 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
460 if (ov.Internal == STATUS_SUCCESS)
462 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
463 ok( r == TRUE, "getoverlappedresult failed\n");
464 ok( dwCount == 0x12, "count wrong\n");
467 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
468 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
469 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
470 ok( pfni->FileNameLength == 6, "len wrong\n" );
471 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
473 /* what happens if the buffer is too small? */
474 r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
475 ok(r==TRUE, "should return true\n");
477 r = CreateDirectoryW( subdir, NULL );
478 ok( r == TRUE, "failed to create directory\n");
480 r = WaitForSingleObject( ov.hEvent, 1000 );
481 ok( r == WAIT_OBJECT_0, "should be ready\n" );
483 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
484 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
486 /* test the recursive watch */
487 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
488 ok(r==TRUE, "should return true\n");
490 r = CreateDirectoryW( subsubdir, NULL );
491 ok( r == TRUE, "failed to create directory\n");
493 r = WaitForSingleObject( ov.hEvent, 1000 );
494 ok( r == WAIT_OBJECT_0, "should be ready\n" );
496 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
497 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
499 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
500 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
501 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
502 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
503 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
505 r = RemoveDirectoryW( subsubdir );
506 ok( r == TRUE, "failed to remove directory\n");
510 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
511 ok(r==TRUE, "should return true\n");
513 r = RemoveDirectoryW( subdir );
514 ok( r == TRUE, "failed to remove directory\n");
516 r = WaitForSingleObject( ov.hEvent, 1000 );
517 ok( r == WAIT_OBJECT_0, "should be ready\n" );
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 == 0x0c, "len wrong\n" );
523 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
525 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
526 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
530 r = RemoveDirectoryW( path );
531 ok( r == TRUE, "failed to remove directory\n");
534 /* show the behaviour when a null buffer is passed */
535 static void test_readdirectorychanges_null(void)
540 DWORD fflags, filter = 0;
542 WCHAR path[MAX_PATH], subdir[MAX_PATH];
543 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
544 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
545 PFILE_NOTIFY_INFORMATION pfni;
547 if (!pReadDirectoryChangesW)
550 r = GetTempPathW( MAX_PATH, path );
551 ok( r != 0, "temp path failed\n");
555 lstrcatW( path, szBoo );
556 lstrcpyW( subdir, path );
557 lstrcatW( subdir, szHoo );
559 RemoveDirectoryW( subdir );
560 RemoveDirectoryW( path );
562 r = CreateDirectoryW(path, NULL);
563 ok( r == TRUE, "failed to create directory\n");
565 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
566 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
567 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
568 OPEN_EXISTING, fflags, NULL);
569 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
571 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
573 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
574 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
576 SetLastError(0xd0b00b00);
579 memset( buffer, 0, sizeof buffer );
581 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
582 ok(r==TRUE, "should return true\n");
584 r = WaitForSingleObject( ov.hEvent, 0 );
585 ok( r == STATUS_TIMEOUT, "should timeout\n" );
587 r = CreateDirectoryW( subdir, NULL );
588 ok( r == TRUE, "failed to create directory\n");
590 r = WaitForSingleObject( ov.hEvent, 0 );
591 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
593 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
594 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
599 S(U(ov)).OffsetHigh = 0;
600 memset( buffer, 0, sizeof buffer );
602 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
603 ok(r==TRUE, "should return true\n");
605 r = WaitForSingleObject( ov.hEvent, 0 );
606 ok( r == STATUS_TIMEOUT, "should timeout\n" );
608 r = RemoveDirectoryW( subdir );
609 ok( r == TRUE, "failed to remove directory\n");
611 r = WaitForSingleObject( ov.hEvent, 1000 );
612 ok( r == WAIT_OBJECT_0, "should be ready\n" );
614 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
615 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
617 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
618 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
622 r = RemoveDirectoryW( path );
623 ok( r == TRUE, "failed to remove directory\n");
626 static void test_readdirectorychanges_filedir(void)
631 DWORD fflags, filter = 0;
633 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
634 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
635 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
636 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
637 PFILE_NOTIFY_INFORMATION pfni;
639 r = GetTempPathW( MAX_PATH, path );
640 ok( r != 0, "temp path failed\n");
644 lstrcatW( path, szBoo );
645 lstrcpyW( subdir, path );
646 lstrcatW( subdir, szHoo );
648 lstrcpyW( file, path );
649 lstrcatW( file, szFoo );
652 RemoveDirectoryW( subdir );
653 RemoveDirectoryW( path );
655 r = CreateDirectoryW(path, NULL);
656 ok( r == TRUE, "failed to create directory\n");
658 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
659 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
660 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
661 OPEN_EXISTING, fflags, NULL);
662 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
664 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
666 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
668 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
669 ok(r==TRUE, "should return true\n");
671 r = WaitForSingleObject( ov.hEvent, 10 );
672 ok( r == WAIT_TIMEOUT, "should timeout\n" );
674 r = CreateDirectoryW( subdir, NULL );
675 ok( r == TRUE, "failed to create directory\n");
677 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
678 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
679 ok( CloseHandle(hfile), "failed toc lose file\n");
681 r = WaitForSingleObject( ov.hEvent, 1000 );
682 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
684 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
685 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
687 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
688 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
689 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
690 ok( pfni->FileNameLength == 6, "len wrong\n" );
691 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
693 r = DeleteFileW( file );
694 ok( r == TRUE, "failed to delete file\n");
696 r = RemoveDirectoryW( subdir );
697 ok( r == TRUE, "failed to remove directory\n");
701 r = RemoveDirectoryW( path );
702 ok( r == TRUE, "failed to remove directory\n");
707 HMODULE hkernel32 = GetModuleHandle("kernel32");
708 pReadDirectoryChangesW = (fnReadDirectoryChangesW)
709 GetProcAddress(hkernel32, "ReadDirectoryChangesW");
711 test_FindFirstChangeNotification();
713 test_readdirectorychanges();
714 test_readdirectorychanges_null();
715 test_readdirectorychanges_filedir();