push f2fb4b43e6a5ad960b5745b9c70e0a8fefaca935
[wine/hacks.git] / dlls / kernel32 / tests / change.c
blobbb1f73d143dc20f7750b2cacc1c29aae9e6d363c
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 SetLastError(0xdeadbeef);
391 r = GetTempPathW( MAX_PATH, path );
392 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
394 skip("GetTempPathW is not implemented\n");
395 return;
397 ok( r != 0, "temp path failed\n");
398 if (!r)
399 return;
401 lstrcatW( path, szBoo );
402 lstrcpyW( subdir, path );
403 lstrcatW( subdir, szHoo );
405 lstrcpyW( subsubdir, path );
406 lstrcatW( subsubdir, szGa );
408 RemoveDirectoryW( subsubdir );
409 RemoveDirectoryW( subdir );
410 RemoveDirectoryW( path );
412 r = CreateDirectoryW(path, NULL);
413 ok( r == TRUE, "failed to create directory\n");
415 SetLastError(0xd0b00b00);
416 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
417 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
418 ok(r==FALSE, "should return false\n");
420 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
421 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
422 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
423 OPEN_EXISTING, fflags, NULL);
424 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
426 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
428 SetLastError(0xd0b00b00);
429 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
430 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
431 ok(r==FALSE, "should return false\n");
433 SetLastError(0xd0b00b00);
434 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
435 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
436 ok(r==FALSE, "should return false\n");
438 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
439 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
440 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
441 filter |= FILE_NOTIFY_CHANGE_SIZE;
442 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
443 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
444 filter |= FILE_NOTIFY_CHANGE_CREATION;
445 filter |= FILE_NOTIFY_CHANGE_SECURITY;
447 SetLastError(0xd0b00b00);
448 ov.Internal = 0;
449 ov.InternalHigh = 0;
450 memset( buffer, 0, sizeof buffer );
452 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
453 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
454 ok(r==FALSE, "should return false\n");
456 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
457 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
458 ok(r==FALSE, "should return false\n");
460 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
461 ok(r==TRUE, "should return true\n");
463 r = WaitForSingleObject( ov.hEvent, 10 );
464 ok( r == STATUS_TIMEOUT, "should timeout\n" );
466 r = CreateDirectoryW( subdir, NULL );
467 ok( r == TRUE, "failed to create directory\n");
469 r = WaitForSingleObject( ov.hEvent, 1000 );
470 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
472 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
473 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
475 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
476 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
477 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
478 ok( pfni->FileNameLength == 6, "len wrong\n" );
479 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
481 ResetEvent(ov.hEvent);
482 SetLastError(0xd0b00b00);
483 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
484 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
485 ok(r==FALSE, "should return false\n");
487 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
488 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
489 ok(r==FALSE, "should return false\n");
491 filter = FILE_NOTIFY_CHANGE_SIZE;
493 SetEvent(ov.hEvent);
494 ov.Internal = 1;
495 ov.InternalHigh = 1;
496 S(U(ov)).Offset = 0;
497 S(U(ov)).OffsetHigh = 0;
498 memset( buffer, 0, sizeof buffer );
499 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
500 ok(r==TRUE, "should return true\n");
502 ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
503 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
505 r = WaitForSingleObject( ov.hEvent, 0 );
506 ok( r == STATUS_TIMEOUT, "should timeout\n" );
508 r = RemoveDirectoryW( subdir );
509 ok( r == TRUE, "failed to remove directory\n");
511 r = WaitForSingleObject( ov.hEvent, 1000 );
512 ok( r == WAIT_OBJECT_0, "should be ready\n" );
514 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
515 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
517 if (ov.Internal == STATUS_SUCCESS)
519 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
520 ok( r == TRUE, "getoverlappedresult failed\n");
521 ok( dwCount == 0x12, "count wrong\n");
524 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
525 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
526 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
527 ok( pfni->FileNameLength == 6, "len wrong\n" );
528 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
530 /* what happens if the buffer is too small? */
531 r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
532 ok(r==TRUE, "should return true\n");
534 r = CreateDirectoryW( subdir, NULL );
535 ok( r == TRUE, "failed to create directory\n");
537 r = WaitForSingleObject( ov.hEvent, 1000 );
538 ok( r == WAIT_OBJECT_0, "should be ready\n" );
540 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
541 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
543 /* test the recursive watch */
544 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
545 ok(r==TRUE, "should return true\n");
547 r = CreateDirectoryW( subsubdir, NULL );
548 ok( r == TRUE, "failed to create directory\n");
550 r = WaitForSingleObject( ov.hEvent, 1000 );
551 ok( r == WAIT_OBJECT_0, "should be ready\n" );
553 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
554 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
556 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
557 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
558 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
559 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
560 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
562 r = RemoveDirectoryW( subsubdir );
563 ok( r == TRUE, "failed to remove directory\n");
565 ov.Internal = 1;
566 ov.InternalHigh = 1;
567 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
568 ok(r==TRUE, "should return true\n");
570 r = RemoveDirectoryW( subdir );
571 ok( r == TRUE, "failed to remove directory\n");
573 r = WaitForSingleObject( ov.hEvent, 1000 );
574 ok( r == WAIT_OBJECT_0, "should be ready\n" );
576 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
577 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
578 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
579 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
580 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
582 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
583 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
585 CloseHandle(hdir);
587 r = RemoveDirectoryW( path );
588 ok( r == TRUE, "failed to remove directory\n");
591 /* show the behaviour when a null buffer is passed */
592 static void test_readdirectorychanges_null(void)
594 NTSTATUS r;
595 HANDLE hdir;
596 char buffer[0x1000];
597 DWORD fflags, filter = 0;
598 OVERLAPPED ov;
599 WCHAR path[MAX_PATH], subdir[MAX_PATH];
600 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
601 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
602 PFILE_NOTIFY_INFORMATION pfni;
604 if (!pReadDirectoryChangesW)
606 skip("ReadDirectoryChangesW is not available\n");
607 return;
609 SetLastError(0xdeadbeef);
610 r = GetTempPathW( MAX_PATH, path );
611 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
613 skip("GetTempPathW is not implemented\n");
614 return;
616 ok( r != 0, "temp path failed\n");
617 if (!r)
618 return;
620 lstrcatW( path, szBoo );
621 lstrcpyW( subdir, path );
622 lstrcatW( subdir, szHoo );
624 RemoveDirectoryW( subdir );
625 RemoveDirectoryW( path );
627 r = CreateDirectoryW(path, NULL);
628 ok( r == TRUE, "failed to create directory\n");
630 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
631 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
632 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
633 OPEN_EXISTING, fflags, NULL);
634 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
636 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
638 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
639 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
641 SetLastError(0xd0b00b00);
642 ov.Internal = 0;
643 ov.InternalHigh = 0;
644 memset( buffer, 0, sizeof buffer );
646 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
647 ok(r==TRUE, "should return true\n");
649 r = WaitForSingleObject( ov.hEvent, 0 );
650 ok( r == STATUS_TIMEOUT, "should timeout\n" );
652 r = CreateDirectoryW( subdir, NULL );
653 ok( r == TRUE, "failed to create directory\n");
655 r = WaitForSingleObject( ov.hEvent, 0 );
656 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
658 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
659 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
661 ov.Internal = 0;
662 ov.InternalHigh = 0;
663 S(U(ov)).Offset = 0;
664 S(U(ov)).OffsetHigh = 0;
665 memset( buffer, 0, sizeof buffer );
667 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
668 ok(r==TRUE, "should return true\n");
670 r = WaitForSingleObject( ov.hEvent, 0 );
671 ok( r == STATUS_TIMEOUT, "should timeout\n" );
673 r = RemoveDirectoryW( subdir );
674 ok( r == TRUE, "failed to remove directory\n");
676 r = WaitForSingleObject( ov.hEvent, 1000 );
677 ok( r == WAIT_OBJECT_0, "should be ready\n" );
679 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
680 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
682 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
683 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
685 CloseHandle(hdir);
687 r = RemoveDirectoryW( path );
688 ok( r == TRUE, "failed to remove directory\n");
691 static void test_readdirectorychanges_filedir(void)
693 NTSTATUS r;
694 HANDLE hdir, hfile;
695 char buffer[0x1000];
696 DWORD fflags, filter = 0;
697 OVERLAPPED ov;
698 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
699 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
700 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
701 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
702 PFILE_NOTIFY_INFORMATION pfni;
704 SetLastError(0xdeadbeef);
705 r = GetTempPathW( MAX_PATH, path );
706 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
708 skip("GetTempPathW is not implemented\n");
709 return;
711 ok( r != 0, "temp path failed\n");
712 if (!r)
713 return;
715 lstrcatW( path, szBoo );
716 lstrcpyW( subdir, path );
717 lstrcatW( subdir, szHoo );
719 lstrcpyW( file, path );
720 lstrcatW( file, szFoo );
722 DeleteFileW( file );
723 RemoveDirectoryW( subdir );
724 RemoveDirectoryW( path );
726 r = CreateDirectoryW(path, NULL);
727 ok( r == TRUE, "failed to create directory\n");
729 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
730 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
731 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
732 OPEN_EXISTING, fflags, NULL);
733 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
735 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
737 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
739 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
740 ok(r==TRUE, "should return true\n");
742 r = WaitForSingleObject( ov.hEvent, 10 );
743 ok( r == WAIT_TIMEOUT, "should timeout\n" );
745 r = CreateDirectoryW( subdir, NULL );
746 ok( r == TRUE, "failed to create directory\n");
748 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
749 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
750 ok( CloseHandle(hfile), "failed toc lose file\n");
752 r = WaitForSingleObject( ov.hEvent, 1000 );
753 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
755 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
756 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
758 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
759 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
760 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
761 ok( pfni->FileNameLength == 6, "len wrong\n" );
762 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
764 r = DeleteFileW( file );
765 ok( r == TRUE, "failed to delete file\n");
767 r = RemoveDirectoryW( subdir );
768 ok( r == TRUE, "failed to remove directory\n");
770 CloseHandle(hdir);
772 r = RemoveDirectoryW( path );
773 ok( r == TRUE, "failed to remove directory\n");
776 START_TEST(change)
778 HMODULE hkernel32 = GetModuleHandle("kernel32");
779 pReadDirectoryChangesW = (fnReadDirectoryChangesW)
780 GetProcAddress(hkernel32, "ReadDirectoryChangesW");
782 test_ffcnMultipleThreads();
783 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
784 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
785 placed first. */
786 test_FindFirstChangeNotification();
787 test_ffcn();
788 test_readdirectorychanges();
789 test_readdirectorychanges_null();
790 test_readdirectorychanges_filedir();