push 421a6d0d1068c98851feb4eb01bb0e754f615ce9
[wine/hacks.git] / dlls / kernel32 / tests / change.c
blob3c5ace388312d680af2846126bb3db38c0a7e278
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 SetLastError(0xdeadbeef);
260 r = GetTempPathW( MAX_PATH, path );
261 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
263 skip("GetTempPathW is not implemented\n");
264 return;
266 ok( r != 0, "temp path failed\n");
267 if (!r)
268 return;
270 lstrcatW( path, szBoo );
271 lstrcpyW( subdir, path );
272 lstrcatW( subdir, szHoo );
274 RemoveDirectoryW( subdir );
275 RemoveDirectoryW( path );
277 r = CreateDirectoryW(path, NULL);
278 ok( r == TRUE, "failed to create directory\n");
280 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
281 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
283 handle = FindFirstChangeNotificationW( path, 1, filter);
284 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
286 r = WaitForSingleObject( handle, 0 );
287 ok( r == STATUS_TIMEOUT, "should time out\n");
289 r = CreateDirectoryW( subdir, NULL );
290 ok( r == TRUE, "failed to create subdir\n");
292 r = WaitForSingleObject( handle, 0 );
293 ok( r == WAIT_OBJECT_0, "should be ready\n");
295 r = WaitForSingleObject( handle, 0 );
296 ok( r == WAIT_OBJECT_0, "should be ready\n");
298 r = FindNextChangeNotification(handle);
299 ok( r == TRUE, "find next failed\n");
301 r = WaitForSingleObject( handle, 0 );
302 ok( r == STATUS_TIMEOUT, "should time out\n");
304 r = RemoveDirectoryW( subdir );
305 ok( r == TRUE, "failed to remove subdir\n");
307 r = WaitForSingleObject( handle, 0 );
308 ok( r == WAIT_OBJECT_0, "should be ready\n");
310 r = WaitForSingleObject( handle, 0 );
311 ok( r == WAIT_OBJECT_0, "should be ready\n");
313 r = FindNextChangeNotification(handle);
314 ok( r == TRUE, "find next failed\n");
316 r = FindNextChangeNotification(handle);
317 ok( r == TRUE, "find next failed\n");
319 r = FindCloseChangeNotification(handle);
320 ok( r == TRUE, "should succeed\n");
322 r = RemoveDirectoryW( path );
323 ok( r == TRUE, "failed to remove dir\n");
326 /* this test concentrates on the wait behavior when multiple threads are
327 * waiting on a change notification handle. */
328 static void test_ffcnMultipleThreads(void)
330 LONG r;
331 DWORD filter, threadId, status, exitcode;
332 HANDLE handles[2];
333 char path[MAX_PATH];
335 r = GetTempPathA(MAX_PATH, path);
336 ok(r, "GetTempPathA error: %d\n", GetLastError());
338 lstrcatA(path, "ffcnTestMultipleThreads");
340 RemoveDirectoryA(path);
342 r = CreateDirectoryA(path, NULL);
343 ok(r, "CreateDirectoryA error: %d\n", GetLastError());
345 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
346 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
348 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
349 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
351 /* Test behavior if a waiting thread holds the last reference to a change
352 * directory object with an empty wine user APC queue for this thread (bug #7286) */
354 /* Create our notification thread */
355 handles[1] = CreateThread(NULL, 0, NotificationThread, (LPVOID)handles[0],
356 0, &threadId);
357 ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
359 status = WaitForMultipleObjects(2, handles, FALSE, 5000);
360 ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError());
361 ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
363 /* Clean up */
364 r = RemoveDirectoryA( path );
365 ok( r == TRUE, "failed to remove dir\n");
368 typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
369 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
370 fnReadDirectoryChangesW pReadDirectoryChangesW;
372 static void test_readdirectorychanges(void)
374 HANDLE hdir;
375 char buffer[0x1000];
376 DWORD fflags, filter = 0, r, dwCount;
377 OVERLAPPED ov;
378 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
379 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
380 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
381 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
382 PFILE_NOTIFY_INFORMATION pfni;
384 if (!pReadDirectoryChangesW)
386 skip("ReadDirectoryChangesW is not available\n");
387 return;
390 r = GetTempPathW( MAX_PATH, path );
391 ok( r != 0, "temp path failed\n");
392 if (!r)
393 return;
395 lstrcatW( path, szBoo );
396 lstrcpyW( subdir, path );
397 lstrcatW( subdir, szHoo );
399 lstrcpyW( subsubdir, path );
400 lstrcatW( subsubdir, szGa );
402 RemoveDirectoryW( subsubdir );
403 RemoveDirectoryW( subdir );
404 RemoveDirectoryW( path );
406 r = CreateDirectoryW(path, NULL);
407 ok( r == TRUE, "failed to create directory\n");
409 SetLastError(0xd0b00b00);
410 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
411 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
412 ok(r==FALSE, "should return false\n");
414 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
415 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
416 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
417 OPEN_EXISTING, fflags, NULL);
418 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
420 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
422 SetLastError(0xd0b00b00);
423 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
424 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
425 ok(r==FALSE, "should return false\n");
427 SetLastError(0xd0b00b00);
428 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
429 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
430 ok(r==FALSE, "should return false\n");
432 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
433 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
434 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
435 filter |= FILE_NOTIFY_CHANGE_SIZE;
436 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
437 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
438 filter |= FILE_NOTIFY_CHANGE_CREATION;
439 filter |= FILE_NOTIFY_CHANGE_SECURITY;
441 SetLastError(0xd0b00b00);
442 ov.Internal = 0;
443 ov.InternalHigh = 0;
444 memset( buffer, 0, sizeof buffer );
446 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
447 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
448 ok(r==FALSE, "should return false\n");
450 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
451 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
452 ok(r==FALSE, "should return false\n");
454 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
455 ok(r==TRUE, "should return true\n");
457 r = WaitForSingleObject( ov.hEvent, 10 );
458 ok( r == STATUS_TIMEOUT, "should timeout\n" );
460 r = CreateDirectoryW( subdir, NULL );
461 ok( r == TRUE, "failed to create directory\n");
463 r = WaitForSingleObject( ov.hEvent, 1000 );
464 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
466 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
467 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
469 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
470 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
471 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
472 ok( pfni->FileNameLength == 6, "len wrong\n" );
473 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
475 ResetEvent(ov.hEvent);
476 SetLastError(0xd0b00b00);
477 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
478 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
479 ok(r==FALSE, "should return false\n");
481 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
482 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
483 ok(r==FALSE, "should return false\n");
485 filter = FILE_NOTIFY_CHANGE_SIZE;
487 SetEvent(ov.hEvent);
488 ov.Internal = 1;
489 ov.InternalHigh = 1;
490 S(U(ov)).Offset = 0;
491 S(U(ov)).OffsetHigh = 0;
492 memset( buffer, 0, sizeof buffer );
493 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
494 ok(r==TRUE, "should return true\n");
496 ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
497 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
499 r = WaitForSingleObject( ov.hEvent, 0 );
500 ok( r == STATUS_TIMEOUT, "should timeout\n" );
502 r = RemoveDirectoryW( subdir );
503 ok( r == TRUE, "failed to remove directory\n");
505 r = WaitForSingleObject( ov.hEvent, 1000 );
506 ok( r == WAIT_OBJECT_0, "should be ready\n" );
508 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
509 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
511 if (ov.Internal == STATUS_SUCCESS)
513 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
514 ok( r == TRUE, "getoverlappedresult failed\n");
515 ok( dwCount == 0x12, "count wrong\n");
518 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
519 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
520 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
521 ok( pfni->FileNameLength == 6, "len wrong\n" );
522 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
524 /* what happens if the buffer is too small? */
525 r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
526 ok(r==TRUE, "should return true\n");
528 r = CreateDirectoryW( subdir, NULL );
529 ok( r == TRUE, "failed to create directory\n");
531 r = WaitForSingleObject( ov.hEvent, 1000 );
532 ok( r == WAIT_OBJECT_0, "should be ready\n" );
534 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
535 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
537 /* test the recursive watch */
538 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
539 ok(r==TRUE, "should return true\n");
541 r = CreateDirectoryW( subsubdir, NULL );
542 ok( r == TRUE, "failed to create directory\n");
544 r = WaitForSingleObject( ov.hEvent, 1000 );
545 ok( r == WAIT_OBJECT_0, "should be ready\n" );
547 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
548 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
550 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
551 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
552 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
553 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
554 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
556 r = RemoveDirectoryW( subsubdir );
557 ok( r == TRUE, "failed to remove directory\n");
559 ov.Internal = 1;
560 ov.InternalHigh = 1;
561 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
562 ok(r==TRUE, "should return true\n");
564 r = RemoveDirectoryW( subdir );
565 ok( r == TRUE, "failed to remove directory\n");
567 r = WaitForSingleObject( ov.hEvent, 1000 );
568 ok( r == WAIT_OBJECT_0, "should be ready\n" );
570 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
571 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
572 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
573 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
574 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
576 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
577 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
579 CloseHandle(hdir);
581 r = RemoveDirectoryW( path );
582 ok( r == TRUE, "failed to remove directory\n");
585 /* show the behaviour when a null buffer is passed */
586 static void test_readdirectorychanges_null(void)
588 NTSTATUS r;
589 HANDLE hdir;
590 char buffer[0x1000];
591 DWORD fflags, filter = 0;
592 OVERLAPPED ov;
593 WCHAR path[MAX_PATH], subdir[MAX_PATH];
594 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
595 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
596 PFILE_NOTIFY_INFORMATION pfni;
598 if (!pReadDirectoryChangesW)
600 skip("ReadDirectoryChangesW is not available\n");
601 return;
604 r = GetTempPathW( MAX_PATH, path );
605 ok( r != 0, "temp path failed\n");
606 if (!r)
607 return;
609 lstrcatW( path, szBoo );
610 lstrcpyW( subdir, path );
611 lstrcatW( subdir, szHoo );
613 RemoveDirectoryW( subdir );
614 RemoveDirectoryW( path );
616 r = CreateDirectoryW(path, NULL);
617 ok( r == TRUE, "failed to create directory\n");
619 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
620 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
621 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
622 OPEN_EXISTING, fflags, NULL);
623 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
625 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
627 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
628 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
630 SetLastError(0xd0b00b00);
631 ov.Internal = 0;
632 ov.InternalHigh = 0;
633 memset( buffer, 0, sizeof buffer );
635 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
636 ok(r==TRUE, "should return true\n");
638 r = WaitForSingleObject( ov.hEvent, 0 );
639 ok( r == STATUS_TIMEOUT, "should timeout\n" );
641 r = CreateDirectoryW( subdir, NULL );
642 ok( r == TRUE, "failed to create directory\n");
644 r = WaitForSingleObject( ov.hEvent, 0 );
645 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
647 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
648 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
650 ov.Internal = 0;
651 ov.InternalHigh = 0;
652 S(U(ov)).Offset = 0;
653 S(U(ov)).OffsetHigh = 0;
654 memset( buffer, 0, sizeof buffer );
656 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
657 ok(r==TRUE, "should return true\n");
659 r = WaitForSingleObject( ov.hEvent, 0 );
660 ok( r == STATUS_TIMEOUT, "should timeout\n" );
662 r = RemoveDirectoryW( subdir );
663 ok( r == TRUE, "failed to remove directory\n");
665 r = WaitForSingleObject( ov.hEvent, 1000 );
666 ok( r == WAIT_OBJECT_0, "should be ready\n" );
668 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
669 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
671 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
672 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
674 CloseHandle(hdir);
676 r = RemoveDirectoryW( path );
677 ok( r == TRUE, "failed to remove directory\n");
680 static void test_readdirectorychanges_filedir(void)
682 NTSTATUS r;
683 HANDLE hdir, hfile;
684 char buffer[0x1000];
685 DWORD fflags, filter = 0;
686 OVERLAPPED ov;
687 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
688 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
689 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
690 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
691 PFILE_NOTIFY_INFORMATION pfni;
693 SetLastError(0xdeadbeef);
694 r = GetTempPathW( MAX_PATH, path );
695 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
697 skip("GetTempPathW is not implemented\n");
698 return;
700 ok( r != 0, "temp path failed\n");
701 if (!r)
702 return;
704 lstrcatW( path, szBoo );
705 lstrcpyW( subdir, path );
706 lstrcatW( subdir, szHoo );
708 lstrcpyW( file, path );
709 lstrcatW( file, szFoo );
711 DeleteFileW( file );
712 RemoveDirectoryW( subdir );
713 RemoveDirectoryW( path );
715 r = CreateDirectoryW(path, NULL);
716 ok( r == TRUE, "failed to create directory\n");
718 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
719 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
720 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
721 OPEN_EXISTING, fflags, NULL);
722 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
724 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
726 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
728 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
729 ok(r==TRUE, "should return true\n");
731 r = WaitForSingleObject( ov.hEvent, 10 );
732 ok( r == WAIT_TIMEOUT, "should timeout\n" );
734 r = CreateDirectoryW( subdir, NULL );
735 ok( r == TRUE, "failed to create directory\n");
737 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
738 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
739 ok( CloseHandle(hfile), "failed toc lose file\n");
741 r = WaitForSingleObject( ov.hEvent, 1000 );
742 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
744 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
745 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
747 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
748 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
749 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
750 ok( pfni->FileNameLength == 6, "len wrong\n" );
751 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
753 r = DeleteFileW( file );
754 ok( r == TRUE, "failed to delete file\n");
756 r = RemoveDirectoryW( subdir );
757 ok( r == TRUE, "failed to remove directory\n");
759 CloseHandle(hdir);
761 r = RemoveDirectoryW( path );
762 ok( r == TRUE, "failed to remove directory\n");
765 START_TEST(change)
767 HMODULE hkernel32 = GetModuleHandle("kernel32");
768 pReadDirectoryChangesW = (fnReadDirectoryChangesW)
769 GetProcAddress(hkernel32, "ReadDirectoryChangesW");
771 test_ffcnMultipleThreads();
772 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
773 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
774 placed first. */
775 test_FindFirstChangeNotification();
776 test_ffcn();
777 test_readdirectorychanges();
778 test_readdirectorychanges_null();
779 test_readdirectorychanges_filedir();