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
)
41 HANDLE change
= (HANDLE
) arg
;
45 status
= WaitForSingleObject(change
, 100);
47 if (status
== WAIT_OBJECT_0
) {
48 ret
= FindNextChangeNotification(change
);
51 ret
= FindCloseChangeNotification(change
);
52 ok( ret
, "FindCloseChangeNotification error: %ld\n",
55 ExitThread((DWORD
)ret
);
58 static HANDLE
StartNotificationThread(LPCSTR path
, BOOL subtree
, DWORD flags
)
60 HANDLE change
, thread
;
63 change
= FindFirstChangeNotificationA(path
, subtree
, flags
);
64 ok(change
!= INVALID_HANDLE_VALUE
, "FindFirstChangeNotification error: %ld\n", GetLastError());
66 thread
= CreateThread(NULL
, 0, NotificationThread
, (LPVOID
)change
,
68 ok(thread
!= INVALID_HANDLE_VALUE
, "CreateThread error: %ld\n", GetLastError());
73 static DWORD
FinishNotificationThread(HANDLE thread
)
75 DWORD status
, exitcode
;
77 status
= WaitForSingleObject(thread
, 5000);
78 ok(status
== WAIT_OBJECT_0
, "WaitForSingleObject status %ld error %ld\n", status
, GetLastError());
80 ok(GetExitCodeThread(thread
, &exitcode
), "Could not retrieve thread exit code\n");
85 static void test_FindFirstChangeNotification(void)
87 HANDLE change
, file
, thread
;
88 DWORD attributes
, count
;
91 char workdir
[MAX_PATH
], dirname1
[MAX_PATH
], dirname2
[MAX_PATH
];
92 char filename1
[MAX_PATH
], filename2
[MAX_PATH
];
93 static const char prefix
[] = "FCN";
98 change
= FindFirstChangeNotificationA("not-a-file", FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
99 ok(change
== INVALID_HANDLE_VALUE
&& GetLastError() == ERROR_FILE_NOT_FOUND
,
100 "FindFirstChangeNotification error: %ld\n", GetLastError());
102 if (0) /* This documents win2k behavior. It crashes on win98. */
104 change
= FindFirstChangeNotificationA(NULL
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
105 ok(change
== NULL
&& GetLastError() == ERROR_PATH_NOT_FOUND
,
106 "FindFirstChangeNotification error: %ld\n", GetLastError());
109 ret
= FindNextChangeNotification(NULL
);
110 ok(!ret
&& GetLastError() == ERROR_INVALID_HANDLE
, "FindNextChangeNotification error: %ld\n",
113 ret
= FindCloseChangeNotification(NULL
);
114 ok(!ret
&& GetLastError() == ERROR_INVALID_HANDLE
, "FindCloseChangeNotification error: %ld\n",
117 ret
= GetTempPathA(MAX_PATH
, workdir
);
118 ok(ret
, "GetTempPathA error: %ld\n", GetLastError());
120 lstrcatA(workdir
, "testFileChangeNotification");
122 ret
= CreateDirectoryA(workdir
, NULL
);
123 ok(ret
, "CreateDirectoryA error: %ld\n", GetLastError());
125 ret
= GetTempFileNameA(workdir
, prefix
, 0, filename1
);
126 ok(ret
, "GetTempFileNameA error: %ld\n", GetLastError());
128 file
= CreateFileA(filename1
, GENERIC_WRITE
|GENERIC_READ
, 0, NULL
, CREATE_ALWAYS
,
129 FILE_ATTRIBUTE_NORMAL
, 0);
130 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %ld\n", GetLastError());
131 ret
= CloseHandle(file
);
132 ok( ret
, "CloseHandle error: %ld\n", GetLastError());
134 /* Try to register notification for a file. win98 and win2k behave differently here */
135 change
= FindFirstChangeNotificationA(filename1
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
136 ok(change
== INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_DIRECTORY
||
137 GetLastError() == ERROR_FILE_NOT_FOUND
),
138 "FindFirstChangeNotification error: %ld\n", GetLastError());
140 lstrcpyA(dirname1
, filename1
);
141 lstrcatA(dirname1
, "dir");
143 lstrcpyA(dirname2
, dirname1
);
144 lstrcatA(dirname2
, "new");
146 ret
= CreateDirectoryA(dirname1
, NULL
);
147 ok(ret
, "CreateDirectoryA error: %ld\n", GetLastError());
149 /* What if we move the directory we registered notification for? */
150 thread
= StartNotificationThread(dirname1
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
151 ret
= MoveFileA(dirname1
, dirname2
);
152 ok(ret
, "MoveFileA error: %ld\n", GetLastError());
153 ok(FinishNotificationThread(thread
), "Missed notification\n");
155 /* What if we remove the directory we registered notification for? */
156 thread
= StartNotificationThread(dirname2
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
157 ret
= RemoveDirectoryA(dirname2
);
158 ok(ret
, "RemoveDirectoryA error: %ld\n", GetLastError());
160 /* win98 and win2k behave differently here */
161 ret
= FinishNotificationThread(thread
);
162 ok(ret
|| !ret
, "You'll never read this\n");
164 /* functional checks */
166 /* Create a directory */
167 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
168 ret
= CreateDirectoryA(dirname1
, NULL
);
169 ok(ret
, "CreateDirectoryA error: %ld\n", GetLastError());
170 ok(FinishNotificationThread(thread
), "Missed notification\n");
172 /* Rename a directory */
173 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
174 ret
= MoveFileA(dirname1
, dirname2
);
175 ok(ret
, "MoveFileA error: %ld\n", GetLastError());
176 ok(FinishNotificationThread(thread
), "Missed notification\n");
178 /* Delete a directory */
179 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_DIR_NAME
);
180 ret
= RemoveDirectoryA(dirname2
);
181 ok(ret
, "RemoveDirectoryA error: %ld\n", GetLastError());
182 ok(FinishNotificationThread(thread
), "Missed notification\n");
184 lstrcpyA(filename2
, filename1
);
185 lstrcatA(filename2
, "new");
188 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
189 ret
= MoveFileA(filename1
, filename2
);
190 ok(ret
, "MoveFileA error: %ld\n", GetLastError());
191 ok(FinishNotificationThread(thread
), "Missed notification\n");
194 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
195 ret
= DeleteFileA(filename2
);
196 ok(ret
, "DeleteFileA error: %ld\n", GetLastError());
197 ok(FinishNotificationThread(thread
), "Missed notification\n");
200 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_FILE_NAME
);
201 file
= CreateFileA(filename2
, GENERIC_WRITE
|GENERIC_READ
, 0, NULL
, CREATE_ALWAYS
,
202 FILE_ATTRIBUTE_NORMAL
, 0);
203 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %ld\n", GetLastError());
204 ret
= CloseHandle(file
);
205 ok( ret
, "CloseHandle error: %ld\n", GetLastError());
206 ok(FinishNotificationThread(thread
), "Missed notification\n");
208 attributes
= GetFileAttributesA(filename2
);
209 ok(attributes
!= INVALID_FILE_ATTRIBUTES
, "GetFileAttributesA error: %ld\n", GetLastError());
210 attributes
&= FILE_ATTRIBUTE_READONLY
;
212 /* Change file attributes */
213 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_ATTRIBUTES
);
214 ret
= SetFileAttributesA(filename2
, attributes
);
215 ok(ret
, "SetFileAttributesA error: %ld\n", GetLastError());
216 ok(FinishNotificationThread(thread
), "Missed notification\n");
218 /* Change last write time by writing to a file */
219 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_LAST_WRITE
);
220 file
= CreateFileA(filename2
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
221 FILE_ATTRIBUTE_NORMAL
, 0);
222 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %ld\n", GetLastError());
223 ret
= WriteFile(file
, buffer
, sizeof(buffer
), &count
, NULL
);
224 ok(ret
&& count
== sizeof(buffer
), "WriteFile error: %ld\n", GetLastError());
225 ret
= CloseHandle(file
);
226 ok( ret
, "CloseHandle error: %ld\n", GetLastError());
227 ok(FinishNotificationThread(thread
), "Missed notification\n");
229 /* Change file size by truncating a file */
230 thread
= StartNotificationThread(workdir
, FALSE
, FILE_NOTIFY_CHANGE_SIZE
);
231 file
= CreateFileA(filename2
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
232 FILE_ATTRIBUTE_NORMAL
, 0);
233 ok(file
!= INVALID_HANDLE_VALUE
, "CreateFileA error: %ld\n", GetLastError());
234 ret
= WriteFile(file
, buffer
, sizeof(buffer
) / 2, &count
, NULL
);
235 ok(ret
&& count
== sizeof(buffer
) / 2, "WriteFileA error: %ld\n", GetLastError());
236 ret
= CloseHandle(file
);
237 ok( ret
, "CloseHandle error: %ld\n", GetLastError());
238 ok(FinishNotificationThread(thread
), "Missed notification\n");
242 ret
= DeleteFileA(filename2
);
243 ok(ret
, "DeleteFileA error: %ld\n", GetLastError());
245 ret
= RemoveDirectoryA(workdir
);
246 ok(ret
, "RemoveDirectoryA error: %ld\n", GetLastError());
249 /* this test concentrates more on the wait behaviour of the handle */
250 static void test_ffcn(void)
255 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
];
256 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
257 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
259 r
= GetTempPathW( MAX_PATH
, path
);
260 ok( r
!= 0, "temp path failed\n");
264 lstrcatW( path
, szBoo
);
265 lstrcpyW( subdir
, path
);
266 lstrcatW( subdir
, szHoo
);
268 RemoveDirectoryW( subdir
);
269 RemoveDirectoryW( path
);
271 r
= CreateDirectoryW(path
, NULL
);
272 ok( r
== TRUE
, "failed to create directory\n");
274 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
275 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
277 handle
= FindFirstChangeNotificationW( path
, 1, filter
);
278 ok( handle
!= INVALID_HANDLE_VALUE
, "invalid handle\n");
280 r
= WaitForSingleObject( handle
, 0 );
281 ok( r
== STATUS_TIMEOUT
, "should time out\n");
283 r
= CreateDirectoryW( subdir
, NULL
);
284 ok( r
== TRUE
, "failed to create subdir\n");
286 r
= WaitForSingleObject( handle
, 0 );
287 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
289 r
= WaitForSingleObject( handle
, 0 );
290 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
292 r
= FindNextChangeNotification(handle
);
293 ok( r
== TRUE
, "find next failed\n");
295 r
= WaitForSingleObject( handle
, 0 );
296 ok( r
== STATUS_TIMEOUT
, "should time out\n");
298 r
= RemoveDirectoryW( subdir
);
299 ok( r
== TRUE
, "failed to remove subdir\n");
301 r
= WaitForSingleObject( handle
, 0 );
302 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
304 r
= WaitForSingleObject( handle
, 0 );
305 ok( r
== WAIT_OBJECT_0
, "should be ready\n");
307 r
= FindNextChangeNotification(handle
);
308 ok( r
== TRUE
, "find next failed\n");
310 r
= FindNextChangeNotification(handle
);
311 ok( r
== TRUE
, "find next failed\n");
313 r
= FindCloseChangeNotification(handle
);
314 ok( r
== TRUE
, "should succeed\n");
316 r
= RemoveDirectoryW( path
);
317 ok( r
== TRUE
, "failed to remove dir\n");
320 typedef BOOL (WINAPI
*fnReadDirectoryChangesW
)(HANDLE
,LPVOID
,DWORD
,BOOL
,DWORD
,
321 LPDWORD
,LPOVERLAPPED
,LPOVERLAPPED_COMPLETION_ROUTINE
);
322 fnReadDirectoryChangesW pReadDirectoryChangesW
;
324 static void test_readdirectorychanges(void)
328 DWORD fflags
, filter
= 0, r
, dwCount
;
330 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
], subsubdir
[MAX_PATH
];
331 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
332 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
333 static const WCHAR szGa
[] = { '\\','h','o','o','\\','g','a',0 };
334 PFILE_NOTIFY_INFORMATION pfni
;
336 if (!pReadDirectoryChangesW
)
339 r
= GetTempPathW( MAX_PATH
, path
);
340 ok( r
!= 0, "temp path failed\n");
344 lstrcatW( path
, szBoo
);
345 lstrcpyW( subdir
, path
);
346 lstrcatW( subdir
, szHoo
);
348 lstrcpyW( subsubdir
, path
);
349 lstrcatW( subsubdir
, szGa
);
351 RemoveDirectoryW( subsubdir
);
352 RemoveDirectoryW( subdir
);
353 RemoveDirectoryW( path
);
355 r
= CreateDirectoryW(path
, NULL
);
356 ok( r
== TRUE
, "failed to create directory\n");
358 SetLastError(0xd0b00b00);
359 r
= pReadDirectoryChangesW(NULL
,NULL
,0,FALSE
,0,NULL
,NULL
,NULL
);
360 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
361 ok(r
==FALSE
, "should return false\n");
363 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
364 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
365 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
366 OPEN_EXISTING
, fflags
, NULL
);
367 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
369 ov
.hEvent
= CreateEvent( NULL
, 1, 0, NULL
);
371 SetLastError(0xd0b00b00);
372 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,0,NULL
,NULL
,NULL
);
373 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
374 ok(r
==FALSE
, "should return false\n");
376 SetLastError(0xd0b00b00);
377 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,0,NULL
,&ov
,NULL
);
378 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
379 ok(r
==FALSE
, "should return false\n");
381 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
382 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
383 filter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
384 filter
|= FILE_NOTIFY_CHANGE_SIZE
;
385 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
386 filter
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
387 filter
|= FILE_NOTIFY_CHANGE_CREATION
;
388 filter
|= FILE_NOTIFY_CHANGE_SECURITY
;
390 SetLastError(0xd0b00b00);
393 memset( buffer
, 0, sizeof buffer
);
395 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,-1,NULL
,&ov
,NULL
);
396 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
397 ok(r
==FALSE
, "should return false\n");
399 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,&ov
,NULL
);
400 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
401 ok(r
==FALSE
, "should return false\n");
403 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,TRUE
,filter
,NULL
,&ov
,NULL
);
404 ok(r
==TRUE
, "should return true\n");
406 r
= WaitForSingleObject( ov
.hEvent
, 10 );
407 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
409 r
= CreateDirectoryW( subdir
, NULL
);
410 ok( r
== TRUE
, "failed to create directory\n");
412 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
413 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
415 ok( ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
416 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
418 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
419 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
420 ok( pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n" );
421 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
422 ok( !memcmp(pfni
->FileName
,&szHoo
[1],6), "name wrong\n" );
424 ResetEvent(ov
.hEvent
);
425 SetLastError(0xd0b00b00);
426 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,NULL
,NULL
);
427 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
428 ok(r
==FALSE
, "should return false\n");
430 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,0,NULL
,&ov
,NULL
);
431 ok(GetLastError()==ERROR_INVALID_PARAMETER
,"last error wrong\n");
432 ok(r
==FALSE
, "should return false\n");
434 filter
= FILE_NOTIFY_CHANGE_SIZE
;
440 S(U(ov
)).OffsetHigh
= 0;
441 memset( buffer
, 0, sizeof buffer
);
442 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
443 ok(r
==TRUE
, "should return true\n");
445 ok( ov
.Internal
== STATUS_PENDING
, "ov.Internal wrong\n");
446 ok( ov
.InternalHigh
== 1, "ov.InternalHigh wrong\n");
448 r
= WaitForSingleObject( ov
.hEvent
, 0 );
449 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
451 r
= RemoveDirectoryW( subdir
);
452 ok( r
== TRUE
, "failed to remove directory\n");
454 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
455 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
457 ok( ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
458 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
460 r
= GetOverlappedResult( hdir
, &ov
, &dwCount
, TRUE
);
461 ok( r
== TRUE
, "getoverlappedresult failed\n");
462 ok( dwCount
== 0x12, "count wrong\n");
464 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
465 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
466 ok( pfni
->Action
== FILE_ACTION_REMOVED
, "action wrong\n" );
467 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
468 ok( !memcmp(pfni
->FileName
,&szHoo
[1],6), "name wrong\n" );
470 /* what happens if the buffer is too small? */
471 r
= pReadDirectoryChangesW(hdir
,buffer
,0x10,FALSE
,filter
,NULL
,&ov
,NULL
);
472 ok(r
==TRUE
, "should return true\n");
474 r
= CreateDirectoryW( subdir
, NULL
);
475 ok( r
== TRUE
, "failed to create directory\n");
477 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
478 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
480 ok( ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
481 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
483 /* test the recursive watch */
484 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
485 ok(r
==TRUE
, "should return true\n");
487 r
= CreateDirectoryW( subsubdir
, NULL
);
488 ok( r
== TRUE
, "failed to create directory\n");
490 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
491 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
493 ok( ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
494 ok( ov
.InternalHigh
== 0x18, "ov.InternalHigh wrong\n");
496 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
497 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
498 ok( pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n" );
499 ok( pfni
->FileNameLength
== 0x0c, "len wrong\n" );
500 ok( !memcmp(pfni
->FileName
,&szGa
[1],6), "name wrong\n" );
502 r
= RemoveDirectoryW( subsubdir
);
503 ok( r
== TRUE
, "failed to remove directory\n");
507 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
508 ok(r
==TRUE
, "should return true\n");
510 r
= RemoveDirectoryW( subdir
);
511 ok( r
== TRUE
, "failed to remove directory\n");
513 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
514 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
516 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
517 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
518 ok( pfni
->Action
== FILE_ACTION_REMOVED
, "action wrong\n" );
519 ok( pfni
->FileNameLength
== 0x0c, "len wrong\n" );
520 ok( !memcmp(pfni
->FileName
,&szGa
[1],6), "name wrong\n" );
522 ok( ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
523 ok( ov
.InternalHigh
== 0x18, "ov.InternalHigh wrong\n");
527 r
= RemoveDirectoryW( path
);
528 ok( r
== TRUE
, "failed to remove directory\n");
531 /* show the behaviour when a null buffer is passed */
532 static void test_readdirectorychanges_null(void)
537 DWORD fflags
, filter
= 0;
539 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
];
540 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
541 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
542 PFILE_NOTIFY_INFORMATION pfni
;
544 if (!pReadDirectoryChangesW
)
547 r
= GetTempPathW( MAX_PATH
, path
);
548 ok( r
!= 0, "temp path failed\n");
552 lstrcatW( path
, szBoo
);
553 lstrcpyW( subdir
, path
);
554 lstrcatW( subdir
, szHoo
);
556 RemoveDirectoryW( subdir
);
557 RemoveDirectoryW( path
);
559 r
= CreateDirectoryW(path
, NULL
);
560 ok( r
== TRUE
, "failed to create directory\n");
562 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
563 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
564 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
565 OPEN_EXISTING
, fflags
, NULL
);
566 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
568 ov
.hEvent
= CreateEvent( NULL
, 1, 0, NULL
);
570 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
571 filter
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
573 SetLastError(0xd0b00b00);
576 memset( buffer
, 0, sizeof buffer
);
578 r
= pReadDirectoryChangesW(hdir
,NULL
,0,FALSE
,filter
,NULL
,&ov
,NULL
);
579 ok(r
==TRUE
, "should return true\n");
581 r
= WaitForSingleObject( ov
.hEvent
, 0 );
582 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
584 r
= CreateDirectoryW( subdir
, NULL
);
585 ok( r
== TRUE
, "failed to create directory\n");
587 r
= WaitForSingleObject( ov
.hEvent
, 0 );
588 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
590 ok( ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
591 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
596 S(U(ov
)).OffsetHigh
= 0;
597 memset( buffer
, 0, sizeof buffer
);
599 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,FALSE
,filter
,NULL
,&ov
,NULL
);
600 ok(r
==TRUE
, "should return true\n");
602 r
= WaitForSingleObject( ov
.hEvent
, 0 );
603 ok( r
== STATUS_TIMEOUT
, "should timeout\n" );
605 r
= RemoveDirectoryW( subdir
);
606 ok( r
== TRUE
, "failed to remove directory\n");
608 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
609 ok( r
== WAIT_OBJECT_0
, "should be ready\n" );
611 ok( ov
.Internal
== STATUS_NOTIFY_ENUM_DIR
, "ov.Internal wrong\n");
612 ok( ov
.InternalHigh
== 0, "ov.InternalHigh wrong\n");
614 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
615 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
619 r
= RemoveDirectoryW( path
);
620 ok( r
== TRUE
, "failed to remove directory\n");
623 static void test_readdirectorychanges_filedir(void)
628 DWORD fflags
, filter
= 0;
630 WCHAR path
[MAX_PATH
], subdir
[MAX_PATH
], file
[MAX_PATH
];
631 static const WCHAR szBoo
[] = { '\\','b','o','o',0 };
632 static const WCHAR szHoo
[] = { '\\','h','o','o',0 };
633 static const WCHAR szFoo
[] = { '\\','f','o','o',0 };
634 PFILE_NOTIFY_INFORMATION pfni
;
636 r
= GetTempPathW( MAX_PATH
, path
);
637 ok( r
!= 0, "temp path failed\n");
641 lstrcatW( path
, szBoo
);
642 lstrcpyW( subdir
, path
);
643 lstrcatW( subdir
, szHoo
);
645 lstrcpyW( file
, path
);
646 lstrcatW( file
, szFoo
);
649 RemoveDirectoryW( subdir
);
650 RemoveDirectoryW( path
);
652 r
= CreateDirectoryW(path
, NULL
);
653 ok( r
== TRUE
, "failed to create directory\n");
655 fflags
= FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
;
656 hdir
= CreateFileW(path
, GENERIC_READ
|SYNCHRONIZE
|FILE_LIST_DIRECTORY
,
657 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
658 OPEN_EXISTING
, fflags
, NULL
);
659 ok( hdir
!= INVALID_HANDLE_VALUE
, "failed to open directory\n");
661 ov
.hEvent
= CreateEvent( NULL
, 0, 0, NULL
);
663 filter
= FILE_NOTIFY_CHANGE_FILE_NAME
;
665 r
= pReadDirectoryChangesW(hdir
,buffer
,sizeof buffer
,TRUE
,filter
,NULL
,&ov
,NULL
);
666 ok(r
==TRUE
, "should return true\n");
668 r
= WaitForSingleObject( ov
.hEvent
, 10 );
669 ok( r
== WAIT_TIMEOUT
, "should timeout\n" );
671 r
= CreateDirectoryW( subdir
, NULL
);
672 ok( r
== TRUE
, "failed to create directory\n");
674 hfile
= CreateFileW( file
, GENERIC_READ
|GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
675 ok( hfile
!= INVALID_HANDLE_VALUE
, "failed to create file\n");
676 ok( CloseHandle(hfile
), "failed toc lose file\n");
678 r
= WaitForSingleObject( ov
.hEvent
, 1000 );
679 ok( r
== WAIT_OBJECT_0
, "event should be ready\n" );
681 ok( ov
.Internal
== STATUS_SUCCESS
, "ov.Internal wrong\n");
682 ok( ov
.InternalHigh
== 0x12, "ov.InternalHigh wrong\n");
684 pfni
= (PFILE_NOTIFY_INFORMATION
) buffer
;
685 ok( pfni
->NextEntryOffset
== 0, "offset wrong\n" );
686 ok( pfni
->Action
== FILE_ACTION_ADDED
, "action wrong\n" );
687 ok( pfni
->FileNameLength
== 6, "len wrong\n" );
688 ok( !memcmp(pfni
->FileName
,&szFoo
[1],6), "name wrong\n" );
690 r
= DeleteFileW( file
);
691 ok( r
== TRUE
, "failed to delete file\n");
693 r
= RemoveDirectoryW( subdir
);
694 ok( r
== TRUE
, "failed to remove directory\n");
698 r
= RemoveDirectoryW( path
);
699 ok( r
== TRUE
, "failed to remove directory\n");
704 HMODULE hkernel32
= GetModuleHandle("kernel32");
705 pReadDirectoryChangesW
= (fnReadDirectoryChangesW
)
706 GetProcAddress(hkernel32
, "ReadDirectoryChangesW");
708 test_FindFirstChangeNotification();
710 test_readdirectorychanges();
711 test_readdirectorychanges_null();
712 test_readdirectorychanges_filedir();