winhttp: Get rid of send_request_t.
[wine.git] / dlls / kernel32 / tests / change.c
blob3c8084de646e4cbd2ddc877473c610e9a0296408
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: %d\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: %d\n", GetLastError());
68 thread = CreateThread(NULL, 0, NotificationThread, change, 0, &threadId);
69 ok(thread != NULL, "CreateThread error: %d\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 %d error %d\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 GetLastError() == ERROR_NO_MORE_FILES, /* win95 */
104 "FindFirstChangeNotification error: %d\n", GetLastError());
106 if (0) /* This documents win2k behavior. It crashes on win98. */
108 change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
109 ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
110 "FindFirstChangeNotification error: %d\n", GetLastError());
113 ret = FindNextChangeNotification(NULL);
114 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n",
115 GetLastError());
117 ret = FindCloseChangeNotification(NULL);
118 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
119 GetLastError());
121 ret = GetTempPathA(MAX_PATH, workdir);
122 ok(ret, "GetTempPathA error: %d\n", GetLastError());
124 lstrcatA(workdir, "testFileChangeNotification");
126 ret = CreateDirectoryA(workdir, NULL);
127 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
129 ret = GetTempFileNameA(workdir, prefix, 0, filename1);
130 ok(ret, "GetTempFileNameA error: %d\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: %d\n", GetLastError());
135 ret = CloseHandle(file);
136 ok( ret, "CloseHandle error: %d\n", GetLastError());
138 /* Try to register notification for a file. win98 and win2k behave differently here */
139 change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
140 ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
141 GetLastError() == ERROR_FILE_NOT_FOUND),
142 "FindFirstChangeNotification error: %d\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: %d\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: %d\n", GetLastError());
157 /* win9x and win2k behave differently here, don't check result */
158 FinishNotificationThread(thread);
160 /* What if we remove the directory we registered notification for? */
161 thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
162 ret = RemoveDirectoryA(dirname2);
163 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
164 /* win9x and win2k behave differently here, don't check result */
165 FinishNotificationThread(thread);
167 /* functional checks */
169 /* Create a directory */
170 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
171 ret = CreateDirectoryA(dirname1, NULL);
172 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
173 ok(FinishNotificationThread(thread), "Missed notification\n");
175 /* Rename a directory */
176 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
177 ret = MoveFileA(dirname1, dirname2);
178 ok(ret, "MoveFileA error: %d\n", GetLastError());
179 ok(FinishNotificationThread(thread), "Missed notification\n");
181 /* Delete a directory */
182 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
183 ret = RemoveDirectoryA(dirname2);
184 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
185 ok(FinishNotificationThread(thread), "Missed notification\n");
187 lstrcpyA(filename2, filename1);
188 lstrcatA(filename2, "new");
190 /* Rename a file */
191 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
192 ret = MoveFileA(filename1, filename2);
193 ok(ret, "MoveFileA error: %d\n", GetLastError());
194 ok(FinishNotificationThread(thread), "Missed notification\n");
196 /* Delete a file */
197 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
198 ret = DeleteFileA(filename2);
199 ok(ret, "DeleteFileA error: %d\n", GetLastError());
200 ok(FinishNotificationThread(thread), "Missed notification\n");
202 /* Create a file */
203 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
204 file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
205 FILE_ATTRIBUTE_NORMAL, 0);
206 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
207 ret = CloseHandle(file);
208 ok( ret, "CloseHandle error: %d\n", GetLastError());
209 ok(FinishNotificationThread(thread), "Missed notification\n");
211 attributes = GetFileAttributesA(filename2);
212 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError());
213 attributes &= FILE_ATTRIBUTE_READONLY;
215 /* Change file attributes */
216 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
217 ret = SetFileAttributesA(filename2, attributes);
218 ok(ret, "SetFileAttributesA error: %d\n", GetLastError());
219 ok(FinishNotificationThread(thread), "Missed notification\n");
221 /* Change last write time by writing to a file */
222 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
223 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
224 FILE_ATTRIBUTE_NORMAL, 0);
225 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
226 memset(buffer, 0, sizeof(buffer));
227 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
228 ok(ret && count == sizeof(buffer), "WriteFile error: %d\n", GetLastError());
229 ret = CloseHandle(file);
230 ok( ret, "CloseHandle error: %d\n", GetLastError());
231 ok(FinishNotificationThread(thread), "Missed notification\n");
233 /* Change file size by truncating a file */
234 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
235 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
236 FILE_ATTRIBUTE_NORMAL, 0);
237 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
238 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
239 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %d\n", GetLastError());
240 ret = CloseHandle(file);
241 ok( ret, "CloseHandle error: %d\n", GetLastError());
242 ok(FinishNotificationThread(thread), "Missed notification\n");
244 /* clean up */
246 ret = DeleteFileA(filename2);
247 ok(ret, "DeleteFileA error: %d\n", GetLastError());
249 ret = RemoveDirectoryA(workdir);
250 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
253 /* this test concentrates more on the wait behaviour of the handle */
254 static void test_ffcn(void)
256 DWORD filter;
257 HANDLE handle, file;
258 LONG r;
259 WCHAR path[MAX_PATH], subdir[MAX_PATH], filename[MAX_PATH];
260 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
261 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
262 static const WCHAR szZoo[] = { '\\','z','o','o',0 };
264 SetLastError(0xdeadbeef);
265 r = GetTempPathW( MAX_PATH, path );
266 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
268 win_skip("GetTempPathW is not implemented\n");
269 return;
271 ok( r != 0, "temp path failed\n");
272 if (!r)
273 return;
275 lstrcatW( path, szBoo );
276 lstrcpyW( subdir, path );
277 lstrcatW( subdir, szHoo );
279 lstrcpyW( filename, path );
280 lstrcatW( filename, szZoo );
282 RemoveDirectoryW( subdir );
283 RemoveDirectoryW( path );
285 r = CreateDirectoryW(path, NULL);
286 ok( r == TRUE, "failed to create directory\n");
288 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
289 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
291 handle = FindFirstChangeNotificationW( path, 1, filter);
292 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
294 r = WaitForSingleObject( handle, 0 );
295 ok( r == STATUS_TIMEOUT, "should time out\n");
297 file = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
298 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
299 ok( file != INVALID_HANDLE_VALUE, "CreateFile error %u\n", GetLastError() );
300 CloseHandle(file);
302 r = WaitForSingleObject( handle, 0 );
303 ok( r == WAIT_OBJECT_0, "should be ready\n");
305 r = WaitForSingleObject( handle, 0 );
306 ok( r == WAIT_OBJECT_0, "should be ready\n");
308 r = FindNextChangeNotification(handle);
309 ok( r == TRUE, "find next failed\n");
311 r = WaitForSingleObject( handle, 0 );
312 ok( r == STATUS_TIMEOUT, "should time out\n");
314 r = DeleteFileW( filename );
315 ok( r == TRUE, "failed to remove file\n");
317 r = WaitForSingleObject( handle, 0 );
318 ok( r == WAIT_OBJECT_0, "should be ready\n");
320 r = WaitForSingleObject( handle, 0 );
321 ok( r == WAIT_OBJECT_0, "should be ready\n");
323 r = FindNextChangeNotification(handle);
324 ok( r == TRUE, "find next failed\n");
326 r = WaitForSingleObject( handle, 0 );
327 ok( r == STATUS_TIMEOUT, "should time out\n");
329 r = CreateDirectoryW( subdir, NULL );
330 ok( r == TRUE, "failed to create subdir\n");
332 r = WaitForSingleObject( handle, 0 );
333 ok( r == WAIT_OBJECT_0, "should be ready\n");
335 r = WaitForSingleObject( handle, 0 );
336 ok( r == WAIT_OBJECT_0, "should be ready\n");
338 r = FindNextChangeNotification(handle);
339 ok( r == TRUE, "find next failed\n");
341 r = WaitForSingleObject( handle, 0 );
342 ok( r == STATUS_TIMEOUT, "should time out\n");
344 r = RemoveDirectoryW( subdir );
345 ok( r == TRUE, "failed to remove subdir\n");
347 r = WaitForSingleObject( handle, 0 );
348 ok( r == WAIT_OBJECT_0, "should be ready\n");
350 r = WaitForSingleObject( handle, 0 );
351 ok( r == WAIT_OBJECT_0, "should be ready\n");
353 r = FindNextChangeNotification(handle);
354 ok( r == TRUE, "find next failed\n");
356 r = FindNextChangeNotification(handle);
357 ok( r == TRUE, "find next failed\n");
359 r = FindCloseChangeNotification(handle);
360 ok( r == TRUE, "should succeed\n");
362 r = RemoveDirectoryW( path );
363 ok( r == TRUE, "failed to remove dir\n");
366 /* this test concentrates on the wait behavior when multiple threads are
367 * waiting on a change notification handle. */
368 static void test_ffcnMultipleThreads(void)
370 LONG r;
371 DWORD filter, threadId, status, exitcode;
372 HANDLE handles[2];
373 char path[MAX_PATH];
375 r = GetTempPathA(MAX_PATH, path);
376 ok(r, "GetTempPathA error: %d\n", GetLastError());
378 lstrcatA(path, "ffcnTestMultipleThreads");
380 RemoveDirectoryA(path);
382 r = CreateDirectoryA(path, NULL);
383 ok(r, "CreateDirectoryA error: %d\n", GetLastError());
385 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
386 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
388 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
389 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
391 /* Test behavior if a waiting thread holds the last reference to a change
392 * directory object with an empty wine user APC queue for this thread (bug #7286) */
394 /* Create our notification thread */
395 handles[1] = CreateThread(NULL, 0, NotificationThread, handles[0], 0,
396 &threadId);
397 ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
399 status = WaitForMultipleObjects(2, handles, FALSE, 5000);
400 ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError());
401 ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
403 /* Clean up */
404 r = RemoveDirectoryA( path );
405 ok( r == TRUE, "failed to remove dir\n");
408 static BOOL (WINAPI *pReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
409 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
411 static void test_readdirectorychanges(void)
413 HANDLE hdir;
414 char buffer[0x1000];
415 DWORD fflags, filter = 0, r, dwCount;
416 OVERLAPPED ov;
417 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
418 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
419 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
420 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
421 PFILE_NOTIFY_INFORMATION pfni;
422 BOOL got_subdir_change = FALSE;
424 if (!pReadDirectoryChangesW)
426 win_skip("ReadDirectoryChangesW is not available\n");
427 return;
430 SetLastError(0xdeadbeef);
431 r = GetTempPathW( MAX_PATH, path );
432 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
434 win_skip("GetTempPathW is not implemented\n");
435 return;
437 ok( r != 0, "temp path failed\n");
438 if (!r)
439 return;
441 lstrcatW( path, szBoo );
442 lstrcpyW( subdir, path );
443 lstrcatW( subdir, szHoo );
445 lstrcpyW( subsubdir, path );
446 lstrcatW( subsubdir, szGa );
448 RemoveDirectoryW( subsubdir );
449 RemoveDirectoryW( subdir );
450 RemoveDirectoryW( path );
452 r = CreateDirectoryW(path, NULL);
453 ok( r == TRUE, "failed to create directory\n");
455 SetLastError(0xd0b00b00);
456 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
457 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
458 ok(r==FALSE, "should return false\n");
460 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
461 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
462 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
463 OPEN_EXISTING, fflags, NULL);
464 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
466 ov.hEvent = CreateEventW( NULL, 1, 0, NULL );
468 SetLastError(0xd0b00b00);
469 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
470 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
471 ok(r==FALSE, "should return false\n");
473 SetLastError(0xd0b00b00);
474 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
475 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
476 ok(r==FALSE, "should return false\n");
478 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
479 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
480 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
481 filter |= FILE_NOTIFY_CHANGE_SIZE;
482 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
483 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
484 filter |= FILE_NOTIFY_CHANGE_CREATION;
485 filter |= FILE_NOTIFY_CHANGE_SECURITY;
487 SetLastError(0xd0b00b00);
488 ov.Internal = 0;
489 ov.InternalHigh = 0;
490 memset( buffer, 0, sizeof buffer );
492 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
493 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
494 ok(r==FALSE, "should return false\n");
496 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
497 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
498 ok(r==FALSE, "should return false\n");
500 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
501 ok(r==TRUE, "should return true\n");
503 r = WaitForSingleObject( ov.hEvent, 10 );
504 ok( r == STATUS_TIMEOUT, "should timeout\n" );
506 r = CreateDirectoryW( subdir, NULL );
507 ok( r == TRUE, "failed to create directory\n");
509 r = WaitForSingleObject( ov.hEvent, 1000 );
510 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
512 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
513 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
515 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
516 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
517 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
518 ok( pfni->FileNameLength == 6, "len wrong\n" );
519 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
521 ResetEvent(ov.hEvent);
522 SetLastError(0xd0b00b00);
523 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
524 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
525 ok(r==FALSE, "should return false\n");
527 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
528 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
529 ok(r==FALSE, "should return false\n");
531 filter = FILE_NOTIFY_CHANGE_SIZE;
533 SetEvent(ov.hEvent);
534 ov.Internal = 1;
535 ov.InternalHigh = 1;
536 S(U(ov)).Offset = 0;
537 S(U(ov)).OffsetHigh = 0;
538 memset( buffer, 0, sizeof buffer );
539 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
540 ok(r==TRUE, "should return true\n");
542 ok( (NTSTATUS)ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
543 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
545 r = WaitForSingleObject( ov.hEvent, 0 );
546 ok( r == STATUS_TIMEOUT, "should timeout\n" );
548 r = RemoveDirectoryW( subdir );
549 ok( r == TRUE, "failed to remove directory\n");
551 r = WaitForSingleObject( ov.hEvent, 1000 );
552 ok( r == WAIT_OBJECT_0, "should be ready\n" );
554 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
555 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
557 if ((NTSTATUS)ov.Internal == STATUS_SUCCESS)
559 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
560 ok( r == TRUE, "getoverlappedresult failed\n");
561 ok( dwCount == 0x12, "count wrong\n");
564 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
565 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
566 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
567 ok( pfni->FileNameLength == 6, "len wrong\n" );
568 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
570 /* what happens if the buffer is too small? */
571 r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
572 ok(r==TRUE, "should return true\n");
574 r = CreateDirectoryW( subdir, NULL );
575 ok( r == TRUE, "failed to create directory\n");
577 r = WaitForSingleObject( ov.hEvent, 1000 );
578 ok( r == WAIT_OBJECT_0, "should be ready\n" );
580 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
581 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
583 /* test the recursive watch */
584 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
585 ok(r==TRUE, "should return true\n");
587 r = CreateDirectoryW( subsubdir, NULL );
588 ok( r == TRUE, "failed to create directory\n");
590 while (1)
592 r = WaitForSingleObject( ov.hEvent, 1000 );
593 ok(r == WAIT_OBJECT_0, "should be ready\n" );
594 if (r == WAIT_TIMEOUT) break;
596 ok((NTSTATUS) ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
598 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
599 while (1)
601 /* We might get one or more modified events on the parent dir */
602 if (pfni->Action == FILE_ACTION_MODIFIED)
604 ok(pfni->FileNameLength == 3 * sizeof(WCHAR), "len wrong\n" );
605 ok(!memcmp(pfni->FileName, &szGa[1], 3 * sizeof(WCHAR)), "name wrong\n");
607 else
609 ok(pfni->Action == FILE_ACTION_ADDED, "action wrong\n");
610 ok(pfni->FileNameLength == 6 * sizeof(WCHAR), "len wrong\n" );
611 ok(!memcmp(pfni->FileName, &szGa[1], 6 * sizeof(WCHAR)), "name wrong\n");
612 got_subdir_change = TRUE;
614 if (!pfni->NextEntryOffset) break;
615 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
618 if (got_subdir_change) break;
620 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
621 ok(r==TRUE, "should return true\n");
623 ok(got_subdir_change, "didn't get subdir change\n");
625 r = RemoveDirectoryW( subsubdir );
626 ok( r == TRUE, "failed to remove directory\n");
628 ov.Internal = 1;
629 ov.InternalHigh = 1;
630 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
631 ok(r==TRUE, "should return true\n");
633 r = RemoveDirectoryW( subdir );
634 ok( r == TRUE, "failed to remove directory\n");
636 r = WaitForSingleObject( ov.hEvent, 1000 );
637 ok( r == WAIT_OBJECT_0, "should be ready\n" );
639 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
640 /* we may get a notification for the parent dir too */
641 if (pfni->Action == FILE_ACTION_MODIFIED && pfni->NextEntryOffset)
643 ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
644 ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n" );
645 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
647 ok( pfni->NextEntryOffset == 0, "offset wrong %u\n", pfni->NextEntryOffset );
648 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong %u\n", pfni->Action );
649 ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
650 ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
652 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
653 dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer;
654 ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %lu/%u\n",ov.InternalHigh, dwCount );
656 CloseHandle(hdir);
658 r = RemoveDirectoryW( path );
659 ok( r == TRUE, "failed to remove directory\n");
662 /* show the behaviour when a null buffer is passed */
663 static void test_readdirectorychanges_null(void)
665 NTSTATUS r;
666 HANDLE hdir;
667 char buffer[0x1000];
668 DWORD fflags, filter = 0;
669 OVERLAPPED ov;
670 WCHAR path[MAX_PATH], subdir[MAX_PATH];
671 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
672 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
673 PFILE_NOTIFY_INFORMATION pfni;
675 if (!pReadDirectoryChangesW)
677 win_skip("ReadDirectoryChangesW is not available\n");
678 return;
680 SetLastError(0xdeadbeef);
681 r = GetTempPathW( MAX_PATH, path );
682 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
684 win_skip("GetTempPathW is not implemented\n");
685 return;
687 ok( r != 0, "temp path failed\n");
688 if (!r)
689 return;
691 lstrcatW( path, szBoo );
692 lstrcpyW( subdir, path );
693 lstrcatW( subdir, szHoo );
695 RemoveDirectoryW( subdir );
696 RemoveDirectoryW( path );
698 r = CreateDirectoryW(path, NULL);
699 ok( r == TRUE, "failed to create directory\n");
701 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
702 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
703 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
704 OPEN_EXISTING, fflags, NULL);
705 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
707 ov.hEvent = CreateEventW( NULL, 1, 0, NULL );
709 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
710 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
712 SetLastError(0xd0b00b00);
713 ov.Internal = 0;
714 ov.InternalHigh = 0;
715 memset( buffer, 0, sizeof buffer );
717 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
718 ok(r==TRUE, "should return true\n");
720 r = WaitForSingleObject( ov.hEvent, 0 );
721 ok( r == STATUS_TIMEOUT, "should timeout\n" );
723 r = CreateDirectoryW( subdir, NULL );
724 ok( r == TRUE, "failed to create directory\n");
726 r = WaitForSingleObject( ov.hEvent, 0 );
727 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
729 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
730 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
732 ov.Internal = 0;
733 ov.InternalHigh = 0;
734 S(U(ov)).Offset = 0;
735 S(U(ov)).OffsetHigh = 0;
736 memset( buffer, 0, sizeof buffer );
738 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
739 ok(r==TRUE, "should return true\n");
741 r = WaitForSingleObject( ov.hEvent, 0 );
742 ok( r == STATUS_TIMEOUT, "should timeout\n" );
744 r = RemoveDirectoryW( subdir );
745 ok( r == TRUE, "failed to remove directory\n");
747 r = WaitForSingleObject( ov.hEvent, 1000 );
748 ok( r == WAIT_OBJECT_0, "should be ready\n" );
750 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
751 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
753 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
754 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
756 CloseHandle(hdir);
758 r = RemoveDirectoryW( path );
759 ok( r == TRUE, "failed to remove directory\n");
762 static void test_readdirectorychanges_filedir(void)
764 NTSTATUS r;
765 HANDLE hdir, hfile;
766 char buffer[0x1000];
767 DWORD fflags, filter = 0;
768 OVERLAPPED ov;
769 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
770 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
771 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
772 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
773 PFILE_NOTIFY_INFORMATION pfni;
775 SetLastError(0xdeadbeef);
776 r = GetTempPathW( MAX_PATH, path );
777 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
779 win_skip("GetTempPathW is not implemented\n");
780 return;
782 ok( r != 0, "temp path failed\n");
783 if (!r)
784 return;
786 lstrcatW( path, szBoo );
787 lstrcpyW( subdir, path );
788 lstrcatW( subdir, szHoo );
790 lstrcpyW( file, path );
791 lstrcatW( file, szFoo );
793 DeleteFileW( file );
794 RemoveDirectoryW( subdir );
795 RemoveDirectoryW( path );
797 r = CreateDirectoryW(path, NULL);
798 ok( r == TRUE, "failed to create directory\n");
800 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
801 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
802 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
803 OPEN_EXISTING, fflags, NULL);
804 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
806 ov.hEvent = CreateEventW( NULL, 0, 0, NULL );
808 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
810 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
811 ok(r==TRUE, "should return true\n");
813 r = WaitForSingleObject( ov.hEvent, 10 );
814 ok( r == WAIT_TIMEOUT, "should timeout\n" );
816 r = CreateDirectoryW( subdir, NULL );
817 ok( r == TRUE, "failed to create directory\n");
819 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
820 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
821 ok( CloseHandle(hfile), "failed to close file\n");
823 r = WaitForSingleObject( ov.hEvent, 1000 );
824 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
826 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
827 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
829 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
830 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
831 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
832 ok( pfni->FileNameLength == 6, "len wrong\n" );
833 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
835 r = DeleteFileW( file );
836 ok( r == TRUE, "failed to delete file\n");
838 r = RemoveDirectoryW( subdir );
839 ok( r == TRUE, "failed to remove directory\n");
841 CloseHandle(hdir);
843 r = RemoveDirectoryW( path );
844 ok( r == TRUE, "failed to remove directory\n");
847 static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov)
849 ok(error == 0, "ReadDirectoryChangesW error %d\n", error);
850 ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n");
853 static void test_readdirectorychanges_cr(void)
855 static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 };
856 static const WCHAR szDir[] = { 'd','i','r',0 };
857 static const WCHAR szFile[] = { 'f','i','l','e',0 };
858 static const WCHAR szBackslash[] = { '\\',0 };
860 WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH];
861 FILE_NOTIFY_INFORMATION fni[1024], *fni_next;
862 OVERLAPPED ov;
863 HANDLE hdir, hfile;
864 NTSTATUS r;
866 if (!pReadDirectoryChangesW)
868 win_skip("ReadDirectoryChangesW is not available\n");
869 return;
872 SetLastError(0xdeadbeef);
873 r = GetTempPathW(MAX_PATH, path);
874 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
876 win_skip("GetTempPathW is not implemented\n");
877 return;
879 ok(r != 0, "temp path failed\n");
880 if (!r)
881 return;
883 lstrcatW(path, szBoo);
884 lstrcpyW(dir, path);
885 lstrcatW(dir, szDir);
886 lstrcpyW(file, path);
887 lstrcatW(file, szFile);
888 lstrcpyW(sub_file, dir);
889 lstrcatW(sub_file, szBackslash);
890 lstrcatW(sub_file, szFile);
892 DeleteFileW(file);
893 RemoveDirectoryW(dir);
894 RemoveDirectoryW(path);
896 r = CreateDirectoryW(path, NULL);
897 ok(r == TRUE, "failed to create directory\n");
899 hdir = CreateFileW(path, GENERIC_READ,
900 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
901 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
902 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
904 memset(&ov, 0, sizeof(ov));
905 ov.hEvent = (void*)0xdeadbeef;
906 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
907 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
908 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
910 hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
911 ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
912 CloseHandle(hfile);
914 r = SleepEx(1000, TRUE);
915 ok(r != 0, "failed to receive file creation event\n");
916 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
917 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
918 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
919 "FileNameLength = %d\n", fni->FileNameLength);
920 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
921 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
923 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
924 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
925 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
927 /* This event will not be reported */
928 r = CreateDirectoryW(dir, NULL);
929 ok(r == TRUE, "failed to create directory\n");
931 r = MoveFileW(file, sub_file);
932 ok(r == TRUE, "failed to move file\n");
934 r = SleepEx(1000, TRUE);
935 ok(r != 0, "failed to receive file move event\n");
936 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
937 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
938 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
939 "FileNameLength = %d\n", fni->FileNameLength);
940 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
941 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
943 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
944 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
945 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
947 r = MoveFileW(sub_file, file);
948 ok(r == TRUE, "failed to move file\n");
950 r = SleepEx(1000, TRUE);
951 ok(r != 0, "failed to receive file move event\n");
952 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
953 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
954 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
955 "FileNameLength = %d\n", fni->FileNameLength);
956 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
957 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
959 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
960 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
961 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
963 r = DeleteFileW(file);
964 ok(r == TRUE, "failed to delete file\n");
966 r = SleepEx(1000, TRUE);
967 ok(r != 0, "failed to receive file removal event\n");
968 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
969 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
970 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
971 "FileNameLength = %d\n", fni->FileNameLength);
972 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
973 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
975 CloseHandle(hdir);
977 hdir = CreateFileW(path, GENERIC_READ,
978 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
979 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
980 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
982 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
983 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
984 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
986 r = MoveFileW(dir, file);
987 ok(r == TRUE, "failed to move directory\n");
989 r = SleepEx(1000, TRUE);
990 ok(r != 0, "failed to receive directory move event\n");
991 if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME)
993 ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %d\n", fni->Action);
994 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
995 "FileNameLength = %d\n", fni->FileNameLength);
996 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
997 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
998 ok(fni->NextEntryOffset != 0, "no next entry in movement event\n");
999 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
1000 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
1001 ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %d\n", fni_next->Action);
1002 ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
1003 "FileNameLength = %d\n", fni_next->FileNameLength);
1004 ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
1005 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR)));
1007 else
1009 todo_wine ok(0, "Expected rename event\n");
1011 if (fni->NextEntryOffset == 0)
1013 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
1014 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
1015 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
1017 r = SleepEx(1000, TRUE);
1018 ok(r != 0, "failed to receive directory move event\n");
1022 r = CreateDirectoryW(dir, NULL);
1023 ok(r == TRUE, "failed to create directory\n");
1025 r = RemoveDirectoryW(dir);
1026 ok(r == TRUE, "failed to remove directory\n");
1028 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
1029 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
1030 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
1032 r = SleepEx(1000, TRUE);
1033 ok(r != 0, "failed to receive directory creation event\n");
1034 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
1035 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
1036 "FileNameLength = %d\n", fni->FileNameLength);
1037 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
1038 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR)));
1039 if (fni->NextEntryOffset)
1040 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
1041 else
1043 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
1044 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
1045 ok(r == TRUE, "pReadDirectoryChangesW failed\n");
1047 r = SleepEx(1000, TRUE);
1048 ok(r != 0, "failed to receive directory removal event\n");
1049 fni_next = fni;
1051 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
1052 ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni_next->Action);
1053 ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
1054 "FileNameLength = %d\n", fni_next->FileNameLength);
1055 ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
1056 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR)));
1058 CloseHandle(hdir);
1059 RemoveDirectoryW(file);
1060 RemoveDirectoryW(path);
1063 static void test_ffcn_directory_overlap(void)
1065 HANDLE parent_watch, child_watch, parent_thread, child_thread;
1066 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH];
1067 char tempfile[MAX_PATH];
1068 DWORD threadId;
1069 BOOL ret;
1071 /* Setup directory hierarchy */
1072 ret = GetTempPathA(MAX_PATH, workdir);
1073 ok((ret > 0) && (ret <= MAX_PATH),
1074 "GetTempPathA error: %d\n", GetLastError());
1076 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile);
1077 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1078 ret = DeleteFileA(tempfile);
1079 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1081 lstrcpyA(parentdir, tempfile);
1082 ret = CreateDirectoryA(parentdir, NULL);
1083 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
1085 lstrcpyA(childdir, parentdir);
1086 lstrcatA(childdir, "\\c");
1087 ret = CreateDirectoryA(childdir, NULL);
1088 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
1091 /* When recursively watching overlapping directories, changes in child
1092 * should trigger notifications for both child and parent */
1093 parent_thread = StartNotificationThread(parentdir, TRUE,
1094 FILE_NOTIFY_CHANGE_FILE_NAME);
1095 child_thread = StartNotificationThread(childdir, TRUE,
1096 FILE_NOTIFY_CHANGE_FILE_NAME);
1098 /* Create a file in child */
1099 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1100 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1102 /* Both watches should trigger */
1103 ret = FinishNotificationThread(parent_thread);
1104 ok(ret, "Missed parent notification\n");
1105 ret = FinishNotificationThread(child_thread);
1106 ok(ret, "Missed child notification\n");
1108 ret = DeleteFileA(tempfile);
1109 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1112 /* Removing a recursive parent watch should not affect child watches. Doing
1113 * so used to crash wineserver. */
1114 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE,
1115 FILE_NOTIFY_CHANGE_FILE_NAME);
1116 ok(parent_watch != INVALID_HANDLE_VALUE,
1117 "FindFirstChangeNotification error: %d\n", GetLastError());
1118 child_watch = FindFirstChangeNotificationA(childdir, TRUE,
1119 FILE_NOTIFY_CHANGE_FILE_NAME);
1120 ok(child_watch != INVALID_HANDLE_VALUE,
1121 "FindFirstChangeNotification error: %d\n", GetLastError());
1123 ret = FindCloseChangeNotification(parent_watch);
1124 ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError());
1126 child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0,
1127 &threadId);
1128 ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError());
1130 /* Create a file in child */
1131 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1132 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1134 /* Child watch should trigger */
1135 ret = FinishNotificationThread(child_thread);
1136 ok(ret, "Missed child notification\n");
1138 /* clean up */
1139 ret = DeleteFileA(tempfile);
1140 ok(ret, "DeleteFileA error: %d\n", GetLastError());
1142 ret = RemoveDirectoryA(childdir);
1143 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1145 ret = RemoveDirectoryA(parentdir);
1146 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1149 START_TEST(change)
1151 HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
1152 pReadDirectoryChangesW = (void *)GetProcAddress(hkernel32, "ReadDirectoryChangesW");
1154 test_ffcnMultipleThreads();
1155 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
1156 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
1157 placed first. */
1158 test_FindFirstChangeNotification();
1159 test_ffcn();
1160 test_readdirectorychanges();
1161 test_readdirectorychanges_null();
1162 test_readdirectorychanges_filedir();
1163 test_readdirectorychanges_cr();
1164 test_ffcn_directory_overlap();