Reactos
at master 1101 lines 42 kB view raw
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 39static DWORD CALLBACK NotificationThread(LPVOID arg) 40{ 41 HANDLE change = 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 FindNextChangeNotification(change); 51 } 52 53 ret = FindCloseChangeNotification(change); 54 ok( ret, "FindCloseChangeNotification error: %ld\n", 55 GetLastError()); 56 57 ExitThread((DWORD)notified); 58} 59 60static 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: %ld\n", GetLastError()); 67 68 thread = CreateThread(NULL, 0, NotificationThread, change, 0, &threadId); 69 ok(thread != NULL, "CreateThread error: %ld\n", GetLastError()); 70 71 return thread; 72} 73 74static DWORD FinishNotificationThread(HANDLE thread) 75{ 76 DWORD status, exitcode; 77 78 status = WaitForSingleObject(thread, 5000); 79 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %ld error %ld\n", status, GetLastError()); 80 81 ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n"); 82 CloseHandle(thread); 83 84 return exitcode; 85} 86 87static 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 "FindFirstChangeNotification error: %ld\n", GetLastError()); 104 105 change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 106 ok(change == INVALID_HANDLE_VALUE || broken(change == NULL) /* < win7 */, 107 "Expected INVALID_HANDLE_VALUE, got %p\n", change); 108 ok(GetLastError() == ERROR_PATH_NOT_FOUND, 109 "FindFirstChangeNotification error: %lu\n", GetLastError()); 110 111 ret = FindNextChangeNotification(NULL); 112 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %ld\n", 113 GetLastError()); 114 115 ret = FindCloseChangeNotification(NULL); 116 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n", 117 GetLastError()); 118 119 ret = GetTempPathA(MAX_PATH, dirname1); 120 ok(ret, "GetTempPathA error: %ld\n", GetLastError()); 121 122 ret = GetTempFileNameA(dirname1, "ffc", 0, workdir); 123 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError()); 124 DeleteFileA( workdir ); 125 126 ret = CreateDirectoryA(workdir, NULL); 127 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError()); 128 129 ret = GetTempFileNameA(workdir, prefix, 0, filename1); 130 ok(ret, "GetTempFileNameA error: %ld\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: %ld\n", GetLastError()); 135 ret = CloseHandle(file); 136 ok( ret, "CloseHandle error: %ld\n", GetLastError()); 137 138 /* Try to register notification for a file */ 139 change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 140 ok(change == INVALID_HANDLE_VALUE, "Expected INVALID_HANDLE_VALUE, got %p\n", change); 141 ok(GetLastError() == ERROR_DIRECTORY, 142 "FindFirstChangeNotification error: %ld\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: %ld\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: %ld\n", GetLastError()); 157 ok(!FinishNotificationThread(thread), "Got notification\n"); 158 159 /* What if we remove the directory we registered notification for? */ 160 thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 161 ret = RemoveDirectoryA(dirname2); 162 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError()); 163 ret = FinishNotificationThread(thread); 164 todo_wine ok(ret, "Missed notification\n"); 165 166 /* functional checks */ 167 168 /* Create a directory */ 169 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 170 ret = CreateDirectoryA(dirname1, NULL); 171 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError()); 172 ok(FinishNotificationThread(thread), "Missed notification\n"); 173 174 /* Rename a directory */ 175 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 176 ret = MoveFileA(dirname1, dirname2); 177 ok(ret, "MoveFileA error: %ld\n", GetLastError()); 178 ok(FinishNotificationThread(thread), "Missed notification\n"); 179 180 /* Delete a directory */ 181 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 182 ret = RemoveDirectoryA(dirname2); 183 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError()); 184 ok(FinishNotificationThread(thread), "Missed notification\n"); 185 186 lstrcpyA(filename2, filename1); 187 lstrcatA(filename2, "new"); 188 189 /* Rename a file */ 190 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 191 ret = MoveFileA(filename1, filename2); 192 ok(ret, "MoveFileA error: %ld\n", GetLastError()); 193 ok(FinishNotificationThread(thread), "Missed notification\n"); 194 195 /* Delete a file */ 196 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 197 ret = DeleteFileA(filename2); 198 ok(ret, "DeleteFileA error: %ld\n", GetLastError()); 199 ok(FinishNotificationThread(thread), "Missed notification\n"); 200 201 /* Create a file */ 202 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 203 file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 204 FILE_ATTRIBUTE_NORMAL, 0); 205 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError()); 206 ret = CloseHandle(file); 207 ok( ret, "CloseHandle error: %ld\n", GetLastError()); 208 ok(FinishNotificationThread(thread), "Missed notification\n"); 209 210 attributes = GetFileAttributesA(filename2); 211 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %ld\n", GetLastError()); 212 attributes &= FILE_ATTRIBUTE_READONLY; 213 214 /* Change file attributes */ 215 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES); 216 ret = SetFileAttributesA(filename2, attributes); 217 ok(ret, "SetFileAttributesA error: %ld\n", GetLastError()); 218 ok(FinishNotificationThread(thread), "Missed notification\n"); 219 220 /* Change last write time by writing to a file */ 221 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE); 222 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 223 FILE_ATTRIBUTE_NORMAL, 0); 224 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError()); 225 memset(buffer, 0, sizeof(buffer)); 226 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL); 227 ok(ret && count == sizeof(buffer), "WriteFile error: %ld\n", GetLastError()); 228 ret = CloseHandle(file); 229 ok( ret, "CloseHandle error: %ld\n", GetLastError()); 230 ok(FinishNotificationThread(thread), "Missed notification\n"); 231 232 /* Change file size by truncating a file */ 233 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE); 234 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 235 FILE_ATTRIBUTE_NORMAL, 0); 236 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError()); 237 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL); 238 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %ld\n", GetLastError()); 239 ret = CloseHandle(file); 240 ok( ret, "CloseHandle error: %ld\n", GetLastError()); 241 ok(FinishNotificationThread(thread), "Missed notification\n"); 242 243 /* clean up */ 244 245 ret = DeleteFileA(filename2); 246 ok(ret, "DeleteFileA error: %ld\n", GetLastError()); 247 248 ret = RemoveDirectoryA(workdir); 249 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError()); 250} 251 252/* this test concentrates more on the wait behaviour of the handle */ 253static void test_ffcn(void) 254{ 255 DWORD filter; 256 HANDLE handle, file; 257 LONG r; 258 WCHAR path[MAX_PATH], subdir[MAX_PATH], filename[MAX_PATH]; 259 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 260 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 261 static const WCHAR szZoo[] = { '\\','z','o','o',0 }; 262 263 r = GetTempPathW( MAX_PATH, path ); 264 ok( r != 0, "temp path failed\n"); 265 266 lstrcatW( path, szBoo ); 267 lstrcpyW( subdir, path ); 268 lstrcatW( subdir, szHoo ); 269 270 lstrcpyW( filename, path ); 271 lstrcatW( filename, szZoo ); 272 273 RemoveDirectoryW( subdir ); 274 RemoveDirectoryW( path ); 275 276 r = CreateDirectoryW(path, NULL); 277 ok( r == TRUE, "failed to create directory\n"); 278 279 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 280 filter |= FILE_NOTIFY_CHANGE_DIR_NAME; 281 282 handle = FindFirstChangeNotificationW( path, 1, filter); 283 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n"); 284 285 r = WaitForSingleObject( handle, 0 ); 286 ok( r == STATUS_TIMEOUT, "should time out\n"); 287 288 file = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, 289 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); 290 ok( file != INVALID_HANDLE_VALUE, "CreateFile error %lu\n", GetLastError() ); 291 CloseHandle(file); 292 293 r = WaitForSingleObject( handle, 1000 ); 294 ok( r == WAIT_OBJECT_0, "should be ready\n"); 295 296 r = WaitForSingleObject( handle, 0 ); 297 ok( r == WAIT_OBJECT_0, "should be ready\n"); 298 299 r = FindNextChangeNotification(handle); 300 ok( r == TRUE, "find next failed\n"); 301 302 r = WaitForSingleObject( handle, 0 ); 303 ok( r == STATUS_TIMEOUT, "should time out\n"); 304 305 r = DeleteFileW( filename ); 306 ok( r == TRUE, "failed to remove file\n"); 307 308 r = WaitForSingleObject( handle, 1000 ); 309 ok( r == WAIT_OBJECT_0, "should be ready\n"); 310 311 r = WaitForSingleObject( handle, 0 ); 312 ok( r == WAIT_OBJECT_0, "should be ready\n"); 313 314 r = FindNextChangeNotification(handle); 315 ok( r == TRUE, "find next failed\n"); 316 317 r = WaitForSingleObject( handle, 0 ); 318 ok( r == STATUS_TIMEOUT, "should time out\n"); 319 320 r = CreateDirectoryW( subdir, NULL ); 321 ok( r == TRUE, "failed to create subdir\n"); 322 323 r = WaitForSingleObject( handle, 1000 ); 324 ok( r == WAIT_OBJECT_0, "should be ready\n"); 325 326 r = WaitForSingleObject( handle, 0 ); 327 ok( r == WAIT_OBJECT_0, "should be ready\n"); 328 329 r = FindNextChangeNotification(handle); 330 ok( r == TRUE, "find next failed\n"); 331 332 r = WaitForSingleObject( handle, 0 ); 333 ok( r == STATUS_TIMEOUT, "should time out\n"); 334 335 r = RemoveDirectoryW( subdir ); 336 ok( r == TRUE, "failed to remove subdir\n"); 337 338 r = WaitForSingleObject( handle, 1000 ); 339 ok( r == WAIT_OBJECT_0, "should be ready\n"); 340 341 r = WaitForSingleObject( handle, 0 ); 342 ok( r == WAIT_OBJECT_0, "should be ready\n"); 343 344 r = FindNextChangeNotification(handle); 345 ok( r == TRUE, "find next failed\n"); 346 347 r = FindNextChangeNotification(handle); 348 ok( r == TRUE, "find next failed\n"); 349 350 r = FindCloseChangeNotification(handle); 351 ok( r == TRUE, "should succeed\n"); 352 353 r = RemoveDirectoryW( path ); 354 ok( r == TRUE, "failed to remove dir\n"); 355} 356 357/* this test concentrates on the wait behavior when multiple threads are 358 * waiting on a change notification handle. */ 359static void test_ffcnMultipleThreads(void) 360{ 361 LONG r; 362 DWORD filter, threadId, status, exitcode; 363 HANDLE handles[2]; 364 char tmp[MAX_PATH], path[MAX_PATH]; 365 366 r = GetTempPathA(MAX_PATH, tmp); 367 ok(r, "GetTempPathA error: %ld\n", GetLastError()); 368 369 r = GetTempFileNameA(tmp, "ffc", 0, path); 370 ok(r, "GetTempFileNameA error: %ld\n", GetLastError()); 371 DeleteFileA( path ); 372 373 r = CreateDirectoryA(path, NULL); 374 ok(r, "CreateDirectoryA error: %ld\n", GetLastError()); 375 376 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 377 filter |= FILE_NOTIFY_CHANGE_DIR_NAME; 378 379 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter); 380 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %ld\n", GetLastError()); 381 382 /* Test behavior if a waiting thread holds the last reference to a change 383 * directory object with an empty wine user APC queue for this thread (bug #7286) */ 384 385 /* Create our notification thread */ 386 handles[1] = CreateThread(NULL, 0, NotificationThread, handles[0], 0, 387 &threadId); 388 ok(handles[1] != NULL, "CreateThread error: %ld\n", GetLastError()); 389 390 status = WaitForMultipleObjects(2, handles, FALSE, 5000); 391 ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %ld error %ld\n", status, GetLastError()); 392 ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n"); 393 394 /* Clean up */ 395 r = RemoveDirectoryA( path ); 396 ok( r == TRUE, "failed to remove dir\n"); 397} 398 399static void test_readdirectorychanges(void) 400{ 401 HANDLE hdir; 402 char buffer[0x1000]; 403 DWORD fflags, filter = 0, r, dwCount; 404 OVERLAPPED ov; 405 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH]; 406 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 407 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 408 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 }; 409 PFILE_NOTIFY_INFORMATION pfni; 410 BOOL got_subdir_change = FALSE; 411 412 r = GetTempPathW( MAX_PATH, path ); 413 ok( r != 0, "temp path failed\n"); 414 415 lstrcatW( path, szBoo ); 416 lstrcpyW( subdir, path ); 417 lstrcatW( subdir, szHoo ); 418 419 lstrcpyW( subsubdir, path ); 420 lstrcatW( subsubdir, szGa ); 421 422 RemoveDirectoryW( subsubdir ); 423 RemoveDirectoryW( subdir ); 424 RemoveDirectoryW( path ); 425 426 r = CreateDirectoryW(path, NULL); 427 ok( r == TRUE, "failed to create directory\n"); 428 429 SetLastError(0xd0b00b00); 430 r = ReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL); 431 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 432 ok(r==FALSE, "should return false\n"); 433 434 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED; 435 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 436 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 437 OPEN_EXISTING, fflags, NULL); 438 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 439 440 ov.hEvent = CreateEventW( NULL, 1, 0, NULL ); 441 442 SetLastError(0xd0b00b00); 443 r = ReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL); 444 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 445 ok(r==FALSE, "should return false\n"); 446 447 SetLastError(0xd0b00b00); 448 r = ReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL); 449 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 450 ok(r==FALSE, "should return false\n"); 451 452 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 453 filter |= FILE_NOTIFY_CHANGE_DIR_NAME; 454 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 455 filter |= FILE_NOTIFY_CHANGE_SIZE; 456 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 457 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 458 filter |= FILE_NOTIFY_CHANGE_CREATION; 459 filter |= FILE_NOTIFY_CHANGE_SECURITY; 460 461 SetLastError(0xd0b00b00); 462 ov.Internal = 0; 463 ov.InternalHigh = 0; 464 memset( buffer, 0, sizeof buffer ); 465 466 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL); 467 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 468 ok(r==FALSE, "should return false\n"); 469 470 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL); 471 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 472 ok(r==FALSE, "should return false\n"); 473 474 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL); 475 ok(r==TRUE, "should return true\n"); 476 477 r = WaitForSingleObject( ov.hEvent, 10 ); 478 ok( r == STATUS_TIMEOUT, "should timeout\n" ); 479 480 r = CreateDirectoryW( subdir, NULL ); 481 ok( r == TRUE, "failed to create directory\n"); 482 483 r = WaitForSingleObject( ov.hEvent, 1000 ); 484 ok( r == WAIT_OBJECT_0, "event should be ready\n" ); 485 486 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 487 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); 488 489 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 490 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 491 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" ); 492 ok( pfni->FileNameLength == 6, "len wrong\n" ); 493 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" ); 494 495 ResetEvent(ov.hEvent); 496 SetLastError(0xd0b00b00); 497 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL); 498 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 499 ok(r==FALSE, "should return false\n"); 500 501 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL); 502 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 503 ok(r==FALSE, "should return false\n"); 504 505 filter = FILE_NOTIFY_CHANGE_SIZE; 506 507 SetEvent(ov.hEvent); 508 ov.Internal = 1; 509 ov.InternalHigh = 1; 510 ov.Offset = 0; 511 ov.OffsetHigh = 0; 512 memset( buffer, 0, sizeof buffer ); 513 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 514 ok(r==TRUE, "should return true\n"); 515 516 ok( (NTSTATUS)ov.Internal == STATUS_PENDING, "ov.Internal wrong\n"); 517 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n"); 518 519 r = WaitForSingleObject( ov.hEvent, 0 ); 520 ok( r == STATUS_TIMEOUT, "should timeout\n" ); 521 522 r = RemoveDirectoryW( subdir ); 523 ok( r == TRUE, "failed to remove directory\n"); 524 525 r = WaitForSingleObject( ov.hEvent, 1000 ); 526 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 527 528 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 529 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); 530 531 if ((NTSTATUS)ov.Internal == STATUS_SUCCESS) 532 { 533 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE ); 534 ok( r == TRUE, "getoverlappedresult failed\n"); 535 ok( dwCount == 0x12, "count wrong\n"); 536 } 537 538 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 539 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 540 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" ); 541 ok( pfni->FileNameLength == 6, "len wrong\n" ); 542 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" ); 543 544 /* what happens if the buffer is too small? */ 545 r = ReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL); 546 ok(r==TRUE, "should return true\n"); 547 548 r = CreateDirectoryW( subdir, NULL ); 549 ok( r == TRUE, "failed to create directory\n"); 550 551 r = WaitForSingleObject( ov.hEvent, 1000 ); 552 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 553 554 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); 555 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); 556 557 /* test the recursive watch */ 558 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 559 ok(r==TRUE, "should return true\n"); 560 561 r = CreateDirectoryW( subsubdir, NULL ); 562 ok( r == TRUE, "failed to create directory\n"); 563 564 while (1) 565 { 566 r = WaitForSingleObject( ov.hEvent, 1000 ); 567 ok(r == WAIT_OBJECT_0, "should be ready\n" ); 568 if (r == WAIT_TIMEOUT) break; 569 570 ok((NTSTATUS) ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 571 572 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 573 while (1) 574 { 575 /* We might get one or more modified events on the parent dir */ 576 if (pfni->Action == FILE_ACTION_MODIFIED) 577 { 578 ok(pfni->FileNameLength == 3 * sizeof(WCHAR), "len wrong\n" ); 579 ok(!memcmp(pfni->FileName, &szGa[1], 3 * sizeof(WCHAR)), "name wrong\n"); 580 } 581 else 582 { 583 ok(pfni->Action == FILE_ACTION_ADDED, "action wrong\n"); 584 ok(pfni->FileNameLength == 6 * sizeof(WCHAR), "len wrong\n" ); 585 ok(!memcmp(pfni->FileName, &szGa[1], 6 * sizeof(WCHAR)), "name wrong\n"); 586 got_subdir_change = TRUE; 587 } 588 if (!pfni->NextEntryOffset) break; 589 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset); 590 } 591 592 if (got_subdir_change) break; 593 594 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 595 ok(r==TRUE, "should return true\n"); 596 } 597 ok(got_subdir_change, "didn't get subdir change\n"); 598 599 r = RemoveDirectoryW( subsubdir ); 600 ok( r == TRUE, "failed to remove directory\n"); 601 602 ov.Internal = 1; 603 ov.InternalHigh = 1; 604 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 605 ok(r==TRUE, "should return true\n"); 606 607 r = RemoveDirectoryW( subdir ); 608 ok( r == TRUE, "failed to remove directory\n"); 609 610 r = WaitForSingleObject( ov.hEvent, 1000 ); 611 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 612 613 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 614 /* we may get a notification for the parent dir too */ 615 if (pfni->Action == FILE_ACTION_MODIFIED && pfni->NextEntryOffset) 616 { 617 ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong %lu\n", pfni->FileNameLength ); 618 ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n" ); 619 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset); 620 } 621 ok( pfni->NextEntryOffset == 0, "offset wrong %lu\n", pfni->NextEntryOffset ); 622 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong %lu\n", pfni->Action ); 623 ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %lu\n", pfni->FileNameLength ); 624 ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" ); 625 626 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 627 dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer; 628 ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %Iu/%lu\n",ov.InternalHigh, dwCount ); 629 630 CloseHandle(hdir); 631 632 r = RemoveDirectoryW( path ); 633 ok( r == TRUE, "failed to remove directory\n"); 634} 635 636/* show the behaviour when a null buffer is passed */ 637static void test_readdirectorychanges_null(void) 638{ 639 NTSTATUS r; 640 HANDLE hdir; 641 char buffer[0x1000]; 642 DWORD fflags, filter = 0; 643 OVERLAPPED ov; 644 WCHAR path[MAX_PATH], subdir[MAX_PATH]; 645 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 646 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 647 PFILE_NOTIFY_INFORMATION pfni; 648 649 r = GetTempPathW( MAX_PATH, path ); 650 ok( r != 0, "temp path failed\n"); 651 652 lstrcatW( path, szBoo ); 653 lstrcpyW( subdir, path ); 654 lstrcatW( subdir, szHoo ); 655 656 RemoveDirectoryW( subdir ); 657 RemoveDirectoryW( path ); 658 659 r = CreateDirectoryW(path, NULL); 660 ok( r == TRUE, "failed to create directory\n"); 661 662 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED; 663 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 664 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 665 OPEN_EXISTING, fflags, NULL); 666 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 667 668 ov.hEvent = CreateEventW( NULL, 1, 0, NULL ); 669 670 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 671 filter |= FILE_NOTIFY_CHANGE_DIR_NAME; 672 673 SetLastError(0xd0b00b00); 674 ov.Internal = 0; 675 ov.InternalHigh = 0; 676 memset( buffer, 0, sizeof buffer ); 677 678 r = ReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL); 679 ok(r==TRUE, "should return true\n"); 680 681 r = WaitForSingleObject( ov.hEvent, 0 ); 682 ok( r == STATUS_TIMEOUT, "should timeout\n" ); 683 684 r = CreateDirectoryW( subdir, NULL ); 685 ok( r == TRUE, "failed to create directory\n"); 686 687 r = WaitForSingleObject( ov.hEvent, 0 ); 688 ok( r == WAIT_OBJECT_0, "event should be ready\n" ); 689 690 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); 691 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); 692 693 ov.Internal = 0; 694 ov.InternalHigh = 0; 695 ov.Offset = 0; 696 ov.OffsetHigh = 0; 697 memset( buffer, 0, sizeof buffer ); 698 699 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 700 ok(r==TRUE, "should return true\n"); 701 702 r = WaitForSingleObject( ov.hEvent, 0 ); 703 ok( r == STATUS_TIMEOUT, "should timeout\n" ); 704 705 r = RemoveDirectoryW( subdir ); 706 ok( r == TRUE, "failed to remove directory\n"); 707 708 r = WaitForSingleObject( ov.hEvent, 1000 ); 709 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 710 711 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); 712 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); 713 714 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 715 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 716 717 CloseHandle(hdir); 718 719 r = RemoveDirectoryW( path ); 720 ok( r == TRUE, "failed to remove directory\n"); 721} 722 723static void test_readdirectorychanges_filedir(void) 724{ 725 NTSTATUS r; 726 HANDLE hdir, hfile; 727 char buffer[0x1000]; 728 DWORD fflags, filter = 0; 729 OVERLAPPED ov; 730 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH]; 731 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 732 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 733 static const WCHAR szFoo[] = { '\\','f','o','o',0 }; 734 PFILE_NOTIFY_INFORMATION pfni; 735 736 r = GetTempPathW( MAX_PATH, path ); 737 ok( r != 0, "temp path failed\n"); 738 739 lstrcatW( path, szBoo ); 740 lstrcpyW( subdir, path ); 741 lstrcatW( subdir, szHoo ); 742 743 lstrcpyW( file, path ); 744 lstrcatW( file, szFoo ); 745 746 DeleteFileW( file ); 747 RemoveDirectoryW( subdir ); 748 RemoveDirectoryW( path ); 749 750 r = CreateDirectoryW(path, NULL); 751 ok( r == TRUE, "failed to create directory\n"); 752 753 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED; 754 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 755 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 756 OPEN_EXISTING, fflags, NULL); 757 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 758 759 ov.hEvent = CreateEventW( NULL, 0, 0, NULL ); 760 761 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 762 763 r = ReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL); 764 ok(r==TRUE, "should return true\n"); 765 766 r = WaitForSingleObject( ov.hEvent, 10 ); 767 ok( r == WAIT_TIMEOUT, "should timeout\n" ); 768 769 r = CreateDirectoryW( subdir, NULL ); 770 ok( r == TRUE, "failed to create directory\n"); 771 772 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL ); 773 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n"); 774 ok( CloseHandle(hfile), "failed to close file\n"); 775 776 r = WaitForSingleObject( ov.hEvent, 1000 ); 777 ok( r == WAIT_OBJECT_0, "event should be ready\n" ); 778 779 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 780 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); 781 782 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 783 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 784 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" ); 785 ok( pfni->FileNameLength == 6, "len wrong\n" ); 786 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" ); 787 788 r = DeleteFileW( file ); 789 ok( r == TRUE, "failed to delete file\n"); 790 791 r = RemoveDirectoryW( subdir ); 792 ok( r == TRUE, "failed to remove directory\n"); 793 794 CloseHandle(hdir); 795 796 r = RemoveDirectoryW( path ); 797 ok( r == TRUE, "failed to remove directory\n"); 798} 799 800static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov) 801{ 802 ok(error == 0, "ReadDirectoryChangesW error %ld\n", error); 803 ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n"); 804} 805 806static void test_readdirectorychanges_cr(void) 807{ 808 static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 }; 809 static const WCHAR szDir[] = { 'd','i','r',0 }; 810 static const WCHAR szFile[] = { 'f','i','l','e',0 }; 811 static const WCHAR szBackslash[] = { '\\',0 }; 812 813 WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH]; 814 FILE_NOTIFY_INFORMATION fni[1024], *fni_next; 815 OVERLAPPED ov; 816 HANDLE hdir, hfile; 817 NTSTATUS r; 818 819 r = GetTempPathW(MAX_PATH, path); 820 ok(r != 0, "temp path failed\n"); 821 822 lstrcatW(path, szBoo); 823 lstrcpyW(dir, path); 824 lstrcatW(dir, szDir); 825 lstrcpyW(file, path); 826 lstrcatW(file, szFile); 827 lstrcpyW(sub_file, dir); 828 lstrcatW(sub_file, szBackslash); 829 lstrcatW(sub_file, szFile); 830 831 DeleteFileW(file); 832 RemoveDirectoryW(dir); 833 RemoveDirectoryW(path); 834 835 r = CreateDirectoryW(path, NULL); 836 ok(r == TRUE, "failed to create directory\n"); 837 838 hdir = CreateFileW(path, GENERIC_READ, 839 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 840 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 841 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 842 843 memset(&ov, 0, sizeof(ov)); 844 ov.hEvent = (void*)0xdeadbeef; 845 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 846 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 847 ok(r == TRUE, "ReadDirectoryChangesW failed\n"); 848 849 hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); 850 ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n"); 851 CloseHandle(hfile); 852 853 r = SleepEx(1000, TRUE); 854 ok(r != 0, "failed to receive file creation event\n"); 855 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 856 ok(fni->Action == FILE_ACTION_ADDED, "Action = %ld\n", fni->Action); 857 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 858 "FileNameLength = %ld\n", fni->FileNameLength); 859 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 860 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 861 862 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 863 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 864 ok(r == TRUE, "ReadDirectoryChangesW failed\n"); 865 866 /* This event will not be reported */ 867 r = CreateDirectoryW(dir, NULL); 868 ok(r == TRUE, "failed to create directory\n"); 869 870 r = MoveFileW(file, sub_file); 871 ok(r == TRUE, "failed to move file\n"); 872 873 r = SleepEx(1000, TRUE); 874 ok(r != 0, "failed to receive file move event\n"); 875 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 876 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %ld\n", fni->Action); 877 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 878 "FileNameLength = %ld\n", fni->FileNameLength); 879 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 880 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 881 882 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 883 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 884 ok(r == TRUE, "ReadDirectoryChangesW failed\n"); 885 886 r = MoveFileW(sub_file, file); 887 ok(r == TRUE, "failed to move file\n"); 888 889 r = SleepEx(1000, TRUE); 890 ok(r != 0, "failed to receive file move event\n"); 891 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 892 ok(fni->Action == FILE_ACTION_ADDED, "Action = %ld\n", fni->Action); 893 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 894 "FileNameLength = %ld\n", fni->FileNameLength); 895 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 896 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 897 898 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 899 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 900 ok(r == TRUE, "ReadDirectoryChangesW failed\n"); 901 902 r = DeleteFileW(file); 903 ok(r == TRUE, "failed to delete file\n"); 904 905 r = SleepEx(1000, TRUE); 906 ok(r != 0, "failed to receive file removal event\n"); 907 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 908 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %ld\n", fni->Action); 909 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 910 "FileNameLength = %ld\n", fni->FileNameLength); 911 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 912 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 913 914 CloseHandle(hdir); 915 916 hdir = CreateFileW(path, GENERIC_READ, 917 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 918 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 919 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 920 921 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 922 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 923 ok(r == TRUE, "ReadDirectoryChangesW failed\n"); 924 925 r = MoveFileW(dir, file); 926 ok(r == TRUE, "failed to move directory\n"); 927 928 r = SleepEx(1000, TRUE); 929 ok(r != 0, "failed to receive directory move event\n"); 930 if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME) 931 { 932 ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %ld\n", fni->Action); 933 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 934 "FileNameLength = %ld\n", fni->FileNameLength); 935 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 936 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 937 ok(fni->NextEntryOffset != 0, "no next entry in movement event\n"); 938 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset); 939 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n"); 940 ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %ld\n", fni_next->Action); 941 ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 942 "FileNameLength = %ld\n", fni_next->FileNameLength); 943 ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 944 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR))); 945 } 946 else 947 { 948 todo_wine ok(0, "Expected rename event\n"); 949 950 if (fni->NextEntryOffset == 0) 951 { 952 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 953 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 954 ok(r == TRUE, "ReadDirectoryChangesW failed\n"); 955 956 r = SleepEx(1000, TRUE); 957 ok(r != 0, "failed to receive directory move event\n"); 958 } 959 } 960 961 r = CreateDirectoryW(dir, NULL); 962 ok(r == TRUE, "failed to create directory\n"); 963 964 r = RemoveDirectoryW(dir); 965 ok(r == TRUE, "failed to remove directory\n"); 966 967 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 968 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 969 ok(r == TRUE, "ReadDirectoryChangesW failed\n"); 970 971 r = SleepEx(1000, TRUE); 972 ok(r != 0, "failed to receive directory creation event\n"); 973 ok(fni->Action == FILE_ACTION_ADDED, "Action = %ld\n", fni->Action); 974 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 975 "FileNameLength = %ld\n", fni->FileNameLength); 976 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 977 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 978 if (fni->NextEntryOffset) 979 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset); 980 else 981 { 982 r = ReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 983 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 984 ok(r == TRUE, "ReadDirectoryChangesW failed\n"); 985 986 r = SleepEx(1000, TRUE); 987 ok(r != 0, "failed to receive directory removal event\n"); 988 fni_next = fni; 989 } 990 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n"); 991 ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %ld\n", fni_next->Action); 992 ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 993 "FileNameLength = %ld\n", fni_next->FileNameLength); 994 ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 995 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR))); 996 997 CloseHandle(hdir); 998 RemoveDirectoryW(file); 999 RemoveDirectoryW(path); 1000} 1001 1002static void test_ffcn_directory_overlap(void) 1003{ 1004 HANDLE parent_watch, child_watch, parent_thread, child_thread; 1005 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH]; 1006 char tempfile[MAX_PATH]; 1007 DWORD threadId; 1008 BOOL ret; 1009 1010 /* Setup directory hierarchy */ 1011 ret = GetTempPathA(MAX_PATH, workdir); 1012 ok((ret > 0) && (ret <= MAX_PATH), 1013 "GetTempPathA error: %ld\n", GetLastError()); 1014 1015 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile); 1016 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError()); 1017 ret = DeleteFileA(tempfile); 1018 ok(ret, "DeleteFileA error: %ld\n", GetLastError()); 1019 1020 lstrcpyA(parentdir, tempfile); 1021 ret = CreateDirectoryA(parentdir, NULL); 1022 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError()); 1023 1024 lstrcpyA(childdir, parentdir); 1025 lstrcatA(childdir, "\\c"); 1026 ret = CreateDirectoryA(childdir, NULL); 1027 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError()); 1028 1029 1030 /* When recursively watching overlapping directories, changes in child 1031 * should trigger notifications for both child and parent */ 1032 parent_thread = StartNotificationThread(parentdir, TRUE, 1033 FILE_NOTIFY_CHANGE_FILE_NAME); 1034 child_thread = StartNotificationThread(childdir, TRUE, 1035 FILE_NOTIFY_CHANGE_FILE_NAME); 1036 1037 /* Create a file in child */ 1038 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile); 1039 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError()); 1040 1041 /* Both watches should trigger */ 1042 ret = FinishNotificationThread(parent_thread); 1043 ok(ret, "Missed parent notification\n"); 1044 ret = FinishNotificationThread(child_thread); 1045 ok(ret, "Missed child notification\n"); 1046 1047 ret = DeleteFileA(tempfile); 1048 ok(ret, "DeleteFileA error: %ld\n", GetLastError()); 1049 1050 1051 /* Removing a recursive parent watch should not affect child watches. Doing 1052 * so used to crash wineserver. */ 1053 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE, 1054 FILE_NOTIFY_CHANGE_FILE_NAME); 1055 ok(parent_watch != INVALID_HANDLE_VALUE, 1056 "FindFirstChangeNotification error: %ld\n", GetLastError()); 1057 child_watch = FindFirstChangeNotificationA(childdir, TRUE, 1058 FILE_NOTIFY_CHANGE_FILE_NAME); 1059 ok(child_watch != INVALID_HANDLE_VALUE, 1060 "FindFirstChangeNotification error: %ld\n", GetLastError()); 1061 1062 ret = FindCloseChangeNotification(parent_watch); 1063 ok(ret, "FindCloseChangeNotification error: %ld\n", GetLastError()); 1064 1065 child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0, 1066 &threadId); 1067 ok(child_thread != NULL, "CreateThread error: %ld\n", GetLastError()); 1068 1069 /* Create a file in child */ 1070 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile); 1071 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError()); 1072 1073 /* Child watch should trigger */ 1074 ret = FinishNotificationThread(child_thread); 1075 ok(ret, "Missed child notification\n"); 1076 1077 /* clean up */ 1078 ret = DeleteFileA(tempfile); 1079 ok(ret, "DeleteFileA error: %ld\n", GetLastError()); 1080 1081 ret = RemoveDirectoryA(childdir); 1082 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError()); 1083 1084 ret = RemoveDirectoryA(parentdir); 1085 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError()); 1086} 1087 1088START_TEST(change) 1089{ 1090 test_ffcnMultipleThreads(); 1091 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the 1092 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be 1093 placed first. */ 1094 test_FindFirstChangeNotification(); 1095 test_ffcn(); 1096 test_readdirectorychanges(); 1097 test_readdirectorychanges_null(); 1098 test_readdirectorychanges_filedir(); 1099 test_readdirectorychanges_cr(); 1100 test_ffcn_directory_overlap(); 1101}