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
33 #define WIN32_NO_STATUS
34 #include "wine/test.h"
39 static DWORD CALLBACK
NotificationThread(LPVOID arg
)
42 BOOL notified
= FALSE
;
46 status
= WaitForSingleObject(change
, 100);
48 if (status
== WAIT_OBJECT_0
) {
50 FindNextChangeNotification(change
);
53 ret
= FindCloseChangeNotification(change
);
54 ok( ret
, "FindCloseChangeNotification error: %d\n",
57 ExitThread((DWORD
)notified
);
60 static HANDLE
StartNotificationThread(LPCSTR path
, BOOL subtree
, DWORD flags
)
62 HANDLE change
, thread
;
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());
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");
87 static void test_FindFirstChangeNotification(void)
89 HANDLE change
, file
, thread
;
90 DWORD attributes
, count
;
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";
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",
117 ret
= FindCloseChangeNotification(NULL
);
118 ok(!ret
&& GetLastError() == ERROR_INVALID_HANDLE
, "FindCloseChangeNotification error: %d\n",
121 ret
= GetTempPathA(MAX_PATH
, dirname1
);
122 ok(ret
, "GetTempPathA error: %d\n", GetLastError());
124 ret
= GetTempFileNameA(dirname1
, "ffc", 0, workdir
);
125 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
126 DeleteFileA( workdir
);
128 ret
= CreateDirectoryA(workdir
, NULL
);
129 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
131 ret
= GetTempFileNameA(workdir
, prefix
, 0, filename1
);
132 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
134 file
= CreateFileA(filename1
, GENERIC_WRITE
|GENERIC_READ
, 0, NULL
, CREATE_ALWAYS
,
135 FILE_ATTRIBUTE_NORMAL
, 0);
136 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %d\n", GetLastError());
137 ret
= CloseHandle(file
);
138 ok( ret
, "CloseHandle error: %d\n", GetLastError());
140 /* Try to register notification for a file. win98 and win2k behave differently here */
141 change
= FindFirstChangeNotificationA(filename1
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
142 ok(change
== INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_DIRECTORY
||
143 GetLastError() == ERROR_FILE_NOT_FOUND
),
144 "FindFirstChangeNotification error: %d\n", GetLastError());
146 lstrcpyA(dirname1
, filename1
);
147 lstrcatA(dirname1
, "dir");
149 lstrcpyA(dirname2
, dirname1
);
150 lstrcatA(dirname2
, "new");
152 ret
= CreateDirectoryA(dirname1
, NULL
);
153 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
155 /* What if we move the directory we registered notification for? */
156 thread
= StartNotificationThread(dirname1
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
157 ret
= MoveFileA(dirname1
, dirname2
);
158 ok(ret
, "MoveFileA error: %d\n", GetLastError());
159 /* win9x and win2k behave differently here, don't check result */
160 FinishNotificationThread(thread
);
162 /* What if we remove the directory we registered notification for? */
163 thread
= StartNotificationThread(dirname2
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
164 ret
= RemoveDirectoryA(dirname2
);
165 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
166 /* win9x and win2k behave differently here, don't check result */
167 FinishNotificationThread(thread
);
169 /* functional checks */
171 /* Create a directory */
172 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
173 ret
= CreateDirectoryA(dirname1
, NULL
);
174 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
175 ok(FinishNotificationThread(thread
), "Missed notification\n");
177 /* Rename a directory */
178 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
179 ret
= MoveFileA(dirname1
, dirname2
);
180 ok(ret
, "MoveFileA error: %d\n", GetLastError());
181 ok(FinishNotificationThread(thread
), "Missed notification\n");
183 /* Delete a directory */
184 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
185 ret
= RemoveDirectoryA(dirname2
);
186 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
187 ok(FinishNotificationThread(thread
), "Missed notification\n");
189 lstrcpyA(filename2
, filename1
);
190 lstrcatA(filename2
, "new");
193 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
194 ret
= MoveFileA(filename1
, filename2
);
195 ok(ret
, "MoveFileA error: %d\n", GetLastError());
196 ok(FinishNotificationThread(thread
), "Missed notification\n");
199 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
200 ret
= DeleteFileA(filename2
);
201 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
202 ok(FinishNotificationThread(thread
), "Missed notification\n");
205 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
206 file
= CreateFileA(filename2
, GENERIC_WRITE
|GENERIC_READ
, 0, NULL
, CREATE_ALWAYS
,
207 FILE_ATTRIBUTE_NORMAL
, 0);
208 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %d\n", GetLastError());
209 ret
= CloseHandle(file
);
210 ok( ret
, "CloseHandle error: %d\n", GetLastError());
211 ok(FinishNotificationThread(thread
), "Missed notification\n");
213 attributes
= GetFileAttributesA(filename2
);
214 ok(attributes
!= INVALID_FILE_ATTRIBUTES
, "GetFileAttributesA error: %d\n", GetLastError());
215 attributes
&= FILE_ATTRIBUTE_READONLY
;
217 /* Change file attributes */
218 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_ATTRIBUTES
);
219 ret
= SetFileAttributesA(filename2
, attributes
);
220 ok(ret
, "SetFileAttributesA error: %d\n", GetLastError());
221 ok(FinishNotificationThread(thread
), "Missed notification\n");
223 /* Change last write time by writing to a file */
224 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_LAST_WRITE
);
225 file
= CreateFileA(filename2
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
226 FILE_ATTRIBUTE_NORMAL
, 0);
227 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %d\n", GetLastError());
228 memset(buffer
, 0, sizeof(buffer
));
229 ret
= WriteFile(file
, buffer
, sizeof(buffer
), &count
, NULL
);
230 ok(ret
&& count
== sizeof(buffer
), "WriteFile error: %d\n", GetLastError());
231 ret
= CloseHandle(file
);
232 ok( ret
, "CloseHandle error: %d\n", GetLastError());
233 ok(FinishNotificationThread(thread
), "Missed notification\n");
235 /* Change file size by truncating a file */
236 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_SIZE
);
237 file
= CreateFileA(filename2
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
238 FILE_ATTRIBUTE_NORMAL
, 0);
239 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %d\n", GetLastError());
240 ret
= WriteFile(file
, buffer
, sizeof(buffer
) / 2, &count
, NULL
);
241 ok(ret
&& count
== sizeof(buffer
) / 2, "WriteFileA error: %d\n", GetLastError());
242 ret
= CloseHandle(file
);
243 ok( ret
, "CloseHandle error: %d\n", GetLastError());
244 ok(FinishNotificationThread(thread
), "Missed notification\n");
248 ret
= DeleteFileA(filename2
);
249 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
251 ret
= RemoveDirectoryA(workdir
);
252 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
255 /* this test concentrates more on the wait behaviour of the handle */
256 static void test_ffcn(void)
261 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
], filename
[MAX_PATH
];
262 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
263 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
264 static const WCHAR szZoo
[] = { '\\','z','o','o',0 };
266 SetLastError(0xdeadbeef);
267 r
= GetTempPathW( MAX_PATH
, path
);
268 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
270 win_skip("GetTempPathW is not implemented\n");
273 ok( r
!= 0, "temp path failed\n");
277 lstrcatW( path
, szBoo
);
278 lstrcpyW( subdir
, path
);
279 lstrcatW( subdir
, szHoo
);
281 lstrcpyW( filename
, path
);
282 lstrcatW( filename
, szZoo
);
284 RemoveDirectoryW( subdir
);
285 RemoveDirectoryW( path
);
287 r
= CreateDirectoryW(path
, NULL
);
288 ok( r
== TRUE
, "failed to create directory\n");
290 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
291 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
293 handle
= FindFirstChangeNotificationW( path
, 1, filter
);
294 ok( handle
!= INVALID_HANDLE_VALUE
, "invalid handle\n");
296 r
= WaitForSingleObject( handle
, 0 );
297 ok( r
== STATUS_TIMEOUT
, "should time out\n");
299 file
= CreateFileW( filename
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
300 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0 );
301 ok( file
!= INVALID_HANDLE_VALUE
, "CreateFile error %u\n", GetLastError() );
304 r
= WaitForSingleObject( handle
, 0 );
305 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
307 r
= WaitForSingleObject( handle
, 0 );
308 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
310 r
= FindNextChangeNotification(handle
);
311 ok( r
== TRUE
, "find next failed\n");
313 r
= WaitForSingleObject( handle
, 0 );
314 ok( r
== STATUS_TIMEOUT
, "should time out\n");
316 r
= DeleteFileW( filename
);
317 ok( r
== TRUE
, "failed to remove file\n");
319 r
= WaitForSingleObject( handle
, 0 );
320 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
322 r
= WaitForSingleObject( handle
, 0 );
323 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
325 r
= FindNextChangeNotification(handle
);
326 ok( r
== TRUE
, "find next failed\n");
328 r
= WaitForSingleObject( handle
, 0 );
329 ok( r
== STATUS_TIMEOUT
, "should time out\n");
331 r
= CreateDirectoryW( subdir
, NULL
);
332 ok( r
== TRUE
, "failed to create subdir\n");
334 r
= WaitForSingleObject( handle
, 0 );
335 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
337 r
= WaitForSingleObject( handle
, 0 );
338 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
340 r
= FindNextChangeNotification(handle
);
341 ok( r
== TRUE
, "find next failed\n");
343 r
= WaitForSingleObject( handle
, 0 );
344 ok( r
== STATUS_TIMEOUT
, "should time out\n");
346 r
= RemoveDirectoryW( subdir
);
347 ok( r
== TRUE
, "failed to remove subdir\n");
349 r
= WaitForSingleObject( handle
, 0 );
350 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
352 r
= WaitForSingleObject( handle
, 0 );
353 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
355 r
= FindNextChangeNotification(handle
);
356 ok( r
== TRUE
, "find next failed\n");
358 r
= FindNextChangeNotification(handle
);
359 ok( r
== TRUE
, "find next failed\n");
361 r
= FindCloseChangeNotification(handle
);
362 ok( r
== TRUE
, "should succeed\n");
364 r
= RemoveDirectoryW( path
);
365 ok( r
== TRUE
, "failed to remove dir\n");
368 /* this test concentrates on the wait behavior when multiple threads are
369 * waiting on a change notification handle. */
370 static void test_ffcnMultipleThreads(void)
373 DWORD filter
, threadId
, status
, exitcode
;
375 char tmp
[MAX_PATH
], path
[MAX_PATH
];
377 r
= GetTempPathA(MAX_PATH
, tmp
);
378 ok(r
, "GetTempPathA error: %d\n", GetLastError());
380 r
= GetTempFileNameA(tmp
, "ffc", 0, path
);
381 ok(r
, "GetTempFileNameA error: %d\n", GetLastError());
384 r
= CreateDirectoryA(path
, NULL
);
385 ok(r
, "CreateDirectoryA error: %d\n", GetLastError());
387 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
388 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
390 handles
[0] = FindFirstChangeNotificationA(path
, FALSE
, filter
);
391 ok(handles
[0] != INVALID_HANDLE_VALUE
, "FindFirstChangeNotification error: %d\n", GetLastError());
393 /* Test behavior if a waiting thread holds the last reference to a change
394 * directory object with an empty wine user APC queue for this thread (bug #7286) */
396 /* Create our notification thread */
397 handles
[1] = CreateThread(NULL
, 0, NotificationThread
, handles
[0], 0,
399 ok(handles
[1] != NULL
, "CreateThread error: %d\n", GetLastError());
401 status
= WaitForMultipleObjects(2, handles
, FALSE
, 5000);
402 ok(status
== WAIT_OBJECT_0
|| status
== WAIT_OBJECT_0
+1, "WaitForMultipleObjects status %d error %d\n", status
, GetLastError());
403 ok(GetExitCodeThread(handles
[1], &exitcode
), "Could not retrieve thread exit code\n");
406 r
= RemoveDirectoryA( path
);
407 ok( r
== TRUE
, "failed to remove dir\n");
410 static BOOL (WINAPI
*pReadDirectoryChangesW
)(HANDLE
,LPVOID
,DWORD
,BOOL
,DWORD
,
411 LPDWORD
,LPOVERLAPPED
,LPOVERLAPPED_COMPLETION_ROUTINE
);
413 static void test_readdirectorychanges(void)
417 DWORD fflags
, filter
= 0, r
, dwCount
;
419 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
], subsubdir
[MAX_PATH
];
420 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
421 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
422 static const WCHAR szGa
[] = { '\\','h','o','o','\\','g','a',0 };
423 PFILE_NOTIFY_INFORMATION pfni
;
424 BOOL got_subdir_change
= FALSE
;
426 if (!pReadDirectoryChangesW
)
428 win_skip("ReadDirectoryChangesW is not available\n");
432 SetLastError(0xdeadbeef);
433 r
= GetTempPathW( MAX_PATH
, path
);
434 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
436 win_skip("GetTempPathW is not implemented\n");
439 ok( r
!= 0, "temp path failed\n");
443 lstrcatW( path
, szBoo
);
444 lstrcpyW( subdir
, path
);
445 lstrcatW( subdir
, szHoo
);
447 lstrcpyW( subsubdir
, path
);
448 lstrcatW( subsubdir
, szGa
);
450 RemoveDirectoryW( subsubdir
);
451 RemoveDirectoryW( subdir
);
452 RemoveDirectoryW( path
);
454 r
= CreateDirectoryW(path
, NULL
);
455 ok( r
== TRUE
, "failed to create directory\n");
457 SetLastError(0xd0b00b00);
458 r
= pReadDirectoryChangesW(NULL
,NULL
,0,FALSE
,0,NULL
,NULL
,NULL
);
459 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
460 ok(r
==FALSE
, "should return false\n");
462 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
463 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
464 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
465 OPEN_EXISTING
, fflags
, NULL
);
466 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
468 ov
.hEvent
= CreateEventW( NULL
, 1, 0, NULL
);
470 SetLastError(0xd0b00b00);
471 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,0,NULL
,NULL
,NULL
);
472 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
473 ok(r
==FALSE
, "should return false\n");
475 SetLastError(0xd0b00b00);
476 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,0,NULL
,&ov
,NULL
);
477 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
478 ok(r
==FALSE
, "should return false\n");
480 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
481 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
482 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
483 filter
|= FILE_NOTIFY_CHANGE_SIZE
;
484 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
485 filter
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
486 filter
|= FILE_NOTIFY_CHANGE_CREATION
;
487 filter
|= FILE_NOTIFY_CHANGE_SECURITY
;
489 SetLastError(0xd0b00b00);
492 memset( buffer
, 0, sizeof buffer
);
494 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,-1,NULL
,&ov
,NULL
);
495 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
496 ok(r
==FALSE
, "should return false\n");
498 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,&ov
,NULL
);
499 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
500 ok(r
==FALSE
, "should return false\n");
502 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,TRUE
,filter
,NULL
,&ov
,NULL
);
503 ok(r
==TRUE
, "should return true\n");
505 r
= WaitForSingleObject( ov
.hEvent
, 10 );
506 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
508 r
= CreateDirectoryW( subdir
, NULL
);
509 ok( r
== TRUE
, "failed to create directory\n");
511 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
512 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
514 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
515 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
517 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
518 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
519 ok( pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n" );
520 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
521 ok( !memcmp(pfni
->FileName
,&szHoo
[1],6), "name wrong\n" );
523 ResetEvent(ov
.hEvent
);
524 SetLastError(0xd0b00b00);
525 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,NULL
,NULL
);
526 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
527 ok(r
==FALSE
, "should return false\n");
529 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,&ov
,NULL
);
530 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
531 ok(r
==FALSE
, "should return false\n");
533 filter
= FILE_NOTIFY_CHANGE_SIZE
;
539 S(U(ov
)).OffsetHigh
= 0;
540 memset( buffer
, 0, sizeof buffer
);
541 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
542 ok(r
==TRUE
, "should return true\n");
544 ok( (NTSTATUS
)ov
.Internal
== STATUS_PENDING
, "ov.Internal wrong\n");
545 ok( ov
.InternalHigh
== 1, "ov.InternalHigh wrong\n");
547 r
= WaitForSingleObject( ov
.hEvent
, 0 );
548 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
550 r
= RemoveDirectoryW( subdir
);
551 ok( r
== TRUE
, "failed to remove directory\n");
553 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
554 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
556 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
557 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
559 if ((NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
)
561 r
= GetOverlappedResult( hdir
, &ov
, &dwCount
, TRUE
);
562 ok( r
== TRUE
, "getoverlappedresult failed\n");
563 ok( dwCount
== 0x12, "count wrong\n");
566 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
567 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
568 ok( pfni
->Action
== FILE_ACTION_REMOVED
, "action wrong\n" );
569 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
570 ok( !memcmp(pfni
->FileName
,&szHoo
[1],6), "name wrong\n" );
572 /* what happens if the buffer is too small? */
573 r
= pReadDirectoryChangesW(hdir
,buffer
,0x10,FALSE
,filter
,NULL
,&ov
,NULL
);
574 ok(r
==TRUE
, "should return true\n");
576 r
= CreateDirectoryW( subdir
, NULL
);
577 ok( r
== TRUE
, "failed to create directory\n");
579 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
580 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
582 ok( (NTSTATUS
)ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
583 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
585 /* test the recursive watch */
586 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
587 ok(r
==TRUE
, "should return true\n");
589 r
= CreateDirectoryW( subsubdir
, NULL
);
590 ok( r
== TRUE
, "failed to create directory\n");
594 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
595 ok(r
== WAIT_OBJECT_0
, "should be ready\n" );
596 if (r
== WAIT_TIMEOUT
) break;
598 ok((NTSTATUS
) ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
600 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
603 /* We might get one or more modified events on the parent dir */
604 if (pfni
->Action
== FILE_ACTION_MODIFIED
)
606 ok(pfni
->FileNameLength
== 3 * sizeof(WCHAR
), "len wrong\n" );
607 ok(!memcmp(pfni
->FileName
, &szGa
[1], 3 * sizeof(WCHAR
)), "name wrong\n");
611 ok(pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n");
612 ok(pfni
->FileNameLength
== 6 * sizeof(WCHAR
), "len wrong\n" );
613 ok(!memcmp(pfni
->FileName
, &szGa
[1], 6 * sizeof(WCHAR
)), "name wrong\n");
614 got_subdir_change
= TRUE
;
616 if (!pfni
->NextEntryOffset
) break;
617 pfni
= (PFILE_NOTIFY_INFORMATION
)((char *)pfni
+ pfni
->NextEntryOffset
);
620 if (got_subdir_change
) break;
622 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
623 ok(r
==TRUE
, "should return true\n");
625 ok(got_subdir_change
, "didn't get subdir change\n");
627 r
= RemoveDirectoryW( subsubdir
);
628 ok( r
== TRUE
, "failed to remove directory\n");
632 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
633 ok(r
==TRUE
, "should return true\n");
635 r
= RemoveDirectoryW( subdir
);
636 ok( r
== TRUE
, "failed to remove directory\n");
638 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
639 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
641 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
642 /* we may get a notification for the parent dir too */
643 if (pfni
->Action
== FILE_ACTION_MODIFIED
&& pfni
->NextEntryOffset
)
645 ok( pfni
->FileNameLength
== 3*sizeof(WCHAR
), "len wrong %u\n", pfni
->FileNameLength
);
646 ok( !memcmp(pfni
->FileName
,&szGa
[1],3*sizeof(WCHAR
)), "name wrong\n" );
647 pfni
= (PFILE_NOTIFY_INFORMATION
)((char *)pfni
+ pfni
->NextEntryOffset
);
649 ok( pfni
->NextEntryOffset
== 0, "offset wrong %u\n", pfni
->NextEntryOffset
);
650 ok( pfni
->Action
== FILE_ACTION_REMOVED
, "action wrong %u\n", pfni
->Action
);
651 ok( pfni
->FileNameLength
== 6*sizeof(WCHAR
), "len wrong %u\n", pfni
->FileNameLength
);
652 ok( !memcmp(pfni
->FileName
,&szGa
[1],6*sizeof(WCHAR
)), "name wrong\n" );
654 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
655 dwCount
= (char *)&pfni
->FileName
[pfni
->FileNameLength
/sizeof(WCHAR
)] - buffer
;
656 ok( ov
.InternalHigh
== dwCount
, "ov.InternalHigh wrong %lu/%u\n",ov
.InternalHigh
, dwCount
);
660 r
= RemoveDirectoryW( path
);
661 ok( r
== TRUE
, "failed to remove directory\n");
664 /* show the behaviour when a null buffer is passed */
665 static void test_readdirectorychanges_null(void)
670 DWORD fflags
, filter
= 0;
672 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
];
673 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
674 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
675 PFILE_NOTIFY_INFORMATION pfni
;
677 if (!pReadDirectoryChangesW
)
679 win_skip("ReadDirectoryChangesW is not available\n");
682 SetLastError(0xdeadbeef);
683 r
= GetTempPathW( MAX_PATH
, path
);
684 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
686 win_skip("GetTempPathW is not implemented\n");
689 ok( r
!= 0, "temp path failed\n");
693 lstrcatW( path
, szBoo
);
694 lstrcpyW( subdir
, path
);
695 lstrcatW( subdir
, szHoo
);
697 RemoveDirectoryW( subdir
);
698 RemoveDirectoryW( path
);
700 r
= CreateDirectoryW(path
, NULL
);
701 ok( r
== TRUE
, "failed to create directory\n");
703 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
704 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
705 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
706 OPEN_EXISTING
, fflags
, NULL
);
707 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
709 ov
.hEvent
= CreateEventW( NULL
, 1, 0, NULL
);
711 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
712 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
714 SetLastError(0xd0b00b00);
717 memset( buffer
, 0, sizeof buffer
);
719 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,filter
,NULL
,&ov
,NULL
);
720 ok(r
==TRUE
, "should return true\n");
722 r
= WaitForSingleObject( ov
.hEvent
, 0 );
723 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
725 r
= CreateDirectoryW( subdir
, NULL
);
726 ok( r
== TRUE
, "failed to create directory\n");
728 r
= WaitForSingleObject( ov
.hEvent
, 0 );
729 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
731 ok( (NTSTATUS
)ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
732 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
737 S(U(ov
)).OffsetHigh
= 0;
738 memset( buffer
, 0, sizeof buffer
);
740 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
741 ok(r
==TRUE
, "should return true\n");
743 r
= WaitForSingleObject( ov
.hEvent
, 0 );
744 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
746 r
= RemoveDirectoryW( subdir
);
747 ok( r
== TRUE
, "failed to remove directory\n");
749 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
750 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
752 ok( (NTSTATUS
)ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
753 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
755 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
756 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
760 r
= RemoveDirectoryW( path
);
761 ok( r
== TRUE
, "failed to remove directory\n");
764 static void test_readdirectorychanges_filedir(void)
769 DWORD fflags
, filter
= 0;
771 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
], file
[MAX_PATH
];
772 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
773 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
774 static const WCHAR szFoo
[] = { '\\','f','o','o',0 };
775 PFILE_NOTIFY_INFORMATION pfni
;
777 SetLastError(0xdeadbeef);
778 r
= GetTempPathW( MAX_PATH
, path
);
779 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
781 win_skip("GetTempPathW is not implemented\n");
784 ok( r
!= 0, "temp path failed\n");
788 lstrcatW( path
, szBoo
);
789 lstrcpyW( subdir
, path
);
790 lstrcatW( subdir
, szHoo
);
792 lstrcpyW( file
, path
);
793 lstrcatW( file
, szFoo
);
796 RemoveDirectoryW( subdir
);
797 RemoveDirectoryW( path
);
799 r
= CreateDirectoryW(path
, NULL
);
800 ok( r
== TRUE
, "failed to create directory\n");
802 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
803 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
804 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
805 OPEN_EXISTING
, fflags
, NULL
);
806 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
808 ov
.hEvent
= CreateEventW( NULL
, 0, 0, NULL
);
810 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
812 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,TRUE
,filter
,NULL
,&ov
,NULL
);
813 ok(r
==TRUE
, "should return true\n");
815 r
= WaitForSingleObject( ov
.hEvent
, 10 );
816 ok( r
== WAIT_TIMEOUT
, "should timeout\n" );
818 r
= CreateDirectoryW( subdir
, NULL
);
819 ok( r
== TRUE
, "failed to create directory\n");
821 hfile
= CreateFileW( file
, GENERIC_READ
|GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
822 ok( hfile
!= INVALID_HANDLE_VALUE
, "failed to create file\n");
823 ok( CloseHandle(hfile
), "failed to close file\n");
825 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
826 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
828 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
829 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
831 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
832 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
833 ok( pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n" );
834 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
835 ok( !memcmp(pfni
->FileName
,&szFoo
[1],6), "name wrong\n" );
837 r
= DeleteFileW( file
);
838 ok( r
== TRUE
, "failed to delete file\n");
840 r
= RemoveDirectoryW( subdir
);
841 ok( r
== TRUE
, "failed to remove directory\n");
845 r
= RemoveDirectoryW( path
);
846 ok( r
== TRUE
, "failed to remove directory\n");
849 static void CALLBACK
readdirectorychanges_cr(DWORD error
, DWORD len
, LPOVERLAPPED ov
)
851 ok(error
== 0, "ReadDirectoryChangesW error %d\n", error
);
852 ok(ov
->hEvent
== (void*)0xdeadbeef, "hEvent should not have changed\n");
855 static void test_readdirectorychanges_cr(void)
857 static const WCHAR szBoo
[] = { '\\','b','o','o','\\',0 };
858 static const WCHAR szDir
[] = { 'd','i','r',0 };
859 static const WCHAR szFile
[] = { 'f','i','l','e',0 };
860 static const WCHAR szBackslash
[] = { '\\',0 };
862 WCHAR path
[MAX_PATH
], file
[MAX_PATH
], dir
[MAX_PATH
], sub_file
[MAX_PATH
];
863 FILE_NOTIFY_INFORMATION fni
[1024], *fni_next
;
868 if (!pReadDirectoryChangesW
)
870 win_skip("ReadDirectoryChangesW is not available\n");
874 SetLastError(0xdeadbeef);
875 r
= GetTempPathW(MAX_PATH
, path
);
876 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
878 win_skip("GetTempPathW is not implemented\n");
881 ok(r
!= 0, "temp path failed\n");
885 lstrcatW(path
, szBoo
);
887 lstrcatW(dir
, szDir
);
888 lstrcpyW(file
, path
);
889 lstrcatW(file
, szFile
);
890 lstrcpyW(sub_file
, dir
);
891 lstrcatW(sub_file
, szBackslash
);
892 lstrcatW(sub_file
, szFile
);
895 RemoveDirectoryW(dir
);
896 RemoveDirectoryW(path
);
898 r
= CreateDirectoryW(path
, NULL
);
899 ok(r
== TRUE
, "failed to create directory\n");
901 hdir
= CreateFileW(path
, GENERIC_READ
,
902 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
,
903 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
, NULL
);
904 ok(hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
906 memset(&ov
, 0, sizeof(ov
));
907 ov
.hEvent
= (void*)0xdeadbeef;
908 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
909 FILE_NOTIFY_CHANGE_FILE_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
910 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
912 hfile
= CreateFileW(file
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
913 ok(hfile
!= INVALID_HANDLE_VALUE
, "failed to create file\n");
916 r
= SleepEx(1000, TRUE
);
917 ok(r
!= 0, "failed to receive file creation event\n");
918 ok(fni
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
919 ok(fni
->Action
== FILE_ACTION_ADDED
, "Action = %d\n", fni
->Action
);
920 ok(fni
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
921 "FileNameLength = %d\n", fni
->FileNameLength
);
922 ok(!memcmp(fni
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
923 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
925 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
926 FILE_NOTIFY_CHANGE_FILE_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
927 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
929 /* This event will not be reported */
930 r
= CreateDirectoryW(dir
, NULL
);
931 ok(r
== TRUE
, "failed to create directory\n");
933 r
= MoveFileW(file
, sub_file
);
934 ok(r
== TRUE
, "failed to move file\n");
936 r
= SleepEx(1000, TRUE
);
937 ok(r
!= 0, "failed to receive file move event\n");
938 ok(fni
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
939 ok(fni
->Action
== FILE_ACTION_REMOVED
, "Action = %d\n", fni
->Action
);
940 ok(fni
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
941 "FileNameLength = %d\n", fni
->FileNameLength
);
942 ok(!memcmp(fni
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
943 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
945 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
946 FILE_NOTIFY_CHANGE_FILE_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
947 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
949 r
= MoveFileW(sub_file
, file
);
950 ok(r
== TRUE
, "failed to move file\n");
952 r
= SleepEx(1000, TRUE
);
953 ok(r
!= 0, "failed to receive file move event\n");
954 ok(fni
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
955 ok(fni
->Action
== FILE_ACTION_ADDED
, "Action = %d\n", fni
->Action
);
956 ok(fni
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
957 "FileNameLength = %d\n", fni
->FileNameLength
);
958 ok(!memcmp(fni
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
959 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
961 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
962 FILE_NOTIFY_CHANGE_FILE_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
963 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
965 r
= DeleteFileW(file
);
966 ok(r
== TRUE
, "failed to delete file\n");
968 r
= SleepEx(1000, TRUE
);
969 ok(r
!= 0, "failed to receive file removal event\n");
970 ok(fni
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
971 ok(fni
->Action
== FILE_ACTION_REMOVED
, "Action = %d\n", fni
->Action
);
972 ok(fni
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
973 "FileNameLength = %d\n", fni
->FileNameLength
);
974 ok(!memcmp(fni
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
975 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
979 hdir
= CreateFileW(path
, GENERIC_READ
,
980 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
,
981 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
, NULL
);
982 ok(hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
984 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
985 FILE_NOTIFY_CHANGE_DIR_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
986 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
988 r
= MoveFileW(dir
, file
);
989 ok(r
== TRUE
, "failed to move directory\n");
991 r
= SleepEx(1000, TRUE
);
992 ok(r
!= 0, "failed to receive directory move event\n");
993 if (fni
->Action
== FILE_ACTION_RENAMED_OLD_NAME
)
995 ok(fni
->Action
== FILE_ACTION_RENAMED_OLD_NAME
, "Action = %d\n", fni
->Action
);
996 ok(fni
->FileNameLength
== lstrlenW(szDir
)*sizeof(WCHAR
),
997 "FileNameLength = %d\n", fni
->FileNameLength
);
998 ok(!memcmp(fni
->FileName
, szDir
, lstrlenW(szDir
)*sizeof(WCHAR
)),
999 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
1000 ok(fni
->NextEntryOffset
!= 0, "no next entry in movement event\n");
1001 fni_next
= (FILE_NOTIFY_INFORMATION
*)((char*)fni
+fni
->NextEntryOffset
);
1002 ok(fni_next
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
1003 ok(fni_next
->Action
== FILE_ACTION_RENAMED_NEW_NAME
, "Action = %d\n", fni_next
->Action
);
1004 ok(fni_next
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
1005 "FileNameLength = %d\n", fni_next
->FileNameLength
);
1006 ok(!memcmp(fni_next
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
1007 "FileName = %s\n", wine_dbgstr_wn(fni_next
->FileName
, fni_next
->FileNameLength
/sizeof(WCHAR
)));
1011 todo_wine
ok(0, "Expected rename event\n");
1013 if (fni
->NextEntryOffset
== 0)
1015 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
1016 FILE_NOTIFY_CHANGE_DIR_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
1017 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
1019 r
= SleepEx(1000, TRUE
);
1020 ok(r
!= 0, "failed to receive directory move event\n");
1024 r
= CreateDirectoryW(dir
, NULL
);
1025 ok(r
== TRUE
, "failed to create directory\n");
1027 r
= RemoveDirectoryW(dir
);
1028 ok(r
== TRUE
, "failed to remove directory\n");
1030 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
1031 FILE_NOTIFY_CHANGE_DIR_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
1032 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
1034 r
= SleepEx(1000, TRUE
);
1035 ok(r
!= 0, "failed to receive directory creation event\n");
1036 ok(fni
->Action
== FILE_ACTION_ADDED
, "Action = %d\n", fni
->Action
);
1037 ok(fni
->FileNameLength
== lstrlenW(szDir
)*sizeof(WCHAR
),
1038 "FileNameLength = %d\n", fni
->FileNameLength
);
1039 ok(!memcmp(fni
->FileName
, szDir
, lstrlenW(szDir
)*sizeof(WCHAR
)),
1040 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
1041 if (fni
->NextEntryOffset
)
1042 fni_next
= (FILE_NOTIFY_INFORMATION
*)((char*)fni
+fni
->NextEntryOffset
);
1045 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
1046 FILE_NOTIFY_CHANGE_DIR_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
1047 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
1049 r
= SleepEx(1000, TRUE
);
1050 ok(r
!= 0, "failed to receive directory removal event\n");
1053 ok(fni_next
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
1054 ok(fni_next
->Action
== FILE_ACTION_REMOVED
, "Action = %d\n", fni_next
->Action
);
1055 ok(fni_next
->FileNameLength
== lstrlenW(szDir
)*sizeof(WCHAR
),
1056 "FileNameLength = %d\n", fni_next
->FileNameLength
);
1057 ok(!memcmp(fni_next
->FileName
, szDir
, lstrlenW(szDir
)*sizeof(WCHAR
)),
1058 "FileName = %s\n", wine_dbgstr_wn(fni_next
->FileName
, fni_next
->FileNameLength
/sizeof(WCHAR
)));
1061 RemoveDirectoryW(file
);
1062 RemoveDirectoryW(path
);
1065 static void test_ffcn_directory_overlap(void)
1067 HANDLE parent_watch
, child_watch
, parent_thread
, child_thread
;
1068 char workdir
[MAX_PATH
], parentdir
[MAX_PATH
], childdir
[MAX_PATH
];
1069 char tempfile
[MAX_PATH
];
1073 /* Setup directory hierarchy */
1074 ret
= GetTempPathA(MAX_PATH
, workdir
);
1075 ok((ret
> 0) && (ret
<= MAX_PATH
),
1076 "GetTempPathA error: %d\n", GetLastError());
1078 ret
= GetTempFileNameA(workdir
, "fcn", 0, tempfile
);
1079 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
1080 ret
= DeleteFileA(tempfile
);
1081 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
1083 lstrcpyA(parentdir
, tempfile
);
1084 ret
= CreateDirectoryA(parentdir
, NULL
);
1085 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
1087 lstrcpyA(childdir
, parentdir
);
1088 lstrcatA(childdir
, "\\c");
1089 ret
= CreateDirectoryA(childdir
, NULL
);
1090 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
1093 /* When recursively watching overlapping directories, changes in child
1094 * should trigger notifications for both child and parent */
1095 parent_thread
= StartNotificationThread(parentdir
, TRUE
,
1096 FILE_NOTIFY_CHANGE_FILE_NAME
);
1097 child_thread
= StartNotificationThread(childdir
, TRUE
,
1098 FILE_NOTIFY_CHANGE_FILE_NAME
);
1100 /* Create a file in child */
1101 ret
= GetTempFileNameA(childdir
, "fcn", 0, tempfile
);
1102 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
1104 /* Both watches should trigger */
1105 ret
= FinishNotificationThread(parent_thread
);
1106 ok(ret
, "Missed parent notification\n");
1107 ret
= FinishNotificationThread(child_thread
);
1108 ok(ret
, "Missed child notification\n");
1110 ret
= DeleteFileA(tempfile
);
1111 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
1114 /* Removing a recursive parent watch should not affect child watches. Doing
1115 * so used to crash wineserver. */
1116 parent_watch
= FindFirstChangeNotificationA(parentdir
, TRUE
,
1117 FILE_NOTIFY_CHANGE_FILE_NAME
);
1118 ok(parent_watch
!= INVALID_HANDLE_VALUE
,
1119 "FindFirstChangeNotification error: %d\n", GetLastError());
1120 child_watch
= FindFirstChangeNotificationA(childdir
, TRUE
,
1121 FILE_NOTIFY_CHANGE_FILE_NAME
);
1122 ok(child_watch
!= INVALID_HANDLE_VALUE
,
1123 "FindFirstChangeNotification error: %d\n", GetLastError());
1125 ret
= FindCloseChangeNotification(parent_watch
);
1126 ok(ret
, "FindCloseChangeNotification error: %d\n", GetLastError());
1128 child_thread
= CreateThread(NULL
, 0, NotificationThread
, child_watch
, 0,
1130 ok(child_thread
!= NULL
, "CreateThread error: %d\n", GetLastError());
1132 /* Create a file in child */
1133 ret
= GetTempFileNameA(childdir
, "fcn", 0, tempfile
);
1134 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
1136 /* Child watch should trigger */
1137 ret
= FinishNotificationThread(child_thread
);
1138 ok(ret
, "Missed child notification\n");
1141 ret
= DeleteFileA(tempfile
);
1142 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
1144 ret
= RemoveDirectoryA(childdir
);
1145 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
1147 ret
= RemoveDirectoryA(parentdir
);
1148 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
1153 HMODULE hkernel32
= GetModuleHandleA("kernel32.dll");
1154 pReadDirectoryChangesW
= (void *)GetProcAddress(hkernel32
, "ReadDirectoryChangesW");
1156 test_ffcnMultipleThreads();
1157 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
1158 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
1160 test_FindFirstChangeNotification();
1162 test_readdirectorychanges();
1163 test_readdirectorychanges_null();
1164 test_readdirectorychanges_filedir();
1165 test_readdirectorychanges_cr();
1166 test_ffcn_directory_overlap();