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 "precomp.h" 30 31 static DWORD CALLBACK NotificationThread(LPVOID arg) 32 { 33 HANDLE change = arg; 34 BOOL notified = FALSE; 35 BOOL ret = FALSE; 36 DWORD status; 37 38 status = WaitForSingleObject(change, 100); 39 40 if (status == WAIT_OBJECT_0 ) { 41 notified = TRUE; 42 FindNextChangeNotification(change); 43 } 44 45 ret = FindCloseChangeNotification(change); 46 ok( ret, "FindCloseChangeNotification error: %d\n", 47 GetLastError()); 48 49 ExitThread((DWORD)notified); 50 } 51 52 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags) 53 { 54 HANDLE change, thread; 55 DWORD threadId; 56 57 change = FindFirstChangeNotificationA(path, subtree, flags); 58 ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError()); 59 60 thread = CreateThread(NULL, 0, NotificationThread, change, 0, &threadId); 61 ok(thread != NULL, "CreateThread error: %d\n", GetLastError()); 62 63 return thread; 64 } 65 66 static DWORD FinishNotificationThread(HANDLE thread) 67 { 68 DWORD status, exitcode; 69 70 status = WaitForSingleObject(thread, 5000); 71 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\n", status, GetLastError()); 72 73 ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n"); 74 CloseHandle(thread); 75 76 return exitcode; 77 } 78 79 static void test_FindFirstChangeNotification(void) 80 { 81 HANDLE change, file, thread; 82 DWORD attributes, count; 83 BOOL ret; 84 85 char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH]; 86 char filename1[MAX_PATH], filename2[MAX_PATH]; 87 static const char prefix[] = "FCN"; 88 char buffer[2048]; 89 90 /* pathetic checks */ 91 92 change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 93 ok(change == INVALID_HANDLE_VALUE, "Expected INVALID_HANDLE_VALUE, got %p\n", change); 94 ok(GetLastError() == ERROR_FILE_NOT_FOUND || 95 GetLastError() == ERROR_NO_MORE_FILES, /* win95 */ 96 "FindFirstChangeNotification error: %d\n", GetLastError()); 97 98 if (0) /* This documents win2k behavior. It crashes on win98. */ 99 { 100 change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 101 ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND, 102 "FindFirstChangeNotification error: %d\n", GetLastError()); 103 } 104 105 ret = FindNextChangeNotification(NULL); 106 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n", 107 GetLastError()); 108 109 ret = FindCloseChangeNotification(NULL); 110 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n", 111 GetLastError()); 112 113 ret = GetTempPathA(MAX_PATH, workdir); 114 ok(ret, "GetTempPathA error: %d\n", GetLastError()); 115 116 lstrcatA(workdir, "testFileChangeNotification"); 117 118 ret = CreateDirectoryA(workdir, NULL); 119 ok(ret, "CreateDirectoryA error: %d\n", GetLastError()); 120 121 ret = GetTempFileNameA(workdir, prefix, 0, filename1); 122 ok(ret, "GetTempFileNameA error: %d\n", GetLastError()); 123 124 file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 125 FILE_ATTRIBUTE_NORMAL, 0); 126 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError()); 127 ret = CloseHandle(file); 128 ok( ret, "CloseHandle error: %d\n", GetLastError()); 129 130 /* Try to register notification for a file. win98 and win2k behave differently here */ 131 change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 132 ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY || 133 GetLastError() == ERROR_FILE_NOT_FOUND), 134 "FindFirstChangeNotification error: %d\n", GetLastError()); 135 136 lstrcpyA(dirname1, filename1); 137 lstrcatA(dirname1, "dir"); 138 139 lstrcpyA(dirname2, dirname1); 140 lstrcatA(dirname2, "new"); 141 142 ret = CreateDirectoryA(dirname1, NULL); 143 ok(ret, "CreateDirectoryA error: %d\n", GetLastError()); 144 145 /* What if we move the directory we registered notification for? */ 146 thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 147 ret = MoveFileA(dirname1, dirname2); 148 ok(ret, "MoveFileA error: %d\n", GetLastError()); 149 /* win9x and win2k behave differently here, don't check result */ 150 FinishNotificationThread(thread); 151 152 /* What if we remove the directory we registered notification for? */ 153 thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 154 ret = RemoveDirectoryA(dirname2); 155 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError()); 156 /* win9x and win2k behave differently here, don't check result */ 157 FinishNotificationThread(thread); 158 159 /* functional checks */ 160 161 /* Create a directory */ 162 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 163 ret = CreateDirectoryA(dirname1, NULL); 164 ok(ret, "CreateDirectoryA error: %d\n", GetLastError()); 165 ok(FinishNotificationThread(thread), "Missed notification\n"); 166 167 /* Rename a directory */ 168 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 169 ret = MoveFileA(dirname1, dirname2); 170 ok(ret, "MoveFileA error: %d\n", GetLastError()); 171 ok(FinishNotificationThread(thread), "Missed notification\n"); 172 173 /* Delete a directory */ 174 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME); 175 ret = RemoveDirectoryA(dirname2); 176 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError()); 177 ok(FinishNotificationThread(thread), "Missed notification\n"); 178 179 lstrcpyA(filename2, filename1); 180 lstrcatA(filename2, "new"); 181 182 /* Rename a file */ 183 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 184 ret = MoveFileA(filename1, filename2); 185 ok(ret, "MoveFileA error: %d\n", GetLastError()); 186 ok(FinishNotificationThread(thread), "Missed notification\n"); 187 188 /* Delete a file */ 189 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 190 ret = DeleteFileA(filename2); 191 ok(ret, "DeleteFileA error: %d\n", GetLastError()); 192 ok(FinishNotificationThread(thread), "Missed notification\n"); 193 194 /* Create a file */ 195 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); 196 file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 197 FILE_ATTRIBUTE_NORMAL, 0); 198 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError()); 199 ret = CloseHandle(file); 200 ok( ret, "CloseHandle error: %d\n", GetLastError()); 201 ok(FinishNotificationThread(thread), "Missed notification\n"); 202 203 attributes = GetFileAttributesA(filename2); 204 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError()); 205 attributes &= FILE_ATTRIBUTE_READONLY; 206 207 /* Change file attributes */ 208 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES); 209 ret = SetFileAttributesA(filename2, attributes); 210 ok(ret, "SetFileAttributesA error: %d\n", GetLastError()); 211 ok(FinishNotificationThread(thread), "Missed notification\n"); 212 213 /* Change last write time by writing to a file */ 214 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE); 215 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 216 FILE_ATTRIBUTE_NORMAL, 0); 217 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError()); 218 memset(buffer, 0, sizeof(buffer)); 219 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL); 220 ok(ret && count == sizeof(buffer), "WriteFile error: %d\n", GetLastError()); 221 ret = CloseHandle(file); 222 ok( ret, "CloseHandle error: %d\n", GetLastError()); 223 ok(FinishNotificationThread(thread), "Missed notification\n"); 224 225 /* Change file size by truncating a file */ 226 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE); 227 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 228 FILE_ATTRIBUTE_NORMAL, 0); 229 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError()); 230 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL); 231 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %d\n", GetLastError()); 232 ret = CloseHandle(file); 233 ok( ret, "CloseHandle error: %d\n", GetLastError()); 234 ok(FinishNotificationThread(thread), "Missed notification\n"); 235 236 /* clean up */ 237 238 ret = DeleteFileA(filename2); 239 ok(ret, "DeleteFileA error: %d\n", GetLastError()); 240 241 ret = RemoveDirectoryA(workdir); 242 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError()); 243 } 244 245 /* this test concentrates more on the wait behaviour of the handle */ 246 static void test_ffcn(void) 247 { 248 DWORD filter; 249 HANDLE handle; 250 LONG r; 251 WCHAR path[MAX_PATH], subdir[MAX_PATH]; 252 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 253 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 254 255 SetLastError(0xdeadbeef); 256 r = GetTempPathW( MAX_PATH, path ); 257 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) 258 { 259 win_skip("GetTempPathW is not implemented\n"); 260 return; 261 } 262 ok( r != 0, "temp path failed\n"); 263 if (!r) 264 return; 265 266 lstrcatW( path, szBoo ); 267 lstrcpyW( subdir, path ); 268 lstrcatW( subdir, szHoo ); 269 270 RemoveDirectoryW( subdir ); 271 RemoveDirectoryW( path ); 272 273 r = CreateDirectoryW(path, NULL); 274 ok( r == TRUE, "failed to create directory\n"); 275 276 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 277 filter |= FILE_NOTIFY_CHANGE_DIR_NAME; 278 279 handle = FindFirstChangeNotificationW( path, 1, filter); 280 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n"); 281 282 r = WaitForSingleObject( handle, 0 ); 283 ok( r == STATUS_TIMEOUT, "should time out\n"); 284 285 r = CreateDirectoryW( subdir, NULL ); 286 ok( r == TRUE, "failed to create subdir\n"); 287 288 r = WaitForSingleObject( handle, 0 ); 289 ok( r == WAIT_OBJECT_0, "should be ready\n"); 290 291 r = WaitForSingleObject( handle, 0 ); 292 ok( r == WAIT_OBJECT_0, "should be ready\n"); 293 294 r = FindNextChangeNotification(handle); 295 ok( r == TRUE, "find next failed\n"); 296 297 r = WaitForSingleObject( handle, 0 ); 298 ok( r == STATUS_TIMEOUT, "should time out\n"); 299 300 r = RemoveDirectoryW( subdir ); 301 ok( r == TRUE, "failed to remove subdir\n"); 302 303 r = WaitForSingleObject( handle, 0 ); 304 ok( r == WAIT_OBJECT_0, "should be ready\n"); 305 306 r = WaitForSingleObject( handle, 0 ); 307 ok( r == WAIT_OBJECT_0, "should be ready\n"); 308 309 r = FindNextChangeNotification(handle); 310 ok( r == TRUE, "find next failed\n"); 311 312 r = FindNextChangeNotification(handle); 313 ok( r == TRUE, "find next failed\n"); 314 315 r = FindCloseChangeNotification(handle); 316 ok( r == TRUE, "should succeed\n"); 317 318 r = RemoveDirectoryW( path ); 319 ok( r == TRUE, "failed to remove dir\n"); 320 } 321 322 /* this test concentrates on the wait behavior when multiple threads are 323 * waiting on a change notification handle. */ 324 static void test_ffcnMultipleThreads(void) 325 { 326 LONG r; 327 DWORD filter, threadId, status, exitcode; 328 HANDLE handles[2]; 329 char path[MAX_PATH]; 330 331 r = GetTempPathA(MAX_PATH, path); 332 ok(r, "GetTempPathA error: %d\n", GetLastError()); 333 334 lstrcatA(path, "ffcnTestMultipleThreads"); 335 336 RemoveDirectoryA(path); 337 338 r = CreateDirectoryA(path, NULL); 339 ok(r, "CreateDirectoryA error: %d\n", GetLastError()); 340 341 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 342 filter |= FILE_NOTIFY_CHANGE_DIR_NAME; 343 344 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter); 345 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError()); 346 347 /* Test behavior if a waiting thread holds the last reference to a change 348 * directory object with an empty wine user APC queue for this thread (bug #7286) */ 349 350 /* Create our notification thread */ 351 handles[1] = CreateThread(NULL, 0, NotificationThread, handles[0], 0, 352 &threadId); 353 ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError()); 354 355 status = WaitForMultipleObjects(2, handles, FALSE, 5000); 356 ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError()); 357 ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n"); 358 359 /* Clean up */ 360 r = RemoveDirectoryA( path ); 361 ok( r == TRUE, "failed to remove dir\n"); 362 } 363 364 static BOOL (WINAPI *pReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD, 365 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE); 366 367 static void test_readdirectorychanges(void) 368 { 369 HANDLE hdir; 370 char buffer[0x1000]; 371 DWORD fflags, filter = 0, r, dwCount; 372 OVERLAPPED ov; 373 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH]; 374 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 375 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 376 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 }; 377 PFILE_NOTIFY_INFORMATION pfni; 378 379 if (!pReadDirectoryChangesW) 380 { 381 win_skip("ReadDirectoryChangesW is not available\n"); 382 return; 383 } 384 385 SetLastError(0xdeadbeef); 386 r = GetTempPathW( MAX_PATH, path ); 387 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) 388 { 389 win_skip("GetTempPathW is not implemented\n"); 390 return; 391 } 392 ok( r != 0, "temp path failed\n"); 393 if (!r) 394 return; 395 396 lstrcatW( path, szBoo ); 397 lstrcpyW( subdir, path ); 398 lstrcatW( subdir, szHoo ); 399 400 lstrcpyW( subsubdir, path ); 401 lstrcatW( subsubdir, szGa ); 402 403 RemoveDirectoryW( subsubdir ); 404 RemoveDirectoryW( subdir ); 405 RemoveDirectoryW( path ); 406 407 r = CreateDirectoryW(path, NULL); 408 ok( r == TRUE, "failed to create directory\n"); 409 410 SetLastError(0xd0b00b00); 411 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL); 412 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 413 ok(r==FALSE, "should return false\n"); 414 415 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED; 416 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 417 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 418 OPEN_EXISTING, fflags, NULL); 419 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 420 421 ov.hEvent = CreateEventW( NULL, 1, 0, NULL ); 422 423 SetLastError(0xd0b00b00); 424 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL); 425 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 426 ok(r==FALSE, "should return false\n"); 427 428 SetLastError(0xd0b00b00); 429 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL); 430 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 431 ok(r==FALSE, "should return false\n"); 432 433 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 434 filter |= FILE_NOTIFY_CHANGE_DIR_NAME; 435 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 436 filter |= FILE_NOTIFY_CHANGE_SIZE; 437 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 438 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 439 filter |= FILE_NOTIFY_CHANGE_CREATION; 440 filter |= FILE_NOTIFY_CHANGE_SECURITY; 441 442 SetLastError(0xd0b00b00); 443 ov.Internal = 0; 444 ov.InternalHigh = 0; 445 memset( buffer, 0, sizeof buffer ); 446 447 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL); 448 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 449 ok(r==FALSE, "should return false\n"); 450 451 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL); 452 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 453 ok(r==FALSE, "should return false\n"); 454 455 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL); 456 ok(r==TRUE, "should return true\n"); 457 458 r = WaitForSingleObject( ov.hEvent, 10 ); 459 ok( r == STATUS_TIMEOUT, "should timeout\n" ); 460 461 r = CreateDirectoryW( subdir, NULL ); 462 ok( r == TRUE, "failed to create directory\n"); 463 464 r = WaitForSingleObject( ov.hEvent, 1000 ); 465 ok( r == WAIT_OBJECT_0, "event should be ready\n" ); 466 467 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 468 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); 469 470 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 471 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 472 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" ); 473 ok( pfni->FileNameLength == 6, "len wrong\n" ); 474 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" ); 475 476 ResetEvent(ov.hEvent); 477 SetLastError(0xd0b00b00); 478 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL); 479 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 480 ok(r==FALSE, "should return false\n"); 481 482 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL); 483 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n"); 484 ok(r==FALSE, "should return false\n"); 485 486 filter = FILE_NOTIFY_CHANGE_SIZE; 487 488 SetEvent(ov.hEvent); 489 ov.Internal = 1; 490 ov.InternalHigh = 1; 491 S(U(ov)).Offset = 0; 492 S(U(ov)).OffsetHigh = 0; 493 memset( buffer, 0, sizeof buffer ); 494 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 495 ok(r==TRUE, "should return true\n"); 496 497 ok( (NTSTATUS)ov.Internal == STATUS_PENDING, "ov.Internal wrong\n"); 498 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n"); 499 500 r = WaitForSingleObject( ov.hEvent, 0 ); 501 ok( r == STATUS_TIMEOUT, "should timeout\n" ); 502 503 r = RemoveDirectoryW( subdir ); 504 ok( r == TRUE, "failed to remove directory\n"); 505 506 r = WaitForSingleObject( ov.hEvent, 1000 ); 507 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 508 509 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 510 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); 511 512 if ((NTSTATUS)ov.Internal == STATUS_SUCCESS) 513 { 514 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE ); 515 ok( r == TRUE, "getoverlappedresult failed\n"); 516 ok( dwCount == 0x12, "count wrong\n"); 517 } 518 519 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 520 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 521 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" ); 522 ok( pfni->FileNameLength == 6, "len wrong\n" ); 523 ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" ); 524 525 /* what happens if the buffer is too small? */ 526 r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL); 527 ok(r==TRUE, "should return true\n"); 528 529 r = CreateDirectoryW( subdir, NULL ); 530 ok( r == TRUE, "failed to create directory\n"); 531 532 r = WaitForSingleObject( ov.hEvent, 1000 ); 533 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 534 535 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); 536 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); 537 538 /* test the recursive watch */ 539 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 540 ok(r==TRUE, "should return true\n"); 541 542 r = CreateDirectoryW( subsubdir, NULL ); 543 ok( r == TRUE, "failed to create directory\n"); 544 545 r = WaitForSingleObject( ov.hEvent, 1000 ); 546 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 547 548 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 549 ok( ov.InternalHigh == 0x18 || ov.InternalHigh == 0x12 + 0x18, 550 "ov.InternalHigh wrong %lx\n", ov.InternalHigh); 551 552 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 553 if (pfni->NextEntryOffset) /* we may get a modified event on the parent dir */ 554 { 555 ok( pfni->NextEntryOffset == 0x12, "offset wrong %x\n", pfni->NextEntryOffset ); 556 ok( pfni->Action == FILE_ACTION_MODIFIED, "action wrong %d\n", pfni->Action ); 557 ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong\n" ); 558 ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n"); 559 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset); 560 } 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( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 594 dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer; 595 ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %lu/%u\n",ov.InternalHigh, dwCount ); 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 win_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 win_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 = CreateEventW( 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( (NTSTATUS)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( (NTSTATUS)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 win_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 = CreateEventW( NULL, 0, 0, NULL ); 748 749 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 750 751 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL); 752 ok(r==TRUE, "should return true\n"); 753 754 r = WaitForSingleObject( ov.hEvent, 10 ); 755 ok( r == WAIT_TIMEOUT, "should timeout\n" ); 756 757 r = CreateDirectoryW( subdir, NULL ); 758 ok( r == TRUE, "failed to create directory\n"); 759 760 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL ); 761 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n"); 762 ok( CloseHandle(hfile), "failed to close file\n"); 763 764 r = WaitForSingleObject( ov.hEvent, 1000 ); 765 ok( r == WAIT_OBJECT_0, "event should be ready\n" ); 766 767 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 768 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); 769 770 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 771 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 772 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" ); 773 ok( pfni->FileNameLength == 6, "len wrong\n" ); 774 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" ); 775 776 r = DeleteFileW( file ); 777 ok( r == TRUE, "failed to delete file\n"); 778 779 r = RemoveDirectoryW( subdir ); 780 ok( r == TRUE, "failed to remove directory\n"); 781 782 CloseHandle(hdir); 783 784 r = RemoveDirectoryW( path ); 785 ok( r == TRUE, "failed to remove directory\n"); 786 } 787 788 static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov) 789 { 790 ok(error == 0, "ReadDirectoryChangesW error %d\n", error); 791 ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n"); 792 } 793 794 static void test_readdirectorychanges_cr(void) 795 { 796 static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 }; 797 static const WCHAR szDir[] = { 'd','i','r',0 }; 798 static const WCHAR szFile[] = { 'f','i','l','e',0 }; 799 static const WCHAR szBackslash[] = { '\\',0 }; 800 801 WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH]; 802 FILE_NOTIFY_INFORMATION fni[1024], *fni_next; 803 OVERLAPPED ov; 804 HANDLE hdir, hfile; 805 NTSTATUS r; 806 807 if (!pReadDirectoryChangesW) 808 { 809 win_skip("ReadDirectoryChangesW is not available\n"); 810 return; 811 } 812 813 SetLastError(0xdeadbeef); 814 r = GetTempPathW(MAX_PATH, path); 815 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) 816 { 817 win_skip("GetTempPathW is not implemented\n"); 818 return; 819 } 820 ok(r != 0, "temp path failed\n"); 821 if (!r) 822 return; 823 824 lstrcatW(path, szBoo); 825 lstrcpyW(dir, path); 826 lstrcatW(dir, szDir); 827 lstrcpyW(file, path); 828 lstrcatW(file, szFile); 829 lstrcpyW(sub_file, dir); 830 lstrcatW(sub_file, szBackslash); 831 lstrcatW(sub_file, szFile); 832 833 DeleteFileW(file); 834 RemoveDirectoryW(dir); 835 RemoveDirectoryW(path); 836 837 r = CreateDirectoryW(path, NULL); 838 ok(r == TRUE, "failed to create directory\n"); 839 840 hdir = CreateFileW(path, GENERIC_READ, 841 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 842 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 843 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 844 845 memset(&ov, 0, sizeof(ov)); 846 ov.hEvent = (void*)0xdeadbeef; 847 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 848 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 849 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 850 851 hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); 852 ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n"); 853 CloseHandle(hfile); 854 855 r = SleepEx(1000, TRUE); 856 ok(r != 0, "failed to receive file creation event\n"); 857 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 858 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action); 859 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 860 "FileNameLength = %d\n", fni->FileNameLength); 861 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 862 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 863 864 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 865 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 866 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 867 868 /* This event will not be reported */ 869 r = CreateDirectoryW(dir, NULL); 870 ok(r == TRUE, "failed to create directory\n"); 871 872 r = MoveFileW(file, sub_file); 873 ok(r == TRUE, "failed to move file\n"); 874 875 r = SleepEx(1000, TRUE); 876 ok(r != 0, "failed to receive file move event\n"); 877 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 878 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action); 879 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 880 "FileNameLength = %d\n", fni->FileNameLength); 881 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 882 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 883 884 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 885 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 886 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 887 888 r = MoveFileW(sub_file, file); 889 ok(r == TRUE, "failed to move file\n"); 890 891 r = SleepEx(1000, TRUE); 892 ok(r != 0, "failed to receive file move event\n"); 893 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 894 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action); 895 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 896 "FileNameLength = %d\n", fni->FileNameLength); 897 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 898 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 899 900 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 901 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 902 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 903 904 r = DeleteFileW(file); 905 ok(r == TRUE, "failed to delete file\n"); 906 907 r = SleepEx(1000, TRUE); 908 ok(r != 0, "failed to receive file removal event\n"); 909 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 910 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action); 911 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 912 "FileNameLength = %d\n", fni->FileNameLength); 913 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 914 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 915 916 CloseHandle(hdir); 917 918 hdir = CreateFileW(path, GENERIC_READ, 919 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 920 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 921 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 922 923 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 924 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 925 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 926 927 r = MoveFileW(dir, file); 928 ok(r == TRUE, "failed to move directory\n"); 929 930 r = SleepEx(1000, TRUE); 931 ok(r != 0, "failed to receive directory move event\n"); 932 if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME) 933 { 934 ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %d\n", fni->Action); 935 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 936 "FileNameLength = %d\n", fni->FileNameLength); 937 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 938 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 939 ok(fni->NextEntryOffset != 0, "no next entry in movement event\n"); 940 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset); 941 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n"); 942 ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %d\n", fni_next->Action); 943 ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 944 "FileNameLength = %d\n", fni_next->FileNameLength); 945 ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 946 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR))); 947 } 948 else 949 { 950 todo_wine ok(0, "Expected rename event\n"); 951 952 if (fni->NextEntryOffset == 0) 953 { 954 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 955 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 956 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 957 958 r = SleepEx(1000, TRUE); 959 ok(r != 0, "failed to receive directory move event\n"); 960 } 961 } 962 963 r = CreateDirectoryW(dir, NULL); 964 ok(r == TRUE, "failed to create directory\n"); 965 966 r = RemoveDirectoryW(dir); 967 ok(r == TRUE, "failed to remove directory\n"); 968 969 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 970 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 971 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 972 973 r = SleepEx(1000, TRUE); 974 ok(r != 0, "failed to receive directory creation event\n"); 975 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action); 976 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 977 "FileNameLength = %d\n", fni->FileNameLength); 978 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 979 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 980 if (fni->NextEntryOffset) 981 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset); 982 else 983 { 984 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 985 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 986 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 987 988 r = SleepEx(1000, TRUE); 989 ok(r != 0, "failed to receive directory removal event\n"); 990 fni_next = fni; 991 } 992 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n"); 993 ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni_next->Action); 994 ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 995 "FileNameLength = %d\n", fni_next->FileNameLength); 996 ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 997 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR))); 998 999 CloseHandle(hdir); 1000 RemoveDirectoryW(file); 1001 RemoveDirectoryW(path); 1002 } 1003 1004 static void test_ffcn_directory_overlap(void) 1005 { 1006 HANDLE parent_watch, child_watch, parent_thread, child_thread; 1007 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH]; 1008 char tempfile[MAX_PATH]; 1009 DWORD threadId; 1010 BOOL ret; 1011 1012 /* Setup directory hierarchy */ 1013 ret = GetTempPathA(MAX_PATH, workdir); 1014 ok((ret > 0) && (ret <= MAX_PATH), 1015 "GetTempPathA error: %d\n", GetLastError()); 1016 1017 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile); 1018 ok(ret, "GetTempFileNameA error: %d\n", GetLastError()); 1019 ret = DeleteFileA(tempfile); 1020 ok(ret, "DeleteFileA error: %d\n", GetLastError()); 1021 1022 lstrcpyA(parentdir, tempfile); 1023 ret = CreateDirectoryA(parentdir, NULL); 1024 ok(ret, "CreateDirectoryA error: %d\n", GetLastError()); 1025 1026 lstrcpyA(childdir, parentdir); 1027 lstrcatA(childdir, "\\c"); 1028 ret = CreateDirectoryA(childdir, NULL); 1029 ok(ret, "CreateDirectoryA error: %d\n", GetLastError()); 1030 1031 1032 /* When recursively watching overlapping directories, changes in child 1033 * should trigger notifications for both child and parent */ 1034 parent_thread = StartNotificationThread(parentdir, TRUE, 1035 FILE_NOTIFY_CHANGE_FILE_NAME); 1036 child_thread = StartNotificationThread(childdir, TRUE, 1037 FILE_NOTIFY_CHANGE_FILE_NAME); 1038 1039 /* Create a file in child */ 1040 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile); 1041 ok(ret, "GetTempFileNameA error: %d\n", GetLastError()); 1042 1043 /* Both watches should trigger */ 1044 ret = FinishNotificationThread(parent_thread); 1045 ok(ret, "Missed parent notification\n"); 1046 ret = FinishNotificationThread(child_thread); 1047 ok(ret, "Missed child notification\n"); 1048 1049 ret = DeleteFileA(tempfile); 1050 ok(ret, "DeleteFileA error: %d\n", GetLastError()); 1051 1052 1053 /* Removing a recursive parent watch should not affect child watches. Doing 1054 * so used to crash wineserver. */ 1055 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE, 1056 FILE_NOTIFY_CHANGE_FILE_NAME); 1057 ok(parent_watch != INVALID_HANDLE_VALUE, 1058 "FindFirstChangeNotification error: %d\n", GetLastError()); 1059 child_watch = FindFirstChangeNotificationA(childdir, TRUE, 1060 FILE_NOTIFY_CHANGE_FILE_NAME); 1061 ok(child_watch != INVALID_HANDLE_VALUE, 1062 "FindFirstChangeNotification error: %d\n", GetLastError()); 1063 1064 ret = FindCloseChangeNotification(parent_watch); 1065 ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError()); 1066 1067 child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0, 1068 &threadId); 1069 ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError()); 1070 1071 /* Create a file in child */ 1072 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile); 1073 ok(ret, "GetTempFileNameA error: %d\n", GetLastError()); 1074 1075 /* Child watch should trigger */ 1076 ret = FinishNotificationThread(child_thread); 1077 ok(ret, "Missed child notification\n"); 1078 1079 /* clean up */ 1080 ret = DeleteFileA(tempfile); 1081 ok(ret, "DeleteFileA error: %d\n", GetLastError()); 1082 1083 ret = RemoveDirectoryA(childdir); 1084 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError()); 1085 1086 ret = RemoveDirectoryA(parentdir); 1087 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError()); 1088 } 1089 1090 START_TEST(change) 1091 { 1092 HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); 1093 pReadDirectoryChangesW = (void *)GetProcAddress(hkernel32, "ReadDirectoryChangesW"); 1094 1095 test_ffcnMultipleThreads(); 1096 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the 1097 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be 1098 placed first. */ 1099 test_FindFirstChangeNotification(); 1100 test_ffcn(); 1101 test_readdirectorychanges(); 1102 test_readdirectorychanges_null(); 1103 test_readdirectorychanges_filedir(); 1104 test_readdirectorychanges_cr(); 1105 test_ffcn_directory_overlap(); 1106 } 1107