~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/kernel32/tests/change.c

Version: ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2  * Tests for file change notification functions
  3  *
  4  * Copyright (c) 2004 Hans Leidekker
  5  * Copyright 2006 Mike McCormack for CodeWeavers
  6  *
  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.
 11  *
 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.
 16  *
 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
 20  */
 21 
 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
 27  */
 28 
 29 #include <stdarg.h>
 30 #include <stdio.h>
 31 
 32 #include "ntstatus.h"
 33 #define WIN32_NO_STATUS
 34 #include "wine/test.h"
 35 #include <windef.h>
 36 #include <winbase.h>
 37 #include <winternl.h>
 38 
 39 static DWORD CALLBACK NotificationThread(LPVOID arg)
 40 {
 41     HANDLE change = (HANDLE) arg;
 42     BOOL notified = FALSE;
 43     BOOL ret = FALSE;
 44     DWORD status;
 45 
 46     status = WaitForSingleObject(change, 100);
 47 
 48     if (status == WAIT_OBJECT_0 ) {
 49         notified = TRUE;
 50         ret = FindNextChangeNotification(change);
 51     }
 52 
 53     ret = FindCloseChangeNotification(change);
 54     ok( ret, "FindCloseChangeNotification error: %d\n",
 55        GetLastError());
 56 
 57     ExitThread((DWORD)notified);
 58 }
 59 
 60 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
 61 {
 62     HANDLE change, thread;
 63     DWORD threadId;
 64 
 65     change = FindFirstChangeNotificationA(path, subtree, flags);
 66     ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
 67 
 68     thread = CreateThread(NULL, 0, NotificationThread, (LPVOID)change,
 69                           0, &threadId);
 70     ok(thread != NULL, "CreateThread error: %d\n", GetLastError());
 71 
 72     return thread;
 73 }
 74 
 75 static DWORD FinishNotificationThread(HANDLE thread)
 76 {
 77     DWORD status, exitcode;
 78 
 79     status = WaitForSingleObject(thread, 5000);
 80     ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\n", status, GetLastError());
 81 
 82     ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
 83 
 84     return exitcode;
 85 }
 86 
 87 static void test_FindFirstChangeNotification(void)
 88 {
 89     HANDLE change, file, thread;
 90     DWORD attributes, count;
 91     BOOL ret;
 92 
 93     char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
 94     char filename1[MAX_PATH], filename2[MAX_PATH];
 95     static const char prefix[] = "FCN";
 96     char buffer[2048];
 97 
 98     /* pathetic checks */
 99 
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());
105 
106     if (0) /* This documents win2k behavior. It crashes on win98. */
107     { 
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());
111     }
112 
113     ret = FindNextChangeNotification(NULL);
114     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n",
115        GetLastError());
116 
117     ret = FindCloseChangeNotification(NULL);
118     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
119        GetLastError());
120 
121     ret = GetTempPathA(MAX_PATH, workdir);
122     ok(ret, "GetTempPathA error: %d\n", GetLastError());
123 
124     lstrcatA(workdir, "testFileChangeNotification");
125 
126     ret = CreateDirectoryA(workdir, NULL);
127     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
128 
129     ret = GetTempFileNameA(workdir, prefix, 0, filename1);
130     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
131 
132     file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
133                        FILE_ATTRIBUTE_NORMAL, 0);
134     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
135     ret = CloseHandle(file);
136     ok( ret, "CloseHandle error: %d\n", GetLastError());
137 
138     /* Try to register notification for a file. win98 and win2k behave differently here */
139     change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
140     ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
141                                           GetLastError() == ERROR_FILE_NOT_FOUND),
142        "FindFirstChangeNotification error: %d\n", GetLastError());
143 
144     lstrcpyA(dirname1, filename1);
145     lstrcatA(dirname1, "dir");
146 
147     lstrcpyA(dirname2, dirname1);
148     lstrcatA(dirname2, "new");
149 
150     ret = CreateDirectoryA(dirname1, NULL);
151     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
152 
153     /* What if we move the directory we registered notification for? */
154     thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
155     ret = MoveFileA(dirname1, dirname2);
156     ok(ret, "MoveFileA error: %d\n", GetLastError());
157     /* win9x and win2k behave differently here, don't check result */
158     FinishNotificationThread(thread);
159 
160     /* What if we remove the directory we registered notification for? */
161     thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
162     ret = RemoveDirectoryA(dirname2);
163     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
164     /* win9x and win2k behave differently here, don't check result */
165     FinishNotificationThread(thread);
166 
167     /* functional checks */
168 
169     /* Create a directory */
170     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
171     ret = CreateDirectoryA(dirname1, NULL);
172     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
173     ok(FinishNotificationThread(thread), "Missed notification\n");
174 
175     /* Rename a directory */
176     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
177     ret = MoveFileA(dirname1, dirname2);
178     ok(ret, "MoveFileA error: %d\n", GetLastError());
179     ok(FinishNotificationThread(thread), "Missed notification\n");
180 
181     /* Delete a directory */
182     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
183     ret = RemoveDirectoryA(dirname2);
184     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
185     ok(FinishNotificationThread(thread), "Missed notification\n");
186 
187     lstrcpyA(filename2, filename1);
188     lstrcatA(filename2, "new");
189 
190     /* Rename a file */
191     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
192     ret = MoveFileA(filename1, filename2);
193     ok(ret, "MoveFileA error: %d\n", GetLastError());
194     ok(FinishNotificationThread(thread), "Missed notification\n");
195 
196     /* Delete a file */
197     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
198     ret = DeleteFileA(filename2);
199     ok(ret, "DeleteFileA error: %d\n", GetLastError());
200     ok(FinishNotificationThread(thread), "Missed notification\n");
201 
202     /* Create a file */
203     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
204     file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 
205                        FILE_ATTRIBUTE_NORMAL, 0);
206     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
207     ret = CloseHandle(file);
208     ok( ret, "CloseHandle error: %d\n", GetLastError());
209     ok(FinishNotificationThread(thread), "Missed notification\n");
210 
211     attributes = GetFileAttributesA(filename2);
212     ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError());
213     attributes &= FILE_ATTRIBUTE_READONLY;
214 
215     /* Change file attributes */
216     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
217     ret = SetFileAttributesA(filename2, attributes);
218     ok(ret, "SetFileAttributesA error: %d\n", GetLastError());
219     ok(FinishNotificationThread(thread), "Missed notification\n");
220 
221     /* Change last write time by writing to a file */
222     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
223     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
224                        FILE_ATTRIBUTE_NORMAL, 0);
225     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
226     memset(buffer, 0, sizeof(buffer));
227     ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
228     ok(ret && count == sizeof(buffer), "WriteFile error: %d\n", GetLastError());
229     ret = CloseHandle(file);
230     ok( ret, "CloseHandle error: %d\n", GetLastError());
231     ok(FinishNotificationThread(thread), "Missed notification\n");
232 
233     /* Change file size by truncating a file */
234     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
235     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
236                        FILE_ATTRIBUTE_NORMAL, 0);
237     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
238     ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
239     ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %d\n", GetLastError());
240     ret = CloseHandle(file);
241     ok( ret, "CloseHandle error: %d\n", GetLastError());
242     ok(FinishNotificationThread(thread), "Missed notification\n");
243 
244     /* clean up */
245     
246     ret = DeleteFileA(filename2);
247     ok(ret, "DeleteFileA error: %d\n", GetLastError());
248 
249     ret = RemoveDirectoryA(workdir);
250     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
251 }
252 
253 /* this test concentrates more on the wait behaviour of the handle */
254 static void test_ffcn(void)
255 {
256     DWORD filter;
257     HANDLE handle;
258     LONG r;
259     WCHAR path[MAX_PATH], subdir[MAX_PATH];
260     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
261     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
262 
263     SetLastError(0xdeadbeef);
264     r = GetTempPathW( MAX_PATH, path );
265     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
266     {
267         skip("GetTempPathW is not implemented\n");
268         return;
269     }
270     ok( r != 0, "temp path failed\n");
271     if (!r)
272         return;
273 
274     lstrcatW( path, szBoo );
275     lstrcpyW( subdir, path );
276     lstrcatW( subdir, szHoo );
277 
278     RemoveDirectoryW( subdir );
279     RemoveDirectoryW( path );
280     
281     r = CreateDirectoryW(path, NULL);
282     ok( r == TRUE, "failed to create directory\n");
283 
284     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
285     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
286 
287     handle = FindFirstChangeNotificationW( path, 1, filter);
288     ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
289 
290     r = WaitForSingleObject( handle, 0 );
291     ok( r == STATUS_TIMEOUT, "should time out\n");
292 
293     r = CreateDirectoryW( subdir, NULL );
294     ok( r == TRUE, "failed to create subdir\n");
295 
296     r = WaitForSingleObject( handle, 0 );
297     ok( r == WAIT_OBJECT_0, "should be ready\n");
298 
299     r = WaitForSingleObject( handle, 0 );
300     ok( r == WAIT_OBJECT_0, "should be ready\n");
301 
302     r = FindNextChangeNotification(handle);
303     ok( r == TRUE, "find next failed\n");
304 
305     r = WaitForSingleObject( handle, 0 );
306     ok( r == STATUS_TIMEOUT, "should time out\n");
307 
308     r = RemoveDirectoryW( subdir );
309     ok( r == TRUE, "failed to remove subdir\n");
310 
311     r = WaitForSingleObject( handle, 0 );
312     ok( r == WAIT_OBJECT_0, "should be ready\n");
313 
314     r = WaitForSingleObject( handle, 0 );
315     ok( r == WAIT_OBJECT_0, "should be ready\n");
316 
317     r = FindNextChangeNotification(handle);
318     ok( r == TRUE, "find next failed\n");
319 
320     r = FindNextChangeNotification(handle);
321     ok( r == TRUE, "find next failed\n");
322 
323     r = FindCloseChangeNotification(handle);
324     ok( r == TRUE, "should succeed\n");
325 
326     r = RemoveDirectoryW( path );
327     ok( r == TRUE, "failed to remove dir\n");
328 }
329 
330 /* this test concentrates on the wait behavior when multiple threads are
331  * waiting on a change notification handle. */
332 static void test_ffcnMultipleThreads(void)
333 {
334     LONG r;
335     DWORD filter, threadId, status, exitcode;
336     HANDLE handles[2];
337     char path[MAX_PATH];
338 
339     r = GetTempPathA(MAX_PATH, path);
340     ok(r, "GetTempPathA error: %d\n", GetLastError());
341 
342     lstrcatA(path, "ffcnTestMultipleThreads");
343 
344     RemoveDirectoryA(path);
345 
346     r = CreateDirectoryA(path, NULL);
347     ok(r, "CreateDirectoryA error: %d\n", GetLastError());
348 
349     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
350     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
351 
352     handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
353     ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
354 
355     /* Test behavior if a waiting thread holds the last reference to a change
356      * directory object with an empty wine user APC queue for this thread (bug #7286) */
357 
358     /* Create our notification thread */
359     handles[1] = CreateThread(NULL, 0, NotificationThread, (LPVOID)handles[0],
360                               0, &threadId);
361     ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
362 
363     status = WaitForMultipleObjects(2, handles, FALSE, 5000);
364     ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError());
365     ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
366 
367     /* Clean up */
368     r = RemoveDirectoryA( path );
369     ok( r == TRUE, "failed to remove dir\n");
370 }
371 
372 typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
373                          LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
374 fnReadDirectoryChangesW pReadDirectoryChangesW;
375 
376 static void test_readdirectorychanges(void)
377 {
378     HANDLE hdir;
379     char buffer[0x1000];
380     DWORD fflags, filter = 0, r, dwCount;
381     OVERLAPPED ov;
382     WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
383     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
384     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
385     static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
386     PFILE_NOTIFY_INFORMATION pfni;
387 
388     if (!pReadDirectoryChangesW)
389     {
390         skip("ReadDirectoryChangesW is not available\n");
391         return;
392     }
393 
394     SetLastError(0xdeadbeef);
395     r = GetTempPathW( MAX_PATH, path );
396     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
397     {
398         skip("GetTempPathW is not implemented\n");
399         return;
400     }
401     ok( r != 0, "temp path failed\n");
402     if (!r)
403         return;
404 
405     lstrcatW( path, szBoo );
406     lstrcpyW( subdir, path );
407     lstrcatW( subdir, szHoo );
408 
409     lstrcpyW( subsubdir, path );
410     lstrcatW( subsubdir, szGa );
411 
412     RemoveDirectoryW( subsubdir );
413     RemoveDirectoryW( subdir );
414     RemoveDirectoryW( path );
415     
416     r = CreateDirectoryW(path, NULL);
417     ok( r == TRUE, "failed to create directory\n");
418 
419     SetLastError(0xd0b00b00);
420     r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
421     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
422     ok(r==FALSE, "should return false\n");
423 
424     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
425     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
426                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
427                         OPEN_EXISTING, fflags, NULL);
428     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
429 
430     ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
431 
432     SetLastError(0xd0b00b00);
433     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
434     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
435     ok(r==FALSE, "should return false\n");
436 
437     SetLastError(0xd0b00b00);
438     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
439     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
440     ok(r==FALSE, "should return false\n");
441 
442     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
443     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
444     filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
445     filter |= FILE_NOTIFY_CHANGE_SIZE;
446     filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
447     filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
448     filter |= FILE_NOTIFY_CHANGE_CREATION;
449     filter |= FILE_NOTIFY_CHANGE_SECURITY;
450 
451     SetLastError(0xd0b00b00);
452     ov.Internal = 0;
453     ov.InternalHigh = 0;
454     memset( buffer, 0, sizeof buffer );
455 
456     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
457     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
458     ok(r==FALSE, "should return false\n");
459 
460     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
461     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
462     ok(r==FALSE, "should return false\n");
463 
464     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
465     ok(r==TRUE, "should return true\n");
466 
467     r = WaitForSingleObject( ov.hEvent, 10 );
468     ok( r == STATUS_TIMEOUT, "should timeout\n" );
469 
470     r = CreateDirectoryW( subdir, NULL );
471     ok( r == TRUE, "failed to create directory\n");
472 
473     r = WaitForSingleObject( ov.hEvent, 1000 );
474     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
475 
476     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
477     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
478 
479     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
480     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
481     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
482     ok( pfni->FileNameLength == 6, "len wrong\n" );
483     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
484 
485     ResetEvent(ov.hEvent);
486     SetLastError(0xd0b00b00);
487     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
488     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
489     ok(r==FALSE, "should return false\n");
490 
491     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
492     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
493     ok(r==FALSE, "should return false\n");
494 
495     filter = FILE_NOTIFY_CHANGE_SIZE;
496 
497     SetEvent(ov.hEvent);
498     ov.Internal = 1;
499     ov.InternalHigh = 1;
500     S(U(ov)).Offset = 0;
501     S(U(ov)).OffsetHigh = 0;
502     memset( buffer, 0, sizeof buffer );
503     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
504     ok(r==TRUE, "should return true\n");
505 
506     ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
507     ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
508 
509     r = WaitForSingleObject( ov.hEvent, 0 );
510     ok( r == STATUS_TIMEOUT, "should timeout\n" );
511 
512     r = RemoveDirectoryW( subdir );
513     ok( r == TRUE, "failed to remove directory\n");
514 
515     r = WaitForSingleObject( ov.hEvent, 1000 );
516     ok( r == WAIT_OBJECT_0, "should be ready\n" );
517 
518     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
519     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
520 
521     if (ov.Internal == STATUS_SUCCESS)
522     {
523         r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
524         ok( r == TRUE, "getoverlappedresult failed\n");
525         ok( dwCount == 0x12, "count wrong\n");
526     }
527 
528     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
529     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
530     ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
531     ok( pfni->FileNameLength == 6, "len wrong\n" );
532     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
533 
534     /* what happens if the buffer is too small? */
535     r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
536     ok(r==TRUE, "should return true\n");
537 
538     r = CreateDirectoryW( subdir, NULL );
539     ok( r == TRUE, "failed to create directory\n");
540 
541     r = WaitForSingleObject( ov.hEvent, 1000 );
542     ok( r == WAIT_OBJECT_0, "should be ready\n" );
543 
544     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
545     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
546 
547     /* test the recursive watch */
548     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
549     ok(r==TRUE, "should return true\n");
550 
551     r = CreateDirectoryW( subsubdir, NULL );
552     ok( r == TRUE, "failed to create directory\n");
553 
554     r = WaitForSingleObject( ov.hEvent, 1000 );
555     ok( r == WAIT_OBJECT_0, "should be ready\n" );
556 
557     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
558     ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
559 
560     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
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" );
565 
566     r = RemoveDirectoryW( subsubdir );
567     ok( r == TRUE, "failed to remove directory\n");
568 
569     ov.Internal = 1;
570     ov.InternalHigh = 1;
571     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
572     ok(r==TRUE, "should return true\n");
573 
574     r = RemoveDirectoryW( subdir );
575     ok( r == TRUE, "failed to remove directory\n");
576 
577     r = WaitForSingleObject( ov.hEvent, 1000 );
578     ok( r == WAIT_OBJECT_0, "should be ready\n" );
579 
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)
583     {
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);
587     }
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" );
592 
593     ok( 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 );
596 
597     CloseHandle(hdir);
598 
599     r = RemoveDirectoryW( path );
600     ok( r == TRUE, "failed to remove directory\n");
601 }
602 
603 /* show the behaviour when a null buffer is passed */
604 static void test_readdirectorychanges_null(void)
605 {
606     NTSTATUS r;
607     HANDLE hdir;
608     char buffer[0x1000];
609     DWORD fflags, filter = 0;
610     OVERLAPPED ov;
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;
615 
616     if (!pReadDirectoryChangesW)
617     {
618         skip("ReadDirectoryChangesW is not available\n");
619         return;
620     }
621     SetLastError(0xdeadbeef);
622     r = GetTempPathW( MAX_PATH, path );
623     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
624     {
625         skip("GetTempPathW is not implemented\n");
626         return;
627     }
628     ok( r != 0, "temp path failed\n");
629     if (!r)
630         return;
631 
632     lstrcatW( path, szBoo );
633     lstrcpyW( subdir, path );
634     lstrcatW( subdir, szHoo );
635 
636     RemoveDirectoryW( subdir );
637     RemoveDirectoryW( path );
638     
639     r = CreateDirectoryW(path, NULL);
640     ok( r == TRUE, "failed to create directory\n");
641 
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");
647 
648     ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
649 
650     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
651     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
652 
653     SetLastError(0xd0b00b00);
654     ov.Internal = 0;
655     ov.InternalHigh = 0;
656     memset( buffer, 0, sizeof buffer );
657 
658     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
659     ok(r==TRUE, "should return true\n");
660 
661     r = WaitForSingleObject( ov.hEvent, 0 );
662     ok( r == STATUS_TIMEOUT, "should timeout\n" );
663 
664     r = CreateDirectoryW( subdir, NULL );
665     ok( r == TRUE, "failed to create directory\n");
666 
667     r = WaitForSingleObject( ov.hEvent, 0 );
668     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
669 
670     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
671     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
672 
673     ov.Internal = 0;
674     ov.InternalHigh = 0;
675     S(U(ov)).Offset = 0;
676     S(U(ov)).OffsetHigh = 0;
677     memset( buffer, 0, sizeof buffer );
678 
679     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
680     ok(r==TRUE, "should return true\n");
681 
682     r = WaitForSingleObject( ov.hEvent, 0 );
683     ok( r == STATUS_TIMEOUT, "should timeout\n" );
684 
685     r = RemoveDirectoryW( subdir );
686     ok( r == TRUE, "failed to remove directory\n");
687 
688     r = WaitForSingleObject( ov.hEvent, 1000 );
689     ok( r == WAIT_OBJECT_0, "should be ready\n" );
690 
691     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
692     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
693 
694     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
695     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
696 
697     CloseHandle(hdir);
698 
699     r = RemoveDirectoryW( path );
700     ok( r == TRUE, "failed to remove directory\n");
701 }
702 
703 static void test_readdirectorychanges_filedir(void)
704 {
705     NTSTATUS r;
706     HANDLE hdir, hfile;
707     char buffer[0x1000];
708     DWORD fflags, filter = 0;
709     OVERLAPPED ov;
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;
715 
716     SetLastError(0xdeadbeef);
717     r = GetTempPathW( MAX_PATH, path );
718     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
719     {
720         skip("GetTempPathW is not implemented\n");
721         return;
722     }
723     ok( r != 0, "temp path failed\n");
724     if (!r)
725         return;
726 
727     lstrcatW( path, szBoo );
728     lstrcpyW( subdir, path );
729     lstrcatW( subdir, szHoo );
730 
731     lstrcpyW( file, path );
732     lstrcatW( file, szFoo );
733 
734     DeleteFileW( file );
735     RemoveDirectoryW( subdir );
736     RemoveDirectoryW( path );
737     
738     r = CreateDirectoryW(path, NULL);
739     ok( r == TRUE, "failed to create directory\n");
740 
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");
746 
747     ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
748 
749     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
<