wow64: In wow64_NtSetInformationToken forward TokenIntegrityLevel.
[wine.git] / dlls / kernel32 / tests / change.c
blobf89d3ba59436561703fe1d728a6754619e44b266
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 = arg;
42 BOOL notified = FALSE;
43 BOOL ret = FALSE;
44 DWORD status;
46 status = WaitForSingleObject(change, 100);
48 if (status == WAIT_OBJECT_0 ) {
49 notified = TRUE;
50 FindNextChangeNotification(change);
53 ret = FindCloseChangeNotification(change);
54 ok( ret, "FindCloseChangeNotification error: %ld\n",
55 GetLastError());
57 ExitThread((DWORD)notified);
60 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
62 HANDLE change, thread;
63 DWORD threadId;
65 change = FindFirstChangeNotificationA(path, subtree, flags);
66 ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %ld\n", GetLastError());
68 thread = CreateThread(NULL, 0, NotificationThread, change, 0, &threadId);
69 ok(thread != NULL, "CreateThread error: %ld\n", GetLastError());
71 return thread;
74 static DWORD FinishNotificationThread(HANDLE thread)
76 DWORD status, exitcode;
78 status = WaitForSingleObject(thread, 5000);
79 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %ld error %ld\n", status, GetLastError());
81 ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
82 CloseHandle(thread);
84 return exitcode;
87 static void test_FindFirstChangeNotification(void)
89 HANDLE change, file, thread;
90 DWORD attributes, count;
91 BOOL ret;
93 char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
94 char filename1[MAX_PATH], filename2[MAX_PATH];
95 static const char prefix[] = "FCN";
96 char buffer[2048];
98 /* pathetic checks */
100 change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
101 ok(change == INVALID_HANDLE_VALUE, "Expected INVALID_HANDLE_VALUE, got %p\n", change);
102 ok(GetLastError() == ERROR_FILE_NOT_FOUND,
103 "FindFirstChangeNotification error: %ld\n", GetLastError());
105 change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
106 ok(change == INVALID_HANDLE_VALUE || broken(change == NULL) /* < win7 */,
107 "Expected INVALID_HANDLE_VALUE, got %p\n", change);
108 ok(GetLastError() == ERROR_PATH_NOT_FOUND,
109 "FindFirstChangeNotification error: %lu\n", GetLastError());
111 ret = FindNextChangeNotification(NULL);
112 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %ld\n",
113 GetLastError());
115 ret = FindCloseChangeNotification(NULL);
116 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n",
117 GetLastError());
119 ret = GetTempPathA(MAX_PATH, dirname1);
120 ok(ret, "GetTempPathA error: %ld\n", GetLastError());
122 ret = GetTempFileNameA(dirname1, "ffc", 0, workdir);
123 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
124 DeleteFileA( workdir );
126 ret = CreateDirectoryA(workdir, NULL);
127 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
129 ret = GetTempFileNameA(workdir, prefix, 0, filename1);
130 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
132 file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
133 FILE_ATTRIBUTE_NORMAL, 0);
134 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
135 ret = CloseHandle(file);
136 ok( ret, "CloseHandle error: %ld\n", GetLastError());
138 /* Try to register notification for a file */
139 change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
140 ok(change == INVALID_HANDLE_VALUE, "Expected INVALID_HANDLE_VALUE, got %p\n", change);
141 ok(GetLastError() == ERROR_DIRECTORY,
142 "FindFirstChangeNotification error: %ld\n", GetLastError());
144 lstrcpyA(dirname1, filename1);
145 lstrcatA(dirname1, "dir");
147 lstrcpyA(dirname2, dirname1);
148 lstrcatA(dirname2, "new");
150 ret = CreateDirectoryA(dirname1, NULL);
151 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
153 /* What if we move the directory we registered notification for? */
154 thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
155 ret = MoveFileA(dirname1, dirname2);
156 ok(ret, "MoveFileA error: %ld\n", GetLastError());
157 ok(!FinishNotificationThread(thread), "Got notification\n");
159 /* What if we remove the directory we registered notification for? */
160 thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
161 ret = RemoveDirectoryA(dirname2);
162 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
163 ret = FinishNotificationThread(thread);
164 todo_wine ok(ret, "Missed notification\n");
166 /* functional checks */
168 /* Create a directory */
169 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
170 ret = CreateDirectoryA(dirname1, NULL);
171 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
172 ok(FinishNotificationThread(thread), "Missed notification\n");
174 /* Rename a directory */
175 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
176 ret = MoveFileA(dirname1, dirname2);
177 ok(ret, "MoveFileA error: %ld\n", GetLastError());
178 ok(FinishNotificationThread(thread), "Missed notification\n");
180 /* Delete a directory */
181 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
182 ret = RemoveDirectoryA(dirname2);
183 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
184 ok(FinishNotificationThread(thread), "Missed notification\n");
186 lstrcpyA(filename2, filename1);
187 lstrcatA(filename2, "new");
189 /* Rename a file */
190 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
191 ret = MoveFileA(filename1, filename2);
192 ok(ret, "MoveFileA error: %ld\n", GetLastError());
193 ok(FinishNotificationThread(thread), "Missed notification\n");
195 /* Delete a file */
196 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
197 ret = DeleteFileA(filename2);
198 ok(ret, "DeleteFileA error: %ld\n", GetLastError());
199 ok(FinishNotificationThread(thread), "Missed notification\n");
201 /* Create a file */
202 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
203 file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
204 FILE_ATTRIBUTE_NORMAL, 0);
205 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
206 ret = CloseHandle(file);
207 ok( ret, "CloseHandle error: %ld\n", GetLastError());
208 ok(FinishNotificationThread(thread), "Missed notification\n");
210 attributes = GetFileAttributesA(filename2);
211 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %ld\n", GetLastError());
212 attributes &= FILE_ATTRIBUTE_READONLY;
214 /* Change file attributes */
215 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
216 ret = SetFileAttributesA(filename2, attributes);
217 ok(ret, "SetFileAttributesA error: %ld\n", GetLastError());
218 ok(FinishNotificationThread(thread), "Missed notification\n");
220 /* Change last write time by writing to a file */
221 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
222 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
223 FILE_ATTRIBUTE_NORMAL, 0);
224 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
225 memset(buffer, 0, sizeof(buffer));
226 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
227 ok(ret && count == sizeof(buffer), "WriteFile error: %ld\n", GetLastError());
228 ret = CloseHandle(file);
229 ok( ret, "CloseHandle error: %ld\n", GetLastError());
230 ok(FinishNotificationThread(thread), "Missed notification\n");
232 /* Change file size by truncating a file */
233 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
234 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
235 FILE_ATTRIBUTE_NORMAL, 0);
236 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
237 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
238 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %ld\n", GetLastError());
239 ret = CloseHandle(file);
240 ok( ret, "CloseHandle error: %ld\n", GetLastError());
241 ok(FinishNotificationThread(thread), "Missed notification\n");
243 /* clean up */
245 ret = DeleteFileA(filename2);
246 ok(ret, "DeleteFileA error: %ld\n", GetLastError());
248 ret = RemoveDirectoryA(workdir);
249 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
252 /* this test concentrates more on the wait behaviour of the handle */
253 static void test_ffcn(void)
255 DWORD filter;
256 HANDLE handle, file;
257 LONG r;
258 WCHAR path[MAX_PATH], subdir[MAX_PATH], filename[MAX_PATH];
259 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
260 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
261 static const WCHAR szZoo[] = { '\\','z','o','o',0 };
263 r = GetTempPathW( MAX_PATH, path );
264 ok( r != 0, "temp path failed\n");
266 lstrcatW( path, szBoo );
267 lstrcpyW( subdir, path );
268 lstrcatW( subdir, szHoo );
270 lstrcpyW( filename, path );
271 lstrcatW( filename, szZoo );
273 RemoveDirectoryW( subdir );
274 RemoveDirectoryW( path );
276 r = CreateDirectoryW(path, NULL);
277 ok( r == TRUE, "failed to create directory\n");
279 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
280 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
282 handle = FindFirstChangeNotificationW( path, 1, filter);
283 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
285 r = WaitForSingleObject( handle, 0 );
286 ok( r == STATUS_TIMEOUT, "should time out\n");
288 file = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
289 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
290 ok( file != INVALID_HANDLE_VALUE, "CreateFile error %lu\n", GetLastError() );
291 CloseHandle(file);
293 r = WaitForSingleObject( handle, 1000 );
294 ok( r == WAIT_OBJECT_0, "should be ready\n");
296 r = WaitForSingleObject( handle, 0 );
297 ok( r == WAIT_OBJECT_0, "should be ready\n");
299 r = FindNextChangeNotification(handle);
300 ok( r == TRUE, "find next failed\n");
302 r = WaitForSingleObject( handle, 0 );
303 ok( r == STATUS_TIMEOUT, "should time out\n");
305 r = DeleteFileW( filename );
306 ok( r == TRUE, "failed to remove file\n");
308 r = WaitForSingleObject( handle, 1000 );
309 ok( r == WAIT_OBJECT_0, "should be ready\n");
311 r = WaitForSingleObject( handle, 0 );
312 ok( r == WAIT_OBJECT_0, "should be ready\n");
314 r = FindNextChangeNotification(handle);
315 ok( r == TRUE, "find next failed\n");
317 r = WaitForSingleObject( handle, 0 );
318 ok( r == STATUS_TIMEOUT, "should time out\n");
320 r = CreateDirectoryW( subdir, NULL );
321 ok( r == TRUE, "failed to create subdir\n");
323 r = WaitForSingleObject( handle, 1000 );
324 ok( r == WAIT_OBJECT_0, "should be ready\n");
326 r = WaitForSingleObject( handle, 0 );
327 ok( r == WAIT_OBJECT_0, "should be ready\n");
329 r = FindNextChangeNotification(handle);
330 ok( r == TRUE, "find next failed\n");
332 r = WaitForSingleObject( handle, 0 );
333 ok( r == STATUS_TIMEOUT, "should time out\n");
335 r = RemoveDirectoryW( subdir );
336 ok( r == TRUE, "failed to remove subdir\n");
338 r = WaitForSingleObject( handle, 1000 );
339 ok( r == WAIT_OBJECT_0, "should be ready\n");
341 r = WaitForSingleObject( handle, 0 );
342 ok( r == WAIT_OBJECT_0, "should be ready\n");
344 r = FindNextChangeNotification(handle);
345 ok( r == TRUE, "find next failed\n");
347 r = FindNextChangeNotification(handle);
348 ok( r == TRUE, "find next failed\n");
350 r = FindCloseChangeNotification(handle);
351 ok( r == TRUE, "should succeed\n");
353 r = RemoveDirectoryW( path );
354 ok( r == TRUE, "failed to remove dir\n");
357 /* this test concentrates on the wait behavior when multiple threads are
358 * waiting on a change notification handle. */
359 static void test_ffcnMultipleThreads(void)
361 LONG r;
362 DWORD filter, threadId, status, exitcode;
363 HANDLE handles[2];
364 char tmp[MAX_PATH], path[MAX_PATH];
366 r = GetTempPathA(MAX_PATH, tmp);
367 ok(r, "GetTempPathA error: %ld\n", GetLastError());
369 r = GetTempFileNameA(tmp, "ffc", 0, path);
370 ok(r, "GetTempFileNameA error: %ld\n", GetLastError());
371 DeleteFileA( path );
373 r = CreateDirectoryA(path, NULL);
374 ok(r, "CreateDirectoryA error: %ld\n", GetLastError());
376 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
377 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
379 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
380 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %ld\n", GetLastError());
382 /* Test behavior if a waiting thread holds the last reference to a change
383 * directory object with an empty wine user APC queue for this thread (bug #7286) */
385 /* Create our notification thread */
386 handles[1] = CreateThread(NULL, 0, NotificationThread, handles[0], 0,
387 &threadId);
388 ok(handles[1] != NULL, "CreateThread error: %ld\n", GetLastError());
390 status = WaitForMultipleObjects(2, handles, FALSE, 5000);
391 ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %ld error %ld\n", status, GetLastError());
392 ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
394 /* Clean up */
395 r = RemoveDirectoryA( path );
396 ok( r == TRUE, "failed to remove dir\n");
399 static void test_readdirectorychanges(void)
401 HANDLE hdir;
402 char buffer[0x1000];
403 DWORD fflags, filter = 0, r, dwCount;
404 OVERLAPPED ov;
405 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
406 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
407 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
408 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
409 PFILE_NOTIFY_INFORMATION pfni;
410 BOOL got_subdir_change = FALSE;
412 r = GetTempPathW( MAX_PATH, path );
413 ok( r != 0, "temp path failed\n");
415 lstrcatW( path, szBoo );
416 lstrcpyW( subdir, path );
417 lstrcatW( subdir, szHoo );
419 lstrcpyW( subsubdir, path );
420 lstrcatW( subsubdir, szGa );
422 RemoveDirectoryW( subsubdir );
423 RemoveDirectoryW( subdir );
424 RemoveDirectoryW( path );
426 r = CreateDirectoryW(path, NULL);
427 ok( r == TRUE, "failed to create directory\n");
429 SetLastError(0xd0b00b00);
430 r = ReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
431 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
432 ok(r==FALSE, "should return false\n");
434 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
435 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
436 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
437 OPEN_EXISTING, fflags, NULL);
438 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
440 ov.hEvent = CreateEventW( NULL, 1, 0, NULL );
442 SetLastError(0xd0b00b00);
443 r = ReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
444 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
445 ok(r==FALSE, "should return false\n");
447 SetLastError(0xd0b00b00);
448 r = ReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
449 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
450 ok(r==FALSE, "should return false\n");
452 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
453 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
454 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
455 filter |= FILE_NOTIFY_CHANGE_SIZE;
456 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
457 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
458 filter |= FILE_NOTIFY_CHANGE_CREATION;
459 filter |= FILE_NOTIFY_CHANGE_SECURITY;
461 SetLastError(0xd0b00b00);
462 ov.Internal = 0;
463 ov.InternalHigh = 0;
464 memset( buffer, 0, sizeof buffer );
466 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
467 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
468 ok(r==FALSE, "should return false\n");
470 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
471 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
472 ok(r==FALSE, "should return false\n");
474 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
475 ok(r==TRUE, "should return true\n");
477 r = WaitForSingleObject( ov.hEvent, 10 );
478 ok( r == STATUS_TIMEOUT, "should timeout\n" );
480 r = CreateDirectoryW( subdir, NULL );
481 ok( r == TRUE, "failed to create directory\n");
483 r = WaitForSingleObject( ov.hEvent, 1000 );
484 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
486 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
487 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
489 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
490 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
491 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
492 ok( pfni->FileNameLength == 6, "len wrong\n" );
493 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
495 ResetEvent(ov.hEvent);
496 SetLastError(0xd0b00b00);
497 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
498 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
499 ok(r==FALSE, "should return false\n");
501 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
502 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
503 ok(r==FALSE, "should return false\n");
505 filter = FILE_NOTIFY_CHANGE_SIZE;
507 SetEvent(ov.hEvent);
508 ov.Internal = 1;
509 ov.InternalHigh = 1;
510 ov.Offset = 0;
511 ov.OffsetHigh = 0;
512 memset( buffer, 0, sizeof buffer );
513 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
514 ok(r==TRUE, "should return true\n");
516 ok( (NTSTATUS)ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
517 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
519 r = WaitForSingleObject( ov.hEvent, 0 );
520 ok( r == STATUS_TIMEOUT, "should timeout\n" );
522 r = RemoveDirectoryW( subdir );
523 ok( r == TRUE, "failed to remove directory\n");
525 r = WaitForSingleObject( ov.hEvent, 1000 );
526 ok( r == WAIT_OBJECT_0, "should be ready\n" );
528 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
529 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
531 if ((NTSTATUS)ov.Internal == STATUS_SUCCESS)
533 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
534 ok( r == TRUE, "getoverlappedresult failed\n");
535 ok( dwCount == 0x12, "count wrong\n");
538 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
539 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
540 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
541 ok( pfni->FileNameLength == 6, "len wrong\n" );
542 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
544 /* what happens if the buffer is too small? */
545 r = ReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
546 ok(r==TRUE, "should return true\n");
548 r = CreateDirectoryW( subdir, NULL );
549 ok( r == TRUE, "failed to create directory\n");
551 r = WaitForSingleObject( ov.hEvent, 1000 );
552 ok( r == WAIT_OBJECT_0, "should be ready\n" );
554 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
555 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
557 /* test the recursive watch */
558 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
559 ok(r==TRUE, "should return true\n");
561 r = CreateDirectoryW( subsubdir, NULL );
562 ok( r == TRUE, "failed to create directory\n");
564 while (1)
566 r = WaitForSingleObject( ov.hEvent, 1000 );
567 ok(r == WAIT_OBJECT_0, "should be ready\n" );
568 if (r == WAIT_TIMEOUT) break;
570 ok((NTSTATUS) ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
572 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
573 while (1)
575 /* We might get one or more modified events on the parent dir */
576 if (pfni->Action == FILE_ACTION_MODIFIED)
578 ok(pfni->FileNameLength == 3 * sizeof(WCHAR), "len wrong\n" );
579 ok(!memcmp(pfni->FileName, &szGa[1], 3 * sizeof(WCHAR)), "name wrong\n");
581 else
583 ok(pfni->Action == FILE_ACTION_ADDED, "action wrong\n");
584 ok(pfni->FileNameLength == 6 * sizeof(WCHAR), "len wrong\n" );
585 ok(!memcmp(pfni->FileName, &szGa[1], 6 * sizeof(WCHAR)), "name wrong\n");
586 got_subdir_change = TRUE;
588 if (!pfni->NextEntryOffset) break;
589 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
592 if (got_subdir_change) break;
594 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
595 ok(r==TRUE, "should return true\n");
597 ok(got_subdir_change, "didn't get subdir change\n");
599 r = RemoveDirectoryW( subsubdir );
600 ok( r == TRUE, "failed to remove directory\n");
602 ov.Internal = 1;
603 ov.InternalHigh = 1;
604 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
605 ok(r==TRUE, "should return true\n");
607 r = RemoveDirectoryW( subdir );
608 ok( r == TRUE, "failed to remove directory\n");
610 r = WaitForSingleObject( ov.hEvent, 1000 );
611 ok( r == WAIT_OBJECT_0, "should be ready\n" );
613 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
614 /* we may get a notification for the parent dir too */
615 if (pfni->Action == FILE_ACTION_MODIFIED && pfni->NextEntryOffset)
617 ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong %lu\n", pfni->FileNameLength );
618 ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n" );
619 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
621 ok( pfni->NextEntryOffset == 0, "offset wrong %lu\n", pfni->NextEntryOffset );
622 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong %lu\n", pfni->Action );
623 ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %lu\n", pfni->FileNameLength );
624 ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
626 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
627 dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer;
628 ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %Iu/%lu\n",ov.InternalHigh, dwCount );
630 CloseHandle(hdir);
632 r = RemoveDirectoryW( path );
633 ok( r == TRUE, "failed to remove directory\n");
636 /* show the behaviour when a null buffer is passed */
637 static void test_readdirectorychanges_null(void)
639 NTSTATUS r;
640 HANDLE hdir;
641 char buffer[0x1000];
642 DWORD fflags, filter = 0;
643 OVERLAPPED ov;
644 WCHAR path[MAX_PATH], subdir[MAX_PATH];
645 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
646 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
647 PFILE_NOTIFY_INFORMATION pfni;
649 r = GetTempPathW( MAX_PATH, path );
650 ok( r != 0, "temp path failed\n");
652 lstrcatW( path, szBoo );
653 lstrcpyW( subdir, path );
654 lstrcatW( subdir, szHoo );
656 RemoveDirectoryW( subdir );
657 RemoveDirectoryW( path );
659 r = CreateDirectoryW(path, NULL);
660 ok( r == TRUE, "failed to create directory\n");
662 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
663 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
664 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
665 OPEN_EXISTING, fflags, NULL);
666 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
668 ov.hEvent = CreateEventW( NULL, 1, 0, NULL );
670 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
671 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
673 SetLastError(0xd0b00b00);
674 ov.Internal = 0;
675 ov.InternalHigh = 0;
676 memset( buffer, 0, sizeof buffer );
678 r = ReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
679 ok(r==TRUE, "should return true\n");
681 r = WaitForSingleObject( ov.hEvent, 0 );
682 ok( r == STATUS_TIMEOUT, "should timeout\n" );
684 r = CreateDirectoryW( subdir, NULL );
685 ok( r == TRUE, "failed to create directory\n");
687 r = WaitForSingleObject( ov.hEvent, 0 );
688 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
690 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
691 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
693 ov.Internal = 0;
694 ov.InternalHigh = 0;
695 ov.Offset = 0;
696 ov.OffsetHigh = 0;
697 memset( buffer, 0, sizeof buffer );
699 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
700 ok(r==TRUE, "should return true\n");
702 r = WaitForSingleObject( ov.hEvent, 0 );
703 ok( r == STATUS_TIMEOUT, "should timeout\n" );
705 r = RemoveDirectoryW( subdir );
706 ok( r == TRUE, "failed to remove directory\n");
708 r = WaitForSingleObject( ov.hEvent, 1000 );
709 ok( r == WAIT_OBJECT_0, "should be ready\n" );
711 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
712 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
714 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
715 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
717 CloseHandle(hdir);
719 r = RemoveDirectoryW( path );
720 ok( r == TRUE, "failed to remove directory\n");
723 static void test_readdirectorychanges_filedir(void)
725 NTSTATUS r;
726 HANDLE hdir, hfile;
727 char buffer[0x1000];
728 DWORD fflags, filter = 0;
729 OVERLAPPED ov;
730 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
731 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
732 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
733 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
734 PFILE_NOTIFY_INFORMATION pfni;
736 r = GetTempPathW( MAX_PATH, path );
737 ok( r != 0, "temp path failed\n");
739 lstrcatW( path, szBoo );
740 lstrcpyW( subdir, path );
741 lstrcatW( subdir, szHoo );
743 lstrcpyW( file, path );
744 lstrcatW( file, szFoo );
746 DeleteFileW( file );
747 RemoveDirectoryW( subdir );
748 RemoveDirectoryW( path );
750 r = CreateDirectoryW(path, NULL);
751 ok( r == TRUE, "failed to create directory\n");
753 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
754 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
755 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
756 OPEN_EXISTING, fflags, NULL);
757 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
759 ov.hEvent = CreateEventW( NULL, 0, 0, NULL );
761 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
763 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
764 ok(r==TRUE, "should return true\n");
766 r = WaitForSingleObject( ov.hEvent, 10 );
767 ok( r == WAIT_TIMEOUT, "should timeout\n" );
769 r = CreateDirectoryW( subdir, NULL );
770 ok( r == TRUE, "failed to create directory\n");
772 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
773 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
774 ok( CloseHandle(hfile), "failed to close file\n");
776 r = WaitForSingleObject( ov.hEvent, 1000 );
777 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
779 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
780 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
782 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
783 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
784 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
785 ok( pfni->FileNameLength == 6, "len wrong\n" );
786 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
788 r = DeleteFileW( file );
789 ok( r == TRUE, "failed to delete file\n");
791 r = RemoveDirectoryW( subdir );
792 ok( r == TRUE, "failed to remove directory\n");
794 CloseHandle(hdir);
796 r = RemoveDirectoryW( path );
797 ok( r == TRUE, "failed to remove directory\n");
800 static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov)
802 ok(error == 0, "ReadDirectoryChangesW error %ld\n", error);
803 ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n");
806 static void test_readdirectorychanges_cr(void)
808 static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 };
809 static const WCHAR szDir[] = { 'd','i','r',0 };
810 static const WCHAR szFile[] = { 'f','i','l','e',0 };
811 static const WCHAR szBackslash[] = { '\\',0 };
813 WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH];
814 FILE_NOTIFY_INFORMATION fni[1024], *fni_next;
815 OVERLAPPED ov;
816 HANDLE hdir, hfile;
817 NTSTATUS r;
819 r = GetTempPathW(MAX_PATH, path);
820 ok(r != 0, "temp path failed\n");
822 lstrcatW(path, szBoo);
823 lstrcpyW(dir, path);
824 lstrcatW(dir, szDir);
825 lstrcpyW(file, path);
826 lstrcatW(file, szFile);
827 lstrcpyW(sub_file, dir);
828 lstrcatW(sub_file, szBackslash);
829 lstrcatW(sub_file, szFile);
831 DeleteFileW(file);
832 RemoveDirectoryW(dir);
833 RemoveDirectoryW(path);
835 r = CreateDirectoryW(path, NULL);
836 ok(r == TRUE, "failed to create directory\n");
838 hdir = CreateFileW(path, GENERIC_READ,
839 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
840 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
841 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
843 memset(&ov, 0, sizeof(ov));
844 ov.hEvent = (void*)0xdeadbeef;
845 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
846 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
847 ok(r == TRUE, "ReadDirectoryChangesW failed\n");
849 hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
850 ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
851 CloseHandle(hfile);
853 r = SleepEx(1000, TRUE);
854 ok(r != 0, "failed to receive file creation event\n");
855 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
856 ok(fni->Action == FILE_ACTION_ADDED, "Action = %ld\n", fni->Action);
857 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
858 "FileNameLength = %ld\n", fni->FileNameLength);
859 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
860 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
862 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
863 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
864 ok(r == TRUE, "ReadDirectoryChangesW failed\n");
866 /* This event will not be reported */
867 r = CreateDirectoryW(dir, NULL);
868 ok(r == TRUE, "failed to create directory\n");
870 r = MoveFileW(file, sub_file);
871 ok(r == TRUE, "failed to move file\n");
873 r = SleepEx(1000, TRUE);
874 ok(r != 0, "failed to receive file move event\n");
875 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
876 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %ld\n", fni->Action);
877 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
878 "FileNameLength = %ld\n", fni->FileNameLength);
879 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
880 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
882 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
883 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
884 ok(r == TRUE, "ReadDirectoryChangesW failed\n");
886 r = MoveFileW(sub_file, file);
887 ok(r == TRUE, "failed to move file\n");
889 r = SleepEx(1000, TRUE);
890 ok(r != 0, "failed to receive file move event\n");
891 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
892 ok(fni->Action == FILE_ACTION_ADDED, "Action = %ld\n", fni->Action);
893 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
894 "FileNameLength = %ld\n", fni->FileNameLength);
895 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
896 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
898 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
899 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
900 ok(r == TRUE, "ReadDirectoryChangesW failed\n");
902 r = DeleteFileW(file);
903 ok(r == TRUE, "failed to delete file\n");
905 r = SleepEx(1000, TRUE);
906 ok(r != 0, "failed to receive file removal event\n");
907 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
908 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %ld\n", fni->Action);
909 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
910 "FileNameLength = %ld\n", fni->FileNameLength);
911 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
912 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
914 CloseHandle(hdir);
916 hdir = CreateFileW(path, GENERIC_READ,
917 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
918 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
919 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
921 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
922 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
923 ok(r == TRUE, "ReadDirectoryChangesW failed\n");
925 r = MoveFileW(dir, file);
926 ok(r == TRUE, "failed to move directory\n");
928 r = SleepEx(1000, TRUE);
929 ok(r != 0, "failed to receive directory move event\n");
930 if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME)
932 ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %ld\n", fni->Action);
933 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
934 "FileNameLength = %ld\n", fni->FileNameLength);
935 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
936 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
937 ok(fni->NextEntryOffset != 0, "no next entry in movement event\n");
938 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
939 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
940 ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %ld\n", fni_next->Action);
941 ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
942 "FileNameLength = %ld\n", fni_next->FileNameLength);
943 ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
944 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR)));
946 else
948 todo_wine ok(0, "Expected rename event\n");
950 if (fni->NextEntryOffset == 0)
952 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
953 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
954 ok(r == TRUE, "ReadDirectoryChangesW failed\n");
956 r = SleepEx(1000, TRUE);
957 ok(r != 0, "failed to receive directory move event\n");
961 r = CreateDirectoryW(dir, NULL);
962 ok(r == TRUE, "failed to create directory\n");
964 r = RemoveDirectoryW(dir);
965 ok(r == TRUE, "failed to remove directory\n");
967 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
968 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
969 ok(r == TRUE, "ReadDirectoryChangesW failed\n");
971 r = SleepEx(1000, TRUE);
972 ok(r != 0, "failed to receive directory creation event\n");
973 ok(fni->Action == FILE_ACTION_ADDED, "Action = %ld\n", fni->Action);
974 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
975 "FileNameLength = %ld\n", fni->FileNameLength);
976 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
977 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
978 if (fni->NextEntryOffset)
979 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
980 else
982 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
983 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
984 ok(r == TRUE, "ReadDirectoryChangesW failed\n");
986 r = SleepEx(1000, TRUE);
987 ok(r != 0, "failed to receive directory removal event\n");
988 fni_next = fni;
990 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
991 ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %ld\n", fni_next->Action);
992 ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
993 "FileNameLength = %ld\n", fni_next->FileNameLength);
994 ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
995 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR)));
997 CloseHandle(hdir);
998 RemoveDirectoryW(file);
999 RemoveDirectoryW(path);
1002 static void test_ffcn_directory_overlap(void)
1004 HANDLE parent_watch, child_watch, parent_thread, child_thread;
1005 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH];
1006 char tempfile[MAX_PATH];
1007 DWORD threadId;
1008 BOOL ret;
1010 /* Setup directory hierarchy */
1011 ret = GetTempPathA(MAX_PATH, workdir);
1012 ok((ret > 0) && (ret <= MAX_PATH),
1013 "GetTempPathA error: %ld\n", GetLastError());
1015 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile);
1016 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
1017 ret = DeleteFileA(tempfile);
1018 ok(ret, "DeleteFileA error: %ld\n", GetLastError());
1020 lstrcpyA(parentdir, tempfile);
1021 ret = CreateDirectoryA(parentdir, NULL);
1022 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
1024 lstrcpyA(childdir, parentdir);
1025 lstrcatA(childdir, "\\c");
1026 ret = CreateDirectoryA(childdir, NULL);
1027 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
1030 /* When recursively watching overlapping directories, changes in child
1031 * should trigger notifications for both child and parent */
1032 parent_thread = StartNotificationThread(parentdir, TRUE,
1033 FILE_NOTIFY_CHANGE_FILE_NAME);
1034 child_thread = StartNotificationThread(childdir, TRUE,
1035 FILE_NOTIFY_CHANGE_FILE_NAME);
1037 /* Create a file in child */
1038 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1039 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
1041 /* Both watches should trigger */
1042 ret = FinishNotificationThread(parent_thread);
1043 ok(ret, "Missed parent notification\n");
1044 ret = FinishNotificationThread(child_thread);
1045 ok(ret, "Missed child notification\n");
1047 ret = DeleteFileA(tempfile);
1048 ok(ret, "DeleteFileA error: %ld\n", GetLastError());
1051 /* Removing a recursive parent watch should not affect child watches. Doing
1052 * so used to crash wineserver. */
1053 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE,
1054 FILE_NOTIFY_CHANGE_FILE_NAME);
1055 ok(parent_watch != INVALID_HANDLE_VALUE,
1056 "FindFirstChangeNotification error: %ld\n", GetLastError());
1057 child_watch = FindFirstChangeNotificationA(childdir, TRUE,
1058 FILE_NOTIFY_CHANGE_FILE_NAME);
1059 ok(child_watch != INVALID_HANDLE_VALUE,
1060 "FindFirstChangeNotification error: %ld\n", GetLastError());
1062 ret = FindCloseChangeNotification(parent_watch);
1063 ok(ret, "FindCloseChangeNotification error: %ld\n", GetLastError());
1065 child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0,
1066 &threadId);
1067 ok(child_thread != NULL, "CreateThread error: %ld\n", GetLastError());
1069 /* Create a file in child */
1070 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1071 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
1073 /* Child watch should trigger */
1074 ret = FinishNotificationThread(child_thread);
1075 ok(ret, "Missed child notification\n");
1077 /* clean up */
1078 ret = DeleteFileA(tempfile);
1079 ok(ret, "DeleteFileA error: %ld\n", GetLastError());
1081 ret = RemoveDirectoryA(childdir);
1082 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
1084 ret = RemoveDirectoryA(parentdir);
1085 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
1088 START_TEST(change)
1090 test_ffcnMultipleThreads();
1091 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
1092 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
1093 placed first. */
1094 test_FindFirstChangeNotification();
1095 test_ffcn();
1096 test_readdirectorychanges();
1097 test_readdirectorychanges_null();
1098 test_readdirectorychanges_filedir();
1099 test_readdirectorychanges_cr();
1100 test_ffcn_directory_overlap();