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
31 static DWORD CALLBACK
NotificationThread(LPVOID arg
)
34 BOOL notified
= FALSE
;
38 status
= WaitForSingleObject(change
, 100);
40 if (status
== WAIT_OBJECT_0
) {
42 FindNextChangeNotification(change
);
45 ret
= FindCloseChangeNotification(change
);
46 ok( ret
, "FindCloseChangeNotification error: %d\n",
49 ExitThread((DWORD
)notified
);
52 static HANDLE
StartNotificationThread(LPCSTR path
, BOOL subtree
, DWORD flags
)
54 HANDLE change
, thread
;
57 change
= FindFirstChangeNotificationA(path
, subtree
, flags
);
58 ok(change
!= INVALID_HANDLE_VALUE
, "FindFirstChangeNotification error: %d\n", GetLastError());
60 thread
= CreateThread(NULL
, 0, NotificationThread
, change
, 0, &threadId
);
61 ok(thread
!= NULL
, "CreateThread error: %d\n", GetLastError());
66 static DWORD
FinishNotificationThread(HANDLE thread
)
68 DWORD status
, exitcode
;
70 status
= WaitForSingleObject(thread
, 5000);
71 ok(status
== WAIT_OBJECT_0
, "WaitForSingleObject status %d error %d\n", status
, GetLastError());
73 ok(GetExitCodeThread(thread
, &exitcode
), "Could not retrieve thread exit code\n");
79 static void test_FindFirstChangeNotification(void)
81 HANDLE change
, file
, thread
;
82 DWORD attributes
, count
;
85 char workdir
[MAX_PATH
], dirname1
[MAX_PATH
], dirname2
[MAX_PATH
];
86 char filename1
[MAX_PATH
], filename2
[MAX_PATH
];
87 static const char prefix
[] = "FCN";
92 change
= FindFirstChangeNotificationA("not-a-file", FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
93 ok(change
== INVALID_HANDLE_VALUE
, "Expected INVALID_HANDLE_VALUE, got %p\n", change
);
94 ok(GetLastError() == ERROR_FILE_NOT_FOUND
||
95 GetLastError() == ERROR_NO_MORE_FILES
, /* win95 */
96 "FindFirstChangeNotification error: %d\n", GetLastError());
98 if (0) /* This documents win2k behavior. It crashes on win98. */
100 change
= FindFirstChangeNotificationA(NULL
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
101 ok(change
== NULL
&& GetLastError() == ERROR_PATH_NOT_FOUND
,
102 "FindFirstChangeNotification error: %d\n", GetLastError());
105 ret
= FindNextChangeNotification(NULL
);
106 ok(!ret
&& GetLastError() == ERROR_INVALID_HANDLE
, "FindNextChangeNotification error: %d\n",
109 ret
= FindCloseChangeNotification(NULL
);
110 ok(!ret
&& GetLastError() == ERROR_INVALID_HANDLE
, "FindCloseChangeNotification error: %d\n",
113 ret
= GetTempPathA(MAX_PATH
, workdir
);
114 ok(ret
, "GetTempPathA error: %d\n", GetLastError());
116 lstrcatA(workdir
, "testFileChangeNotification");
118 ret
= CreateDirectoryA(workdir
, NULL
);
119 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
121 ret
= GetTempFileNameA(workdir
, prefix
, 0, filename1
);
122 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
124 file
= CreateFileA(filename1
, GENERIC_WRITE
|GENERIC_READ
, 0, NULL
, CREATE_ALWAYS
,
125 FILE_ATTRIBUTE_NORMAL
, 0);
126 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %d\n", GetLastError());
127 ret
= CloseHandle(file
);
128 ok( ret
, "CloseHandle error: %d\n", GetLastError());
130 /* Try to register notification for a file. win98 and win2k behave differently here */
131 change
= FindFirstChangeNotificationA(filename1
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
132 ok(change
== INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_DIRECTORY
||
133 GetLastError() == ERROR_FILE_NOT_FOUND
),
134 "FindFirstChangeNotification error: %d\n", GetLastError());
136 lstrcpyA(dirname1
, filename1
);
137 lstrcatA(dirname1
, "dir");
139 lstrcpyA(dirname2
, dirname1
);
140 lstrcatA(dirname2
, "new");
142 ret
= CreateDirectoryA(dirname1
, NULL
);
143 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
145 /* What if we move the directory we registered notification for? */
146 thread
= StartNotificationThread(dirname1
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
147 ret
= MoveFileA(dirname1
, dirname2
);
148 ok(ret
, "MoveFileA error: %d\n", GetLastError());
149 /* win9x and win2k behave differently here, don't check result */
150 FinishNotificationThread(thread
);
152 /* What if we remove the directory we registered notification for? */
153 thread
= StartNotificationThread(dirname2
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
154 ret
= RemoveDirectoryA(dirname2
);
155 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
156 /* win9x and win2k behave differently here, don't check result */
157 FinishNotificationThread(thread
);
159 /* functional checks */
161 /* Create a directory */
162 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
163 ret
= CreateDirectoryA(dirname1
, NULL
);
164 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
165 ok(FinishNotificationThread(thread
), "Missed notification\n");
167 /* Rename a directory */
168 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
169 ret
= MoveFileA(dirname1
, dirname2
);
170 ok(ret
, "MoveFileA error: %d\n", GetLastError());
171 ok(FinishNotificationThread(thread
), "Missed notification\n");
173 /* Delete a directory */
174 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
175 ret
= RemoveDirectoryA(dirname2
);
176 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
177 ok(FinishNotificationThread(thread
), "Missed notification\n");
179 lstrcpyA(filename2
, filename1
);
180 lstrcatA(filename2
, "new");
183 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
184 ret
= MoveFileA(filename1
, filename2
);
185 ok(ret
, "MoveFileA error: %d\n", GetLastError());
186 ok(FinishNotificationThread(thread
), "Missed notification\n");
189 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
190 ret
= DeleteFileA(filename2
);
191 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
192 ok(FinishNotificationThread(thread
), "Missed notification\n");
195 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
196 file
= CreateFileA(filename2
, GENERIC_WRITE
|GENERIC_READ
, 0, NULL
, CREATE_ALWAYS
,
197 FILE_ATTRIBUTE_NORMAL
, 0);
198 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %d\n", GetLastError());
199 ret
= CloseHandle(file
);
200 ok( ret
, "CloseHandle error: %d\n", GetLastError());
201 ok(FinishNotificationThread(thread
), "Missed notification\n");
203 attributes
= GetFileAttributesA(filename2
);
204 ok(attributes
!= INVALID_FILE_ATTRIBUTES
, "GetFileAttributesA error: %d\n", GetLastError());
205 attributes
&= FILE_ATTRIBUTE_READONLY
;
207 /* Change file attributes */
208 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_ATTRIBUTES
);
209 ret
= SetFileAttributesA(filename2
, attributes
);
210 ok(ret
, "SetFileAttributesA error: %d\n", GetLastError());
211 ok(FinishNotificationThread(thread
), "Missed notification\n");
213 /* Change last write time by writing to a file */
214 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_LAST_WRITE
);
215 file
= CreateFileA(filename2
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
216 FILE_ATTRIBUTE_NORMAL
, 0);
217 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %d\n", GetLastError());
218 memset(buffer
, 0, sizeof(buffer
));
219 ret
= WriteFile(file
, buffer
, sizeof(buffer
), &count
, NULL
);
220 ok(ret
&& count
== sizeof(buffer
), "WriteFile error: %d\n", GetLastError());
221 ret
= CloseHandle(file
);
222 ok( ret
, "CloseHandle error: %d\n", GetLastError());
223 ok(FinishNotificationThread(thread
), "Missed notification\n");
225 /* Change file size by truncating a file */
226 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_SIZE
);
227 file
= CreateFileA(filename2
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
228 FILE_ATTRIBUTE_NORMAL
, 0);
229 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %d\n", GetLastError());
230 ret
= WriteFile(file
, buffer
, sizeof(buffer
) / 2, &count
, NULL
);
231 ok(ret
&& count
== sizeof(buffer
) / 2, "WriteFileA error: %d\n", GetLastError());
232 ret
= CloseHandle(file
);
233 ok( ret
, "CloseHandle error: %d\n", GetLastError());
234 ok(FinishNotificationThread(thread
), "Missed notification\n");
238 ret
= DeleteFileA(filename2
);
239 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
241 ret
= RemoveDirectoryA(workdir
);
242 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
245 /* this test concentrates more on the wait behaviour of the handle */
246 static void test_ffcn(void)
251 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
];
252 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
253 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
255 SetLastError(0xdeadbeef);
256 r
= GetTempPathW( MAX_PATH
, path
);
257 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
259 win_skip("GetTempPathW is not implemented\n");
262 ok( r
!= 0, "temp path failed\n");
266 lstrcatW( path
, szBoo
);
267 lstrcpyW( subdir
, path
);
268 lstrcatW( subdir
, szHoo
);
270 RemoveDirectoryW( subdir
);
271 RemoveDirectoryW( path
);
273 r
= CreateDirectoryW(path
, NULL
);
274 ok( r
== TRUE
, "failed to create directory\n");
276 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
277 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
279 handle
= FindFirstChangeNotificationW( path
, 1, filter
);
280 ok( handle
!= INVALID_HANDLE_VALUE
, "invalid handle\n");
282 r
= WaitForSingleObject( handle
, 0 );
283 ok( r
== STATUS_TIMEOUT
, "should time out\n");
285 r
= CreateDirectoryW( subdir
, NULL
);
286 ok( r
== TRUE
, "failed to create subdir\n");
288 r
= WaitForSingleObject( handle
, 0 );
289 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
291 r
= WaitForSingleObject( handle
, 0 );
292 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
294 r
= FindNextChangeNotification(handle
);
295 ok( r
== TRUE
, "find next failed\n");
297 r
= WaitForSingleObject( handle
, 0 );
298 ok( r
== STATUS_TIMEOUT
, "should time out\n");
300 r
= RemoveDirectoryW( subdir
);
301 ok( r
== TRUE
, "failed to remove subdir\n");
303 r
= WaitForSingleObject( handle
, 0 );
304 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
306 r
= WaitForSingleObject( handle
, 0 );
307 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
309 r
= FindNextChangeNotification(handle
);
310 ok( r
== TRUE
, "find next failed\n");
312 r
= FindNextChangeNotification(handle
);
313 ok( r
== TRUE
, "find next failed\n");
315 r
= FindCloseChangeNotification(handle
);
316 ok( r
== TRUE
, "should succeed\n");
318 r
= RemoveDirectoryW( path
);
319 ok( r
== TRUE
, "failed to remove dir\n");
322 /* this test concentrates on the wait behavior when multiple threads are
323 * waiting on a change notification handle. */
324 static void test_ffcnMultipleThreads(void)
327 DWORD filter
, threadId
, status
, exitcode
;
331 r
= GetTempPathA(MAX_PATH
, path
);
332 ok(r
, "GetTempPathA error: %d\n", GetLastError());
334 lstrcatA(path
, "ffcnTestMultipleThreads");
336 RemoveDirectoryA(path
);
338 r
= CreateDirectoryA(path
, NULL
);
339 ok(r
, "CreateDirectoryA error: %d\n", GetLastError());
341 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
342 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
344 handles
[0] = FindFirstChangeNotificationA(path
, FALSE
, filter
);
345 ok(handles
[0] != INVALID_HANDLE_VALUE
, "FindFirstChangeNotification error: %d\n", GetLastError());
347 /* Test behavior if a waiting thread holds the last reference to a change
348 * directory object with an empty wine user APC queue for this thread (bug #7286) */
350 /* Create our notification thread */
351 handles
[1] = CreateThread(NULL
, 0, NotificationThread
, handles
[0], 0,
353 ok(handles
[1] != NULL
, "CreateThread error: %d\n", GetLastError());
355 status
= WaitForMultipleObjects(2, handles
, FALSE
, 5000);
356 ok(status
== WAIT_OBJECT_0
|| status
== WAIT_OBJECT_0
+1, "WaitForMultipleObjects status %d error %d\n", status
, GetLastError());
357 ok(GetExitCodeThread(handles
[1], &exitcode
), "Could not retrieve thread exit code\n");
360 r
= RemoveDirectoryA( path
);
361 ok( r
== TRUE
, "failed to remove dir\n");
364 static BOOL (WINAPI
*pReadDirectoryChangesW
)(HANDLE
,LPVOID
,DWORD
,BOOL
,DWORD
,
365 LPDWORD
,LPOVERLAPPED
,LPOVERLAPPED_COMPLETION_ROUTINE
);
367 static void test_readdirectorychanges(void)
371 DWORD fflags
, filter
= 0, r
, dwCount
;
373 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
], subsubdir
[MAX_PATH
];
374 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
375 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
376 static const WCHAR szGa
[] = { '\\','h','o','o','\\','g','a',0 };
377 PFILE_NOTIFY_INFORMATION pfni
;
379 if (!pReadDirectoryChangesW
)
381 win_skip("ReadDirectoryChangesW is not available\n");
385 SetLastError(0xdeadbeef);
386 r
= GetTempPathW( MAX_PATH
, path
);
387 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
389 win_skip("GetTempPathW is not implemented\n");
392 ok( r
!= 0, "temp path failed\n");
396 lstrcatW( path
, szBoo
);
397 lstrcpyW( subdir
, path
);
398 lstrcatW( subdir
, szHoo
);
400 lstrcpyW( subsubdir
, path
);
401 lstrcatW( subsubdir
, szGa
);
403 RemoveDirectoryW( subsubdir
);
404 RemoveDirectoryW( subdir
);
405 RemoveDirectoryW( path
);
407 r
= CreateDirectoryW(path
, NULL
);
408 ok( r
== TRUE
, "failed to create directory\n");
410 SetLastError(0xd0b00b00);
411 r
= pReadDirectoryChangesW(NULL
,NULL
,0,FALSE
,0,NULL
,NULL
,NULL
);
412 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
413 ok(r
==FALSE
, "should return false\n");
415 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
416 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
417 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
418 OPEN_EXISTING
, fflags
, NULL
);
419 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
421 ov
.hEvent
= CreateEventW( NULL
, 1, 0, NULL
);
423 SetLastError(0xd0b00b00);
424 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,0,NULL
,NULL
,NULL
);
425 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
426 ok(r
==FALSE
, "should return false\n");
428 SetLastError(0xd0b00b00);
429 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,0,NULL
,&ov
,NULL
);
430 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
431 ok(r
==FALSE
, "should return false\n");
433 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
434 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
435 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
436 filter
|= FILE_NOTIFY_CHANGE_SIZE
;
437 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
438 filter
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
439 filter
|= FILE_NOTIFY_CHANGE_CREATION
;
440 filter
|= FILE_NOTIFY_CHANGE_SECURITY
;
442 SetLastError(0xd0b00b00);
445 memset( buffer
, 0, sizeof buffer
);
447 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,-1,NULL
,&ov
,NULL
);
448 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
449 ok(r
==FALSE
, "should return false\n");
451 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,&ov
,NULL
);
452 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
453 ok(r
==FALSE
, "should return false\n");
455 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,TRUE
,filter
,NULL
,&ov
,NULL
);
456 ok(r
==TRUE
, "should return true\n");
458 r
= WaitForSingleObject( ov
.hEvent
, 10 );
459 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
461 r
= CreateDirectoryW( subdir
, NULL
);
462 ok( r
== TRUE
, "failed to create directory\n");
464 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
465 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
467 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
468 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
470 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
471 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
472 ok( pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n" );
473 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
474 ok( !memcmp(pfni
->FileName
,&szHoo
[1],6), "name wrong\n" );
476 ResetEvent(ov
.hEvent
);
477 SetLastError(0xd0b00b00);
478 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,NULL
,NULL
);
479 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
480 ok(r
==FALSE
, "should return false\n");
482 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,&ov
,NULL
);
483 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
484 ok(r
==FALSE
, "should return false\n");
486 filter
= FILE_NOTIFY_CHANGE_SIZE
;
492 S(U(ov
)).OffsetHigh
= 0;
493 memset( buffer
, 0, sizeof buffer
);
494 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
495 ok(r
==TRUE
, "should return true\n");
497 ok( (NTSTATUS
)ov
.Internal
== STATUS_PENDING
, "ov.Internal wrong\n");
498 ok( ov
.InternalHigh
== 1, "ov.InternalHigh wrong\n");
500 r
= WaitForSingleObject( ov
.hEvent
, 0 );
501 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
503 r
= RemoveDirectoryW( subdir
);
504 ok( r
== TRUE
, "failed to remove directory\n");
506 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
507 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
509 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
510 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
512 if ((NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
)
514 r
= GetOverlappedResult( hdir
, &ov
, &dwCount
, TRUE
);
515 ok( r
== TRUE
, "getoverlappedresult failed\n");
516 ok( dwCount
== 0x12, "count wrong\n");
519 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
520 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
521 ok( pfni
->Action
== FILE_ACTION_REMOVED
, "action wrong\n" );
522 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
523 ok( !memcmp(pfni
->FileName
,&szHoo
[1],6), "name wrong\n" );
525 /* what happens if the buffer is too small? */
526 r
= pReadDirectoryChangesW(hdir
,buffer
,0x10,FALSE
,filter
,NULL
,&ov
,NULL
);
527 ok(r
==TRUE
, "should return true\n");
529 r
= CreateDirectoryW( subdir
, NULL
);
530 ok( r
== TRUE
, "failed to create directory\n");
532 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
533 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
535 ok( (NTSTATUS
)ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
536 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
538 /* test the recursive watch */
539 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
540 ok(r
==TRUE
, "should return true\n");
542 r
= CreateDirectoryW( subsubdir
, NULL
);
543 ok( r
== TRUE
, "failed to create directory\n");
545 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
546 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
548 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
549 ok( ov
.InternalHigh
== 0x18 || ov
.InternalHigh
== 0x12 + 0x18,
550 "ov.InternalHigh wrong %lx\n", ov
.InternalHigh
);
552 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
553 if (pfni
->NextEntryOffset
) /* we may get a modified event on the parent dir */
555 ok( pfni
->NextEntryOffset
== 0x12, "offset wrong %x\n", pfni
->NextEntryOffset
);
556 ok( pfni
->Action
== FILE_ACTION_MODIFIED
, "action wrong %d\n", pfni
->Action
);
557 ok( pfni
->FileNameLength
== 3*sizeof(WCHAR
), "len wrong\n" );
558 ok( !memcmp(pfni
->FileName
,&szGa
[1],3*sizeof(WCHAR
)), "name wrong\n");
559 pfni
= (PFILE_NOTIFY_INFORMATION
)((char *)pfni
+ pfni
->NextEntryOffset
);
561 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
562 ok( pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n" );
563 ok( pfni
->FileNameLength
== 6*sizeof(WCHAR
), "len wrong\n" );
564 ok( !memcmp(pfni
->FileName
,&szGa
[1],6*sizeof(WCHAR
)), "name wrong\n" );
566 r
= RemoveDirectoryW( subsubdir
);
567 ok( r
== TRUE
, "failed to remove directory\n");
571 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
572 ok(r
==TRUE
, "should return true\n");
574 r
= RemoveDirectoryW( subdir
);
575 ok( r
== TRUE
, "failed to remove directory\n");
577 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
578 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
580 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
581 /* we may get a notification for the parent dir too */
582 if (pfni
->Action
== FILE_ACTION_MODIFIED
&& pfni
->NextEntryOffset
)
584 ok( pfni
->FileNameLength
== 3*sizeof(WCHAR
), "len wrong %u\n", pfni
->FileNameLength
);
585 ok( !memcmp(pfni
->FileName
,&szGa
[1],3*sizeof(WCHAR
)), "name wrong\n" );
586 pfni
= (PFILE_NOTIFY_INFORMATION
)((char *)pfni
+ pfni
->NextEntryOffset
);
588 ok( pfni
->NextEntryOffset
== 0, "offset wrong %u\n", pfni
->NextEntryOffset
);
589 ok( pfni
->Action
== FILE_ACTION_REMOVED
, "action wrong %u\n", pfni
->Action
);
590 ok( pfni
->FileNameLength
== 6*sizeof(WCHAR
), "len wrong %u\n", pfni
->FileNameLength
);
591 ok( !memcmp(pfni
->FileName
,&szGa
[1],6*sizeof(WCHAR
)), "name wrong\n" );
593 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
594 dwCount
= (char *)&pfni
->FileName
[pfni
->FileNameLength
/sizeof(WCHAR
)] - buffer
;
595 ok( ov
.InternalHigh
== dwCount
, "ov.InternalHigh wrong %lu/%u\n",ov
.InternalHigh
, dwCount
);
599 r
= RemoveDirectoryW( path
);
600 ok( r
== TRUE
, "failed to remove directory\n");
603 /* show the behaviour when a null buffer is passed */
604 static void test_readdirectorychanges_null(void)
609 DWORD fflags
, filter
= 0;
611 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
];
612 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
613 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
614 PFILE_NOTIFY_INFORMATION pfni
;
616 if (!pReadDirectoryChangesW
)
618 win_skip("ReadDirectoryChangesW is not available\n");
621 SetLastError(0xdeadbeef);
622 r
= GetTempPathW( MAX_PATH
, path
);
623 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
625 win_skip("GetTempPathW is not implemented\n");
628 ok( r
!= 0, "temp path failed\n");
632 lstrcatW( path
, szBoo
);
633 lstrcpyW( subdir
, path
);
634 lstrcatW( subdir
, szHoo
);
636 RemoveDirectoryW( subdir
);
637 RemoveDirectoryW( path
);
639 r
= CreateDirectoryW(path
, NULL
);
640 ok( r
== TRUE
, "failed to create directory\n");
642 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
643 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
644 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
645 OPEN_EXISTING
, fflags
, NULL
);
646 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
648 ov
.hEvent
= CreateEventW( NULL
, 1, 0, NULL
);
650 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
651 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
653 SetLastError(0xd0b00b00);
656 memset( buffer
, 0, sizeof buffer
);
658 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,filter
,NULL
,&ov
,NULL
);
659 ok(r
==TRUE
, "should return true\n");
661 r
= WaitForSingleObject( ov
.hEvent
, 0 );
662 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
664 r
= CreateDirectoryW( subdir
, NULL
);
665 ok( r
== TRUE
, "failed to create directory\n");
667 r
= WaitForSingleObject( ov
.hEvent
, 0 );
668 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
670 ok( (NTSTATUS
)ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
671 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
676 S(U(ov
)).OffsetHigh
= 0;
677 memset( buffer
, 0, sizeof buffer
);
679 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
680 ok(r
==TRUE
, "should return true\n");
682 r
= WaitForSingleObject( ov
.hEvent
, 0 );
683 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
685 r
= RemoveDirectoryW( subdir
);
686 ok( r
== TRUE
, "failed to remove directory\n");
688 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
689 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
691 ok( (NTSTATUS
)ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
692 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
694 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
695 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
699 r
= RemoveDirectoryW( path
);
700 ok( r
== TRUE
, "failed to remove directory\n");
703 static void test_readdirectorychanges_filedir(void)
708 DWORD fflags
, filter
= 0;
710 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
], file
[MAX_PATH
];
711 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
712 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
713 static const WCHAR szFoo
[] = { '\\','f','o','o',0 };
714 PFILE_NOTIFY_INFORMATION pfni
;
716 SetLastError(0xdeadbeef);
717 r
= GetTempPathW( MAX_PATH
, path
);
718 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
720 win_skip("GetTempPathW is not implemented\n");
723 ok( r
!= 0, "temp path failed\n");
727 lstrcatW( path
, szBoo
);
728 lstrcpyW( subdir
, path
);
729 lstrcatW( subdir
, szHoo
);
731 lstrcpyW( file
, path
);
732 lstrcatW( file
, szFoo
);
735 RemoveDirectoryW( subdir
);
736 RemoveDirectoryW( path
);
738 r
= CreateDirectoryW(path
, NULL
);
739 ok( r
== TRUE
, "failed to create directory\n");
741 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
742 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
743 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
744 OPEN_EXISTING
, fflags
, NULL
);
745 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
747 ov
.hEvent
= CreateEventW( NULL
, 0, 0, NULL
);
749 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
751 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,TRUE
,filter
,NULL
,&ov
,NULL
);
752 ok(r
==TRUE
, "should return true\n");
754 r
= WaitForSingleObject( ov
.hEvent
, 10 );
755 ok( r
== WAIT_TIMEOUT
, "should timeout\n" );
757 r
= CreateDirectoryW( subdir
, NULL
);
758 ok( r
== TRUE
, "failed to create directory\n");
760 hfile
= CreateFileW( file
, GENERIC_READ
|GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
761 ok( hfile
!= INVALID_HANDLE_VALUE
, "failed to create file\n");
762 ok( CloseHandle(hfile
), "failed to close file\n");
764 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
765 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
767 ok( (NTSTATUS
)ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
768 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
770 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
771 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
772 ok( pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n" );
773 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
774 ok( !memcmp(pfni
->FileName
,&szFoo
[1],6), "name wrong\n" );
776 r
= DeleteFileW( file
);
777 ok( r
== TRUE
, "failed to delete file\n");
779 r
= RemoveDirectoryW( subdir
);
780 ok( r
== TRUE
, "failed to remove directory\n");
784 r
= RemoveDirectoryW( path
);
785 ok( r
== TRUE
, "failed to remove directory\n");
788 static void CALLBACK
readdirectorychanges_cr(DWORD error
, DWORD len
, LPOVERLAPPED ov
)
790 ok(error
== 0, "ReadDirectoryChangesW error %d\n", error
);
791 ok(ov
->hEvent
== (void*)0xdeadbeef, "hEvent should not have changed\n");
794 static void test_readdirectorychanges_cr(void)
796 static const WCHAR szBoo
[] = { '\\','b','o','o','\\',0 };
797 static const WCHAR szDir
[] = { 'd','i','r',0 };
798 static const WCHAR szFile
[] = { 'f','i','l','e',0 };
799 static const WCHAR szBackslash
[] = { '\\',0 };
801 WCHAR path
[MAX_PATH
], file
[MAX_PATH
], dir
[MAX_PATH
], sub_file
[MAX_PATH
];
802 FILE_NOTIFY_INFORMATION fni
[1024], *fni_next
;
807 if (!pReadDirectoryChangesW
)
809 win_skip("ReadDirectoryChangesW is not available\n");
813 SetLastError(0xdeadbeef);
814 r
= GetTempPathW(MAX_PATH
, path
);
815 if (!r
&& (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
))
817 win_skip("GetTempPathW is not implemented\n");
820 ok(r
!= 0, "temp path failed\n");
824 lstrcatW(path
, szBoo
);
826 lstrcatW(dir
, szDir
);
827 lstrcpyW(file
, path
);
828 lstrcatW(file
, szFile
);
829 lstrcpyW(sub_file
, dir
);
830 lstrcatW(sub_file
, szBackslash
);
831 lstrcatW(sub_file
, szFile
);
834 RemoveDirectoryW(dir
);
835 RemoveDirectoryW(path
);
837 r
= CreateDirectoryW(path
, NULL
);
838 ok(r
== TRUE
, "failed to create directory\n");
840 hdir
= CreateFileW(path
, GENERIC_READ
,
841 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
,
842 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
, NULL
);
843 ok(hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
845 memset(&ov
, 0, sizeof(ov
));
846 ov
.hEvent
= (void*)0xdeadbeef;
847 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
848 FILE_NOTIFY_CHANGE_FILE_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
849 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
851 hfile
= CreateFileW(file
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
852 ok(hfile
!= INVALID_HANDLE_VALUE
, "failed to create file\n");
855 r
= SleepEx(1000, TRUE
);
856 ok(r
!= 0, "failed to receive file creation event\n");
857 ok(fni
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
858 ok(fni
->Action
== FILE_ACTION_ADDED
, "Action = %d\n", fni
->Action
);
859 ok(fni
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
860 "FileNameLength = %d\n", fni
->FileNameLength
);
861 ok(!memcmp(fni
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
862 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
864 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
865 FILE_NOTIFY_CHANGE_FILE_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
866 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
868 /* This event will not be reported */
869 r
= CreateDirectoryW(dir
, NULL
);
870 ok(r
== TRUE
, "failed to create directory\n");
872 r
= MoveFileW(file
, sub_file
);
873 ok(r
== TRUE
, "failed to move file\n");
875 r
= SleepEx(1000, TRUE
);
876 ok(r
!= 0, "failed to receive file move event\n");
877 ok(fni
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
878 ok(fni
->Action
== FILE_ACTION_REMOVED
, "Action = %d\n", fni
->Action
);
879 ok(fni
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
880 "FileNameLength = %d\n", fni
->FileNameLength
);
881 ok(!memcmp(fni
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
882 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
884 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
885 FILE_NOTIFY_CHANGE_FILE_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
886 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
888 r
= MoveFileW(sub_file
, file
);
889 ok(r
== TRUE
, "failed to move file\n");
891 r
= SleepEx(1000, TRUE
);
892 ok(r
!= 0, "failed to receive file move event\n");
893 ok(fni
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
894 ok(fni
->Action
== FILE_ACTION_ADDED
, "Action = %d\n", fni
->Action
);
895 ok(fni
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
896 "FileNameLength = %d\n", fni
->FileNameLength
);
897 ok(!memcmp(fni
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
898 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
900 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
901 FILE_NOTIFY_CHANGE_FILE_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
902 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
904 r
= DeleteFileW(file
);
905 ok(r
== TRUE
, "failed to delete file\n");
907 r
= SleepEx(1000, TRUE
);
908 ok(r
!= 0, "failed to receive file removal event\n");
909 ok(fni
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
910 ok(fni
->Action
== FILE_ACTION_REMOVED
, "Action = %d\n", fni
->Action
);
911 ok(fni
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
912 "FileNameLength = %d\n", fni
->FileNameLength
);
913 ok(!memcmp(fni
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
914 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
918 hdir
= CreateFileW(path
, GENERIC_READ
,
919 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
,
920 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
, NULL
);
921 ok(hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
923 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
924 FILE_NOTIFY_CHANGE_DIR_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
925 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
927 r
= MoveFileW(dir
, file
);
928 ok(r
== TRUE
, "failed to move directory\n");
930 r
= SleepEx(1000, TRUE
);
931 ok(r
!= 0, "failed to receive directory move event\n");
932 if (fni
->Action
== FILE_ACTION_RENAMED_OLD_NAME
)
934 ok(fni
->Action
== FILE_ACTION_RENAMED_OLD_NAME
, "Action = %d\n", fni
->Action
);
935 ok(fni
->FileNameLength
== lstrlenW(szDir
)*sizeof(WCHAR
),
936 "FileNameLength = %d\n", fni
->FileNameLength
);
937 ok(!memcmp(fni
->FileName
, szDir
, lstrlenW(szDir
)*sizeof(WCHAR
)),
938 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
939 ok(fni
->NextEntryOffset
!= 0, "no next entry in movement event\n");
940 fni_next
= (FILE_NOTIFY_INFORMATION
*)((char*)fni
+fni
->NextEntryOffset
);
941 ok(fni_next
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
942 ok(fni_next
->Action
== FILE_ACTION_RENAMED_NEW_NAME
, "Action = %d\n", fni_next
->Action
);
943 ok(fni_next
->FileNameLength
== lstrlenW(szFile
)*sizeof(WCHAR
),
944 "FileNameLength = %d\n", fni_next
->FileNameLength
);
945 ok(!memcmp(fni_next
->FileName
, szFile
, lstrlenW(szFile
)*sizeof(WCHAR
)),
946 "FileName = %s\n", wine_dbgstr_wn(fni_next
->FileName
, fni_next
->FileNameLength
/sizeof(WCHAR
)));
950 todo_wine
ok(0, "Expected rename event\n");
952 if (fni
->NextEntryOffset
== 0)
954 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
955 FILE_NOTIFY_CHANGE_DIR_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
956 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
958 r
= SleepEx(1000, TRUE
);
959 ok(r
!= 0, "failed to receive directory move event\n");
963 r
= CreateDirectoryW(dir
, NULL
);
964 ok(r
== TRUE
, "failed to create directory\n");
966 r
= RemoveDirectoryW(dir
);
967 ok(r
== TRUE
, "failed to remove directory\n");
969 r
= pReadDirectoryChangesW(hdir
, fni
, sizeof(fni
), FALSE
,
970 FILE_NOTIFY_CHANGE_DIR_NAME
, NULL
, &ov
, readdirectorychanges_cr
);
971 ok(r
== TRUE
, "pReadDirectoryChangesW failed\n");
973 r
= SleepEx(1000, TRUE
);
974 ok(r
!= 0, "failed to receive directory creation event\n");
975 ok(fni
->Action
== FILE_ACTION_ADDED
, "Action = %d\n", fni
->Action
);
976 ok(fni
->FileNameLength
== lstrlenW(szDir
)*sizeof(WCHAR
),
977 "FileNameLength = %d\n", fni
->FileNameLength
);
978 ok(!memcmp(fni
->FileName
, szDir
, lstrlenW(szDir
)*sizeof(WCHAR
)),
979 "FileName = %s\n", wine_dbgstr_wn(fni
->FileName
, fni
->FileNameLength
/sizeof(WCHAR
)));
980 if (fni
->NextEntryOffset
)
981 fni_next
= (FILE_NOTIFY_INFORMATION
*)((char*)fni
+fni
->NextEntryOffset
);
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
= SleepEx(1000, TRUE
);
989 ok(r
!= 0, "failed to receive directory removal event\n");
992 ok(fni_next
->NextEntryOffset
== 0, "there should be no more events in buffer\n");
993 ok(fni_next
->Action
== FILE_ACTION_REMOVED
, "Action = %d\n", fni_next
->Action
);
994 ok(fni_next
->FileNameLength
== lstrlenW(szDir
)*sizeof(WCHAR
),
995 "FileNameLength = %d\n", fni_next
->FileNameLength
);
996 ok(!memcmp(fni_next
->FileName
, szDir
, lstrlenW(szDir
)*sizeof(WCHAR
)),
997 "FileName = %s\n", wine_dbgstr_wn(fni_next
->FileName
, fni_next
->FileNameLength
/sizeof(WCHAR
)));
1000 RemoveDirectoryW(file
);
1001 RemoveDirectoryW(path
);
1004 static void test_ffcn_directory_overlap(void)
1006 HANDLE parent_watch
, child_watch
, parent_thread
, child_thread
;
1007 char workdir
[MAX_PATH
], parentdir
[MAX_PATH
], childdir
[MAX_PATH
];
1008 char tempfile
[MAX_PATH
];
1012 /* Setup directory hierarchy */
1013 ret
= GetTempPathA(MAX_PATH
, workdir
);
1014 ok((ret
> 0) && (ret
<= MAX_PATH
),
1015 "GetTempPathA error: %d\n", GetLastError());
1017 ret
= GetTempFileNameA(workdir
, "fcn", 0, tempfile
);
1018 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
1019 ret
= DeleteFileA(tempfile
);
1020 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
1022 lstrcpyA(parentdir
, tempfile
);
1023 ret
= CreateDirectoryA(parentdir
, NULL
);
1024 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
1026 lstrcpyA(childdir
, parentdir
);
1027 lstrcatA(childdir
, "\\c");
1028 ret
= CreateDirectoryA(childdir
, NULL
);
1029 ok(ret
, "CreateDirectoryA error: %d\n", GetLastError());
1032 /* When recursively watching overlapping directories, changes in child
1033 * should trigger notifications for both child and parent */
1034 parent_thread
= StartNotificationThread(parentdir
, TRUE
,
1035 FILE_NOTIFY_CHANGE_FILE_NAME
);
1036 child_thread
= StartNotificationThread(childdir
, TRUE
,
1037 FILE_NOTIFY_CHANGE_FILE_NAME
);
1039 /* Create a file in child */
1040 ret
= GetTempFileNameA(childdir
, "fcn", 0, tempfile
);
1041 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
1043 /* Both watches should trigger */
1044 ret
= FinishNotificationThread(parent_thread
);
1045 ok(ret
, "Missed parent notification\n");
1046 ret
= FinishNotificationThread(child_thread
);
1047 ok(ret
, "Missed child notification\n");
1049 ret
= DeleteFileA(tempfile
);
1050 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
1053 /* Removing a recursive parent watch should not affect child watches. Doing
1054 * so used to crash wineserver. */
1055 parent_watch
= FindFirstChangeNotificationA(parentdir
, TRUE
,
1056 FILE_NOTIFY_CHANGE_FILE_NAME
);
1057 ok(parent_watch
!= INVALID_HANDLE_VALUE
,
1058 "FindFirstChangeNotification error: %d\n", GetLastError());
1059 child_watch
= FindFirstChangeNotificationA(childdir
, TRUE
,
1060 FILE_NOTIFY_CHANGE_FILE_NAME
);
1061 ok(child_watch
!= INVALID_HANDLE_VALUE
,
1062 "FindFirstChangeNotification error: %d\n", GetLastError());
1064 ret
= FindCloseChangeNotification(parent_watch
);
1065 ok(ret
, "FindCloseChangeNotification error: %d\n", GetLastError());
1067 child_thread
= CreateThread(NULL
, 0, NotificationThread
, child_watch
, 0,
1069 ok(child_thread
!= NULL
, "CreateThread error: %d\n", GetLastError());
1071 /* Create a file in child */
1072 ret
= GetTempFileNameA(childdir
, "fcn", 0, tempfile
);
1073 ok(ret
, "GetTempFileNameA error: %d\n", GetLastError());
1075 /* Child watch should trigger */
1076 ret
= FinishNotificationThread(child_thread
);
1077 ok(ret
, "Missed child notification\n");
1080 ret
= DeleteFileA(tempfile
);
1081 ok(ret
, "DeleteFileA error: %d\n", GetLastError());
1083 ret
= RemoveDirectoryA(childdir
);
1084 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
1086 ret
= RemoveDirectoryA(parentdir
);
1087 ok(ret
, "RemoveDirectoryA error: %d\n", GetLastError());
1092 HMODULE hkernel32
= GetModuleHandleA("kernel32.dll");
1093 pReadDirectoryChangesW
= (void *)GetProcAddress(hkernel32
, "ReadDirectoryChangesW");
1095 test_ffcnMultipleThreads();
1096 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
1097 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
1099 test_FindFirstChangeNotification();
1101 test_readdirectorychanges();
1102 test_readdirectorychanges_null();
1103 test_readdirectorychanges_filedir();
1104 test_readdirectorychanges_cr();
1105 test_ffcn_directory_overlap();