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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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: %ld\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: %ld\n", GetLastError());
66 thread = CreateThread(NULL, 0, NotificationThread, (LPVOID)change,
68 ok(thread != INVALID_HANDLE_VALUE, "CreateThread error: %ld\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 %ld error %ld\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: %ld\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: %ld\n", GetLastError());
109 ret = FindNextChangeNotification(NULL);
110 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %ld\n",
113 ret = FindCloseChangeNotification(NULL);
114 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n",
117 ret = GetTempPathA(MAX_PATH, workdir);
118 ok(ret, "GetTempPathA error: %ld\n", GetLastError());
120 lstrcatA(workdir, "testFileChangeNotification");
122 ret = CreateDirectoryA(workdir, NULL);
123 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
125 ret = GetTempFileNameA(workdir, prefix, 0, filename1);
126 ok(ret, "GetTempFileNameA error: %ld\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: %ld\n", GetLastError());
131 ret = CloseHandle(file);
132 ok( ret, "CloseHandle error: %ld\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: %ld\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: %ld\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: %ld\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: %ld\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: %ld\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: %ld\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: %ld\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: %ld\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: %ld\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: %ld\n", GetLastError());
204 ret = CloseHandle(file);
205 ok( ret, "CloseHandle error: %ld\n", GetLastError());
206 ok(FinishNotificationThread(thread), "Missed notification\n");
208 attributes = GetFileAttributesA(filename2);
209 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %ld\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: %ld\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: %ld\n", GetLastError());
223 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
224 ok(ret && count == sizeof(buffer), "WriteFile error: %ld\n", GetLastError());
225 ret = CloseHandle(file);
226 ok( ret, "CloseHandle error: %ld\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: %ld\n", GetLastError());
234 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
235 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %ld\n", GetLastError());
236 ret = CloseHandle(file);
237 ok( ret, "CloseHandle error: %ld\n", GetLastError());
238 ok(FinishNotificationThread(thread), "Missed notification\n");
242 ret = DeleteFileA(filename2);
243 ok(ret, "DeleteFileA error: %ld\n", GetLastError());
245 ret = RemoveDirectoryA(workdir);
246 ok(ret, "RemoveDirectoryA error: %ld\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 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
461 ok( r == TRUE, "getoverlappedresult failed\n");
462 ok( dwCount == 0x12, "count wrong\n");
464 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
465 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
466 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
467 ok( pfni->FileNameLength == 6, "len wrong\n" );
468 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
470 /* what happens if the buffer is too small? */
471 r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
472 ok(r==TRUE, "should return true\n");
474 r = CreateDirectoryW( subdir, NULL );
475 ok( r == TRUE, "failed to create directory\n");
477 r = WaitForSingleObject( ov.hEvent, 1000 );
478 ok( r == WAIT_OBJECT_0, "should be ready\n" );
480 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
481 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
483 /* test the recursive watch */
484 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
485 ok(r==TRUE, "should return true\n");
487 r = CreateDirectoryW( subsubdir, NULL );
488 ok( r == TRUE, "failed to create directory\n");
490 r = WaitForSingleObject( ov.hEvent, 1000 );
491 ok( r == WAIT_OBJECT_0, "should be ready\n" );
493 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
494 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
496 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
497 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
498 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
499 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
500 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
502 r = RemoveDirectoryW( subsubdir );
503 ok( r == TRUE, "failed to remove directory\n");
507 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
508 ok(r==TRUE, "should return true\n");
510 r = RemoveDirectoryW( subdir );
511 ok( r == TRUE, "failed to remove directory\n");
513 r = WaitForSingleObject( ov.hEvent, 1000 );
514 ok( r == WAIT_OBJECT_0, "should be ready\n" );
516 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
517 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
518 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
519 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
520 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
522 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
523 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
527 r = RemoveDirectoryW( path );
528 ok( r == TRUE, "failed to remove directory\n");
531 /* show the behaviour when a null buffer is passed */
532 static void test_readdirectorychanges_null(void)
537 DWORD fflags, filter = 0;
539 WCHAR path[MAX_PATH], subdir[MAX_PATH];
540 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
541 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
542 PFILE_NOTIFY_INFORMATION pfni;
544 if (!pReadDirectoryChangesW)
547 r = GetTempPathW( MAX_PATH, path );
548 ok( r != 0, "temp path failed\n");
552 lstrcatW( path, szBoo );
553 lstrcpyW( subdir, path );
554 lstrcatW( subdir, szHoo );
556 RemoveDirectoryW( subdir );
557 RemoveDirectoryW( path );
559 r = CreateDirectoryW(path, NULL);
560 ok( r == TRUE, "failed to create directory\n");
562 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
563 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
564 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
565 OPEN_EXISTING, fflags, NULL);
566 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
568 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
570 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
571 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
573 SetLastError(0xd0b00b00);
576 memset( buffer, 0, sizeof buffer );
578 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
579 ok(r==TRUE, "should return true\n");
581 r = WaitForSingleObject( ov.hEvent, 0 );
582 ok( r == STATUS_TIMEOUT, "should timeout\n" );
584 r = CreateDirectoryW( subdir, NULL );
585 ok( r == TRUE, "failed to create directory\n");
587 r = WaitForSingleObject( ov.hEvent, 0 );
588 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
590 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
591 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
596 S(U(ov)).OffsetHigh = 0;
597 memset( buffer, 0, sizeof buffer );
599 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
600 ok(r==TRUE, "should return true\n");
602 r = WaitForSingleObject( ov.hEvent, 0 );
603 ok( r == STATUS_TIMEOUT, "should timeout\n" );
605 r = RemoveDirectoryW( subdir );
606 ok( r == TRUE, "failed to remove directory\n");
608 r = WaitForSingleObject( ov.hEvent, 1000 );
609 ok( r == WAIT_OBJECT_0, "should be ready\n" );
611 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
612 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
614 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
615 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
619 r = RemoveDirectoryW( path );
620 ok( r == TRUE, "failed to remove directory\n");
623 static void test_readdirectorychanges_filedir(void)
628 DWORD fflags, filter = 0;
630 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
631 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
632 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
633 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
634 PFILE_NOTIFY_INFORMATION pfni;
636 r = GetTempPathW( MAX_PATH, path );
637 ok( r != 0, "temp path failed\n");
641 lstrcatW( path, szBoo );
642 lstrcpyW( subdir, path );
643 lstrcatW( subdir, szHoo );
645 lstrcpyW( file, path );
646 lstrcatW( file, szFoo );
649 RemoveDirectoryW( subdir );
650 RemoveDirectoryW( path );
652 r = CreateDirectoryW(path, NULL);
653 ok( r == TRUE, "failed to create directory\n");
655 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
656 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
657 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
658 OPEN_EXISTING, fflags, NULL);
659 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
661 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
663 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
665 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
666 ok(r==TRUE, "should return true\n");
668 r = WaitForSingleObject( ov.hEvent, 10 );
669 ok( r == WAIT_TIMEOUT, "should timeout\n" );
671 r = CreateDirectoryW( subdir, NULL );
672 ok( r == TRUE, "failed to create directory\n");
674 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
675 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
676 ok( CloseHandle(hfile), "failed toc lose file\n");
678 r = WaitForSingleObject( ov.hEvent, 1000 );
679 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
681 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
682 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
684 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
685 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
686 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
687 ok( pfni->FileNameLength == 6, "len wrong\n" );
688 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
690 r = DeleteFileW( file );
691 ok( r == TRUE, "failed to delete file\n");
693 r = RemoveDirectoryW( subdir );
694 ok( r == TRUE, "failed to remove directory\n");
698 r = RemoveDirectoryW( path );
699 ok( r == TRUE, "failed to remove directory\n");
704 HMODULE hkernel32 = GetModuleHandle("kernel32");
705 pReadDirectoryChangesW = (fnReadDirectoryChangesW)
706 GetProcAddress(hkernel32, "ReadDirectoryChangesW");
708 test_FindFirstChangeNotification();
710 test_readdirectorychanges();
711 test_readdirectorychanges_null();
712 test_readdirectorychanges_filedir();