server: Distinguish between a directory and a file changing in
[wine/hacks.git] / dlls / kernel / tests / change.c
blobdfdaa9e6694fb78df001192ab9312a665dd527c1
1 /*
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
29 #include <stdarg.h>
30 #include <stdio.h>
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "wine/test.h"
35 #include <windef.h>
36 #include <winbase.h>
37 #include <winternl.h>
39 static DWORD CALLBACK NotificationThread(LPVOID arg)
41 HANDLE change = (HANDLE) arg;
42 BOOL ret = FALSE;
43 DWORD status;
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",
53 GetLastError());
55 ExitThread((DWORD)ret);
58 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
60 HANDLE change, thread;
61 DWORD threadId;
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,
67 0, &threadId);
68 ok(thread != INVALID_HANDLE_VALUE, "CreateThread error: %ld\n", GetLastError());
70 return thread;
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");
82 return exitcode;
85 static void test_FindFirstChangeNotification(void)
87 HANDLE change, file, thread;
88 DWORD attributes, count;
89 BOOL ret;
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";
94 char buffer[2048];
96 /* pathetic checks */
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",
111 GetLastError());
113 ret = FindCloseChangeNotification(NULL);
114 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n",
115 GetLastError());
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");
187 /* Rename a file */
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");
193 /* Delete a file */
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");
199 /* Create a file */
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");
240 /* clean up */
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)
252 DWORD filter;
253 HANDLE handle;
254 LONG r;
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");
261 if (!r)
262 return;
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)
326 HANDLE hdir;
327 char buffer[0x1000];
328 DWORD fflags, filter = 0, r, dwCount;
329 OVERLAPPED ov;
330 WCHAR path[MAX_PATH], subdir[MAX_PATH];
331 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
332 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
333 PFILE_NOTIFY_INFORMATION pfni;
335 if (!pReadDirectoryChangesW)
336 return;
338 r = GetTempPathW( MAX_PATH, path );
339 ok( r != 0, "temp path failed\n");
340 if (!r)
341 return;
343 lstrcatW( path, szBoo );
344 lstrcpyW( subdir, path );
345 lstrcatW( subdir, szHoo );
347 RemoveDirectoryW( subdir );
348 RemoveDirectoryW( path );
350 r = CreateDirectoryW(path, NULL);
351 ok( r == TRUE, "failed to create directory\n");
353 SetLastError(0xd0b00b00);
354 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
355 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
356 ok(r==FALSE, "should return false\n");
358 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
359 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
360 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
361 OPEN_EXISTING, fflags, NULL);
362 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
364 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
366 SetLastError(0xd0b00b00);
367 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
368 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
369 ok(r==FALSE, "should return false\n");
371 SetLastError(0xd0b00b00);
372 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
373 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
374 ok(r==FALSE, "should return false\n");
376 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
377 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
378 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
379 filter |= FILE_NOTIFY_CHANGE_SIZE;
380 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
381 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
382 filter |= FILE_NOTIFY_CHANGE_CREATION;
383 filter |= FILE_NOTIFY_CHANGE_SECURITY;
385 SetLastError(0xd0b00b00);
386 ov.Internal = 0;
387 ov.InternalHigh = 0;
388 memset( buffer, 0, sizeof buffer );
390 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
391 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
392 ok(r==FALSE, "should return false\n");
394 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
395 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
396 ok(r==FALSE, "should return false\n");
398 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
399 ok(r==TRUE, "should return true\n");
401 r = WaitForSingleObject( ov.hEvent, 10 );
402 ok( r == STATUS_TIMEOUT, "should timeout\n" );
404 r = CreateDirectoryW( subdir, NULL );
405 ok( r == TRUE, "failed to create directory\n");
407 r = WaitForSingleObject( ov.hEvent, INFINITE );
408 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
410 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
411 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
413 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
414 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
415 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
416 ok( pfni->FileNameLength == 6, "len wrong\n" );
417 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
419 ResetEvent(ov.hEvent);
420 SetLastError(0xd0b00b00);
421 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
422 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
423 ok(r==FALSE, "should return false\n");
425 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
426 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
427 ok(r==FALSE, "should return false\n");
429 filter = FILE_NOTIFY_CHANGE_SIZE;
431 SetEvent(ov.hEvent);
432 ov.Internal = 1;
433 ov.InternalHigh = 1;
434 S(U(ov)).Offset = 0;
435 S(U(ov)).OffsetHigh = 0;
436 memset( buffer, 0, sizeof buffer );
437 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
438 ok(r==TRUE, "should return true\n");
440 ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
441 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
443 r = WaitForSingleObject( ov.hEvent, 0 );
444 ok( r == STATUS_TIMEOUT, "should timeout\n" );
446 r = RemoveDirectoryW( subdir );
447 ok( r == TRUE, "failed to remove directory\n");
449 r = WaitForSingleObject( ov.hEvent, INFINITE );
450 ok( r == WAIT_OBJECT_0, "should be ready\n" );
452 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
453 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
455 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
456 ok( r == TRUE, "getoverlappedresult failed\n");
457 ok( dwCount == 0x12, "count wrong\n");
459 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
460 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
461 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
462 ok( pfni->FileNameLength == 6, "len wrong\n" );
463 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
465 CloseHandle(hdir);
467 r = RemoveDirectoryW( path );
468 ok( r == TRUE, "failed to remove directory\n");
471 /* show the behaviour when a null buffer is passed */
472 static void test_readdirectorychanges_null(void)
474 NTSTATUS r;
475 HANDLE hdir;
476 char buffer[0x1000];
477 DWORD fflags, filter = 0;
478 OVERLAPPED ov;
479 WCHAR path[MAX_PATH], subdir[MAX_PATH];
480 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
481 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
482 PFILE_NOTIFY_INFORMATION pfni;
484 if (!pReadDirectoryChangesW)
485 return;
487 r = GetTempPathW( MAX_PATH, path );
488 ok( r != 0, "temp path failed\n");
489 if (!r)
490 return;
492 lstrcatW( path, szBoo );
493 lstrcpyW( subdir, path );
494 lstrcatW( subdir, szHoo );
496 RemoveDirectoryW( subdir );
497 RemoveDirectoryW( path );
499 r = CreateDirectoryW(path, NULL);
500 ok( r == TRUE, "failed to create directory\n");
502 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
503 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
504 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
505 OPEN_EXISTING, fflags, NULL);
506 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
508 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
510 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
511 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
513 SetLastError(0xd0b00b00);
514 ov.Internal = 0;
515 ov.InternalHigh = 0;
516 memset( buffer, 0, sizeof buffer );
518 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
519 ok(r==TRUE, "should return true\n");
521 r = WaitForSingleObject( ov.hEvent, 0 );
522 ok( r == STATUS_TIMEOUT, "should timeout\n" );
524 r = CreateDirectoryW( subdir, NULL );
525 ok( r == TRUE, "failed to create directory\n");
527 r = WaitForSingleObject( ov.hEvent, 0 );
528 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
530 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
531 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
533 ov.Internal = 0;
534 ov.InternalHigh = 0;
535 S(U(ov)).Offset = 0;
536 S(U(ov)).OffsetHigh = 0;
537 memset( buffer, 0, sizeof buffer );
539 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
540 ok(r==TRUE, "should return true\n");
542 r = WaitForSingleObject( ov.hEvent, 0 );
543 ok( r == STATUS_TIMEOUT, "should timeout\n" );
545 r = RemoveDirectoryW( subdir );
546 ok( r == TRUE, "failed to remove directory\n");
548 r = WaitForSingleObject( ov.hEvent, INFINITE );
549 ok( r == WAIT_OBJECT_0, "should be ready\n" );
551 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
552 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
554 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
555 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
557 CloseHandle(hdir);
559 r = RemoveDirectoryW( path );
560 ok( r == TRUE, "failed to remove directory\n");
563 static void test_readdirectorychanges_filedir(void)
565 NTSTATUS r;
566 HANDLE hdir, hfile;
567 char buffer[0x1000];
568 DWORD fflags, filter = 0;
569 OVERLAPPED ov;
570 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
571 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
572 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
573 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
574 PFILE_NOTIFY_INFORMATION pfni;
576 r = GetTempPathW( MAX_PATH, path );
577 ok( r != 0, "temp path failed\n");
578 if (!r)
579 return;
581 lstrcatW( path, szBoo );
582 lstrcpyW( subdir, path );
583 lstrcatW( subdir, szHoo );
585 lstrcpyW( file, path );
586 lstrcatW( file, szFoo );
588 DeleteFileW( file );
589 RemoveDirectoryW( subdir );
590 RemoveDirectoryW( path );
592 r = CreateDirectoryW(path, NULL);
593 ok( r == TRUE, "failed to create directory\n");
595 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
596 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
597 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
598 OPEN_EXISTING, fflags, NULL);
599 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
601 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
603 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
605 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
606 ok(r==TRUE, "should return true\n");
608 r = WaitForSingleObject( ov.hEvent, 10 );
609 ok( r == WAIT_TIMEOUT, "should timeout\n" );
611 r = CreateDirectoryW( subdir, NULL );
612 ok( r == TRUE, "failed to create directory\n");
614 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
615 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
616 ok( CloseHandle(hfile), "failed toc lose file\n");
618 r = WaitForSingleObject( ov.hEvent, INFINITE );
619 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
621 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
622 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
624 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
625 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
626 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
627 ok( pfni->FileNameLength == 6, "len wrong\n" );
628 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
630 r = DeleteFileW( file );
631 ok( r == TRUE, "failed to delete file\n");
633 r = RemoveDirectoryW( subdir );
634 ok( r == TRUE, "failed to remove directory\n");
636 CloseHandle(hdir);
638 r = RemoveDirectoryW( path );
639 ok( r == TRUE, "failed to remove directory\n");
642 START_TEST(change)
644 HMODULE hkernel32 = GetModuleHandle("kernel32");
645 pReadDirectoryChangesW = (fnReadDirectoryChangesW)
646 GetProcAddress(hkernel32, "ReadDirectoryChangesW");
648 test_FindFirstChangeNotification();
649 test_ffcn();
650 test_readdirectorychanges();
651 test_readdirectorychanges_null();
652 test_readdirectorychanges_filedir();