new 818051de2c8769029049ce3d36c6b856f47496c9
[wine/hacks.git] / dlls / kernel32 / tests / change.c
blob9b6c52be3e0e1a3c282ebb09025d4cb80d2e19d0
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., 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
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: %d\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: %d\n", GetLastError());
66 thread = CreateThread(NULL, 0, NotificationThread, (LPVOID)change,
67 0, &threadId);
68 ok(thread != NULL, "CreateThread error: %d\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 %d error %d\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: %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",
111 GetLastError());
113 ret = FindCloseChangeNotification(NULL);
114 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
115 GetLastError());
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");
187 /* Rename a file */
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");
193 /* Delete a file */
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");
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: %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");
240 /* clean up */
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)
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 /* this test concentrates on the wait behavior when multiple threads are
321 * waiting on a change notification handle. */
322 static void test_ffcnMultipleThreads(void)
324 LONG r;
325 DWORD filter, threadId, status, exitcode;
326 HANDLE handles[2];
327 char path[MAX_PATH];
329 r = GetTempPathA(MAX_PATH, path);
330 ok(r, "GetTempPathA error: %d\n", GetLastError());
332 lstrcatA(path, "ffcnTestMultipleThreads");
334 RemoveDirectoryA(path);
336 r = CreateDirectoryA(path, NULL);
337 ok(r, "CreateDirectoryA error: %d\n", GetLastError());
339 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
340 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
342 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
343 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
345 /* Test behavior if a waiting thread holds the last reference to a change
346 * directory object with an empty wine user APC queue for this thread (bug #7286) */
348 /* Create our notification thread */
349 handles[1] = CreateThread(NULL, 0, NotificationThread, (LPVOID)handles[0],
350 0, &threadId);
351 ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
353 status = WaitForMultipleObjects(2, handles, FALSE, 5000);
354 ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError());
355 ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
357 /* Clean up */
358 r = RemoveDirectoryA( path );
359 ok( r == TRUE, "failed to remove dir\n");
362 typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
363 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
364 fnReadDirectoryChangesW pReadDirectoryChangesW;
366 static void test_readdirectorychanges(void)
368 HANDLE hdir;
369 char buffer[0x1000];
370 DWORD fflags, filter = 0, r, dwCount;
371 OVERLAPPED ov;
372 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
373 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
374 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
375 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
376 PFILE_NOTIFY_INFORMATION pfni;
378 if (!pReadDirectoryChangesW)
379 return;
381 r = GetTempPathW( MAX_PATH, path );
382 ok( r != 0, "temp path failed\n");
383 if (!r)
384 return;
386 lstrcatW( path, szBoo );
387 lstrcpyW( subdir, path );
388 lstrcatW( subdir, szHoo );
390 lstrcpyW( subsubdir, path );
391 lstrcatW( subsubdir, szGa );
393 RemoveDirectoryW( subsubdir );
394 RemoveDirectoryW( subdir );
395 RemoveDirectoryW( path );
397 r = CreateDirectoryW(path, NULL);
398 ok( r == TRUE, "failed to create directory\n");
400 SetLastError(0xd0b00b00);
401 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
402 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
403 ok(r==FALSE, "should return false\n");
405 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
406 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
407 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
408 OPEN_EXISTING, fflags, NULL);
409 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
411 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
413 SetLastError(0xd0b00b00);
414 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
415 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
416 ok(r==FALSE, "should return false\n");
418 SetLastError(0xd0b00b00);
419 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
420 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
421 ok(r==FALSE, "should return false\n");
423 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
424 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
425 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
426 filter |= FILE_NOTIFY_CHANGE_SIZE;
427 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
428 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
429 filter |= FILE_NOTIFY_CHANGE_CREATION;
430 filter |= FILE_NOTIFY_CHANGE_SECURITY;
432 SetLastError(0xd0b00b00);
433 ov.Internal = 0;
434 ov.InternalHigh = 0;
435 memset( buffer, 0, sizeof buffer );
437 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
438 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
439 ok(r==FALSE, "should return false\n");
441 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
442 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
443 ok(r==FALSE, "should return false\n");
445 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
446 ok(r==TRUE, "should return true\n");
448 r = WaitForSingleObject( ov.hEvent, 10 );
449 ok( r == STATUS_TIMEOUT, "should timeout\n" );
451 r = CreateDirectoryW( subdir, NULL );
452 ok( r == TRUE, "failed to create directory\n");
454 r = WaitForSingleObject( ov.hEvent, 1000 );
455 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
457 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
458 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
460 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
461 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
462 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
463 ok( pfni->FileNameLength == 6, "len wrong\n" );
464 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
466 ResetEvent(ov.hEvent);
467 SetLastError(0xd0b00b00);
468 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
469 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
470 ok(r==FALSE, "should return false\n");
472 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
473 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
474 ok(r==FALSE, "should return false\n");
476 filter = FILE_NOTIFY_CHANGE_SIZE;
478 SetEvent(ov.hEvent);
479 ov.Internal = 1;
480 ov.InternalHigh = 1;
481 S(U(ov)).Offset = 0;
482 S(U(ov)).OffsetHigh = 0;
483 memset( buffer, 0, sizeof buffer );
484 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
485 ok(r==TRUE, "should return true\n");
487 ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
488 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
490 r = WaitForSingleObject( ov.hEvent, 0 );
491 ok( r == STATUS_TIMEOUT, "should timeout\n" );
493 r = RemoveDirectoryW( subdir );
494 ok( r == TRUE, "failed to remove directory\n");
496 r = WaitForSingleObject( ov.hEvent, 1000 );
497 ok( r == WAIT_OBJECT_0, "should be ready\n" );
499 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
500 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
502 if (ov.Internal == STATUS_SUCCESS)
504 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
505 ok( r == TRUE, "getoverlappedresult failed\n");
506 ok( dwCount == 0x12, "count wrong\n");
509 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
510 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
511 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
512 ok( pfni->FileNameLength == 6, "len wrong\n" );
513 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
515 /* what happens if the buffer is too small? */
516 r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
517 ok(r==TRUE, "should return true\n");
519 r = CreateDirectoryW( subdir, NULL );
520 ok( r == TRUE, "failed to create directory\n");
522 r = WaitForSingleObject( ov.hEvent, 1000 );
523 ok( r == WAIT_OBJECT_0, "should be ready\n" );
525 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
526 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
528 /* test the recursive watch */
529 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
530 ok(r==TRUE, "should return true\n");
532 r = CreateDirectoryW( subsubdir, NULL );
533 ok( r == TRUE, "failed to create directory\n");
535 r = WaitForSingleObject( ov.hEvent, 1000 );
536 ok( r == WAIT_OBJECT_0, "should be ready\n" );
538 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
539 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
541 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
542 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
543 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
544 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
545 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
547 r = RemoveDirectoryW( subsubdir );
548 ok( r == TRUE, "failed to remove directory\n");
550 ov.Internal = 1;
551 ov.InternalHigh = 1;
552 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
553 ok(r==TRUE, "should return true\n");
555 r = RemoveDirectoryW( subdir );
556 ok( r == TRUE, "failed to remove directory\n");
558 r = WaitForSingleObject( ov.hEvent, 1000 );
559 ok( r == WAIT_OBJECT_0, "should be ready\n" );
561 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
562 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
563 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
564 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
565 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
567 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
568 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
570 CloseHandle(hdir);
572 r = RemoveDirectoryW( path );
573 ok( r == TRUE, "failed to remove directory\n");
576 /* show the behaviour when a null buffer is passed */
577 static void test_readdirectorychanges_null(void)
579 NTSTATUS r;
580 HANDLE hdir;
581 char buffer[0x1000];
582 DWORD fflags, filter = 0;
583 OVERLAPPED ov;
584 WCHAR path[MAX_PATH], subdir[MAX_PATH];
585 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
586 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
587 PFILE_NOTIFY_INFORMATION pfni;
589 if (!pReadDirectoryChangesW)
590 return;
592 r = GetTempPathW( MAX_PATH, path );
593 ok( r != 0, "temp path failed\n");
594 if (!r)
595 return;
597 lstrcatW( path, szBoo );
598 lstrcpyW( subdir, path );
599 lstrcatW( subdir, szHoo );
601 RemoveDirectoryW( subdir );
602 RemoveDirectoryW( path );
604 r = CreateDirectoryW(path, NULL);
605 ok( r == TRUE, "failed to create directory\n");
607 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
608 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
609 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
610 OPEN_EXISTING, fflags, NULL);
611 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
613 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
615 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
616 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
618 SetLastError(0xd0b00b00);
619 ov.Internal = 0;
620 ov.InternalHigh = 0;
621 memset( buffer, 0, sizeof buffer );
623 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
624 ok(r==TRUE, "should return true\n");
626 r = WaitForSingleObject( ov.hEvent, 0 );
627 ok( r == STATUS_TIMEOUT, "should timeout\n" );
629 r = CreateDirectoryW( subdir, NULL );
630 ok( r == TRUE, "failed to create directory\n");
632 r = WaitForSingleObject( ov.hEvent, 0 );
633 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
635 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
636 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
638 ov.Internal = 0;
639 ov.InternalHigh = 0;
640 S(U(ov)).Offset = 0;
641 S(U(ov)).OffsetHigh = 0;
642 memset( buffer, 0, sizeof buffer );
644 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
645 ok(r==TRUE, "should return true\n");
647 r = WaitForSingleObject( ov.hEvent, 0 );
648 ok( r == STATUS_TIMEOUT, "should timeout\n" );
650 r = RemoveDirectoryW( subdir );
651 ok( r == TRUE, "failed to remove directory\n");
653 r = WaitForSingleObject( ov.hEvent, 1000 );
654 ok( r == WAIT_OBJECT_0, "should be ready\n" );
656 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
657 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
659 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
660 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
662 CloseHandle(hdir);
664 r = RemoveDirectoryW( path );
665 ok( r == TRUE, "failed to remove directory\n");
668 static void test_readdirectorychanges_filedir(void)
670 NTSTATUS r;
671 HANDLE hdir, hfile;
672 char buffer[0x1000];
673 DWORD fflags, filter = 0;
674 OVERLAPPED ov;
675 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
676 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
677 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
678 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
679 PFILE_NOTIFY_INFORMATION pfni;
681 r = GetTempPathW( MAX_PATH, path );
682 ok( r != 0, "temp path failed\n");
683 if (!r)
684 return;
686 lstrcatW( path, szBoo );
687 lstrcpyW( subdir, path );
688 lstrcatW( subdir, szHoo );
690 lstrcpyW( file, path );
691 lstrcatW( file, szFoo );
693 DeleteFileW( file );
694 RemoveDirectoryW( subdir );
695 RemoveDirectoryW( path );
697 r = CreateDirectoryW(path, NULL);
698 ok( r == TRUE, "failed to create directory\n");
700 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
701 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
702 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
703 OPEN_EXISTING, fflags, NULL);
704 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
706 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
708 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
710 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
711 ok(r==TRUE, "should return true\n");
713 r = WaitForSingleObject( ov.hEvent, 10 );
714 ok( r == WAIT_TIMEOUT, "should timeout\n" );
716 r = CreateDirectoryW( subdir, NULL );
717 ok( r == TRUE, "failed to create directory\n");
719 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
720 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
721 ok( CloseHandle(hfile), "failed toc lose file\n");
723 r = WaitForSingleObject( ov.hEvent, 1000 );
724 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
726 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
727 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
729 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
730 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
731 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
732 ok( pfni->FileNameLength == 6, "len wrong\n" );
733 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
735 r = DeleteFileW( file );
736 ok( r == TRUE, "failed to delete file\n");
738 r = RemoveDirectoryW( subdir );
739 ok( r == TRUE, "failed to remove directory\n");
741 CloseHandle(hdir);
743 r = RemoveDirectoryW( path );
744 ok( r == TRUE, "failed to remove directory\n");
747 START_TEST(change)
749 HMODULE hkernel32 = GetModuleHandle("kernel32");
750 pReadDirectoryChangesW = (fnReadDirectoryChangesW)
751 GetProcAddress(hkernel32, "ReadDirectoryChangesW");
753 test_ffcnMultipleThreads();
754 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
755 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
756 placed first. */
757 test_FindFirstChangeNotification();
758 test_ffcn();
759 test_readdirectorychanges();
760 test_readdirectorychanges_null();
761 test_readdirectorychanges_filedir();