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 = 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: %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, change, 0, &threadId); 69 ok(thread != NULL, "CreateThread error: %d\n", GetLastError()); 70 71 return thread; 72 } 73 74 static DWORD FinishNotificationThread(HANDLE thread) 75 { 76 DWORD status, exitcode; 77 78 status = WaitForSingleObject(thread, 5000); 79 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\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 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 win_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, handles[0], 0, 360 &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 static BOOL (WINAPI *pReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD, 373 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE); 374 375 static void test_readdirectorychanges(void) 376 { 377 HANDLE hdir; 378 char buffer[0x1000]; 379 DWORD fflags, filter = 0, r, dwCount; 380 OVERLAPPED ov; 381 WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH]; 382 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 383 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 384 static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 }; 385 PFILE_NOTIFY_INFORMATION pfni; 386 BOOL got_subdir_change = FALSE; 387 388 if (!pReadDirectoryChangesW) 389 { 390 win_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 win_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 = CreateEventW( 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( (NTSTATUS)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( (NTSTATUS)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( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 519 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); 520 521 if ((NTSTATUS)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( (NTSTATUS)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 while (1) 555 { 556 r = WaitForSingleObject( ov.hEvent, 1000 ); 557 ok(r == WAIT_OBJECT_0, "should be ready\n" ); 558 if (r == WAIT_TIMEOUT) break; 559 560 ok((NTSTATUS) ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 561 562 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 563 while (1) 564 { 565 /* We might get one or more modified events on the parent dir */ 566 if (pfni->Action == FILE_ACTION_MODIFIED) 567 { 568 ok(pfni->FileNameLength == 3 * sizeof(WCHAR), "len wrong\n" ); 569 ok(!memcmp(pfni->FileName, &szGa[1], 3 * sizeof(WCHAR)), "name wrong\n"); 570 } 571 else 572 { 573 ok(pfni->Action == FILE_ACTION_ADDED, "action wrong\n"); 574 ok(pfni->FileNameLength == 6 * sizeof(WCHAR), "len wrong\n" ); 575 ok(!memcmp(pfni->FileName, &szGa[1], 6 * sizeof(WCHAR)), "name wrong\n"); 576 got_subdir_change = TRUE; 577 } 578 if (!pfni->NextEntryOffset) break; 579 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset); 580 } 581 582 if (got_subdir_change) break; 583 584 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 585 ok(r==TRUE, "should return true\n"); 586 } 587 ok(got_subdir_change, "didn't get subdir change\n"); 588 589 r = RemoveDirectoryW( subsubdir ); 590 ok( r == TRUE, "failed to remove directory\n"); 591 592 ov.Internal = 1; 593 ov.InternalHigh = 1; 594 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 595 ok(r==TRUE, "should return true\n"); 596 597 r = RemoveDirectoryW( subdir ); 598 ok( r == TRUE, "failed to remove directory\n"); 599 600 r = WaitForSingleObject( ov.hEvent, 1000 ); 601 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 602 603 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 604 /* we may get a notification for the parent dir too */ 605 if (pfni->Action == FILE_ACTION_MODIFIED && pfni->NextEntryOffset) 606 { 607 ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength ); 608 ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n" ); 609 pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset); 610 } 611 ok( pfni->NextEntryOffset == 0, "offset wrong %u\n", pfni->NextEntryOffset ); 612 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong %u\n", pfni->Action ); 613 ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength ); 614 ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" ); 615 616 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 617 dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer; 618 ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %lu/%u\n",ov.InternalHigh, dwCount ); 619 620 CloseHandle(hdir); 621 622 r = RemoveDirectoryW( path ); 623 ok( r == TRUE, "failed to remove directory\n"); 624 } 625 626 /* show the behaviour when a null buffer is passed */ 627 static void test_readdirectorychanges_null(void) 628 { 629 NTSTATUS r; 630 HANDLE hdir; 631 char buffer[0x1000]; 632 DWORD fflags, filter = 0; 633 OVERLAPPED ov; 634 WCHAR path[MAX_PATH], subdir[MAX_PATH]; 635 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 636 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 637 PFILE_NOTIFY_INFORMATION pfni; 638 639 if (!pReadDirectoryChangesW) 640 { 641 win_skip("ReadDirectoryChangesW is not available\n"); 642 return; 643 } 644 SetLastError(0xdeadbeef); 645 r = GetTempPathW( MAX_PATH, path ); 646 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) 647 { 648 win_skip("GetTempPathW is not implemented\n"); 649 return; 650 } 651 ok( r != 0, "temp path failed\n"); 652 if (!r) 653 return; 654 655 lstrcatW( path, szBoo ); 656 lstrcpyW( subdir, path ); 657 lstrcatW( subdir, szHoo ); 658 659 RemoveDirectoryW( subdir ); 660 RemoveDirectoryW( path ); 661 662 r = CreateDirectoryW(path, NULL); 663 ok( r == TRUE, "failed to create directory\n"); 664 665 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED; 666 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 667 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 668 OPEN_EXISTING, fflags, NULL); 669 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 670 671 ov.hEvent = CreateEventW( NULL, 1, 0, NULL ); 672 673 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 674 filter |= FILE_NOTIFY_CHANGE_DIR_NAME; 675 676 SetLastError(0xd0b00b00); 677 ov.Internal = 0; 678 ov.InternalHigh = 0; 679 memset( buffer, 0, sizeof buffer ); 680 681 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL); 682 ok(r==TRUE, "should return true\n"); 683 684 r = WaitForSingleObject( ov.hEvent, 0 ); 685 ok( r == STATUS_TIMEOUT, "should timeout\n" ); 686 687 r = CreateDirectoryW( subdir, NULL ); 688 ok( r == TRUE, "failed to create directory\n"); 689 690 r = WaitForSingleObject( ov.hEvent, 0 ); 691 ok( r == WAIT_OBJECT_0, "event should be ready\n" ); 692 693 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); 694 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); 695 696 ov.Internal = 0; 697 ov.InternalHigh = 0; 698 S(U(ov)).Offset = 0; 699 S(U(ov)).OffsetHigh = 0; 700 memset( buffer, 0, sizeof buffer ); 701 702 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); 703 ok(r==TRUE, "should return true\n"); 704 705 r = WaitForSingleObject( ov.hEvent, 0 ); 706 ok( r == STATUS_TIMEOUT, "should timeout\n" ); 707 708 r = RemoveDirectoryW( subdir ); 709 ok( r == TRUE, "failed to remove directory\n"); 710 711 r = WaitForSingleObject( ov.hEvent, 1000 ); 712 ok( r == WAIT_OBJECT_0, "should be ready\n" ); 713 714 ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); 715 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); 716 717 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 718 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 719 720 CloseHandle(hdir); 721 722 r = RemoveDirectoryW( path ); 723 ok( r == TRUE, "failed to remove directory\n"); 724 } 725 726 static void test_readdirectorychanges_filedir(void) 727 { 728 NTSTATUS r; 729 HANDLE hdir, hfile; 730 char buffer[0x1000]; 731 DWORD fflags, filter = 0; 732 OVERLAPPED ov; 733 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH]; 734 static const WCHAR szBoo[] = { '\\','b','o','o',0 }; 735 static const WCHAR szHoo[] = { '\\','h','o','o',0 }; 736 static const WCHAR szFoo[] = { '\\','f','o','o',0 }; 737 PFILE_NOTIFY_INFORMATION pfni; 738 739 SetLastError(0xdeadbeef); 740 r = GetTempPathW( MAX_PATH, path ); 741 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) 742 { 743 win_skip("GetTempPathW is not implemented\n"); 744 return; 745 } 746 ok( r != 0, "temp path failed\n"); 747 if (!r) 748 return; 749 750 lstrcatW( path, szBoo ); 751 lstrcpyW( subdir, path ); 752 lstrcatW( subdir, szHoo ); 753 754 lstrcpyW( file, path ); 755 lstrcatW( file, szFoo ); 756 757 DeleteFileW( file ); 758 RemoveDirectoryW( subdir ); 759 RemoveDirectoryW( path ); 760 761 r = CreateDirectoryW(path, NULL); 762 ok( r == TRUE, "failed to create directory\n"); 763 764 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED; 765 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 766 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 767 OPEN_EXISTING, fflags, NULL); 768 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 769 770 ov.hEvent = CreateEventW( NULL, 0, 0, NULL ); 771 772 filter = FILE_NOTIFY_CHANGE_FILE_NAME; 773 774 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL); 775 ok(r==TRUE, "should return true\n"); 776 777 r = WaitForSingleObject( ov.hEvent, 10 ); 778 ok( r == WAIT_TIMEOUT, "should timeout\n" ); 779 780 r = CreateDirectoryW( subdir, NULL ); 781 ok( r == TRUE, "failed to create directory\n"); 782 783 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL ); 784 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n"); 785 ok( CloseHandle(hfile), "failed to close file\n"); 786 787 r = WaitForSingleObject( ov.hEvent, 1000 ); 788 ok( r == WAIT_OBJECT_0, "event should be ready\n" ); 789 790 ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); 791 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); 792 793 pfni = (PFILE_NOTIFY_INFORMATION) buffer; 794 ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); 795 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" ); 796 ok( pfni->FileNameLength == 6, "len wrong\n" ); 797 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" ); 798 799 r = DeleteFileW( file ); 800 ok( r == TRUE, "failed to delete file\n"); 801 802 r = RemoveDirectoryW( subdir ); 803 ok( r == TRUE, "failed to remove directory\n"); 804 805 CloseHandle(hdir); 806 807 r = RemoveDirectoryW( path ); 808 ok( r == TRUE, "failed to remove directory\n"); 809 } 810 811 static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov) 812 { 813 ok(error == 0, "ReadDirectoryChangesW error %d\n", error); 814 ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n"); 815 } 816 817 static void test_readdirectorychanges_cr(void) 818 { 819 static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 }; 820 static const WCHAR szDir[] = { 'd','i','r',0 }; 821 static const WCHAR szFile[] = { 'f','i','l','e',0 }; 822 static const WCHAR szBackslash[] = { '\\',0 }; 823 824 WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH]; 825 FILE_NOTIFY_INFORMATION fni[1024], *fni_next; 826 OVERLAPPED ov; 827 HANDLE hdir, hfile; 828 NTSTATUS r; 829 830 if (!pReadDirectoryChangesW) 831 { 832 win_skip("ReadDirectoryChangesW is not available\n"); 833 return; 834 } 835 836 SetLastError(0xdeadbeef); 837 r = GetTempPathW(MAX_PATH, path); 838 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) 839 { 840 win_skip("GetTempPathW is not implemented\n"); 841 return; 842 } 843 ok(r != 0, "temp path failed\n"); 844 if (!r) 845 return; 846 847 lstrcatW(path, szBoo); 848 lstrcpyW(dir, path); 849 lstrcatW(dir, szDir); 850 lstrcpyW(file, path); 851 lstrcatW(file, szFile); 852 lstrcpyW(sub_file, dir); 853 lstrcatW(sub_file, szBackslash); 854 lstrcatW(sub_file, szFile); 855 856 DeleteFileW(file); 857 RemoveDirectoryW(dir); 858 RemoveDirectoryW(path); 859 860 r = CreateDirectoryW(path, NULL); 861 ok(r == TRUE, "failed to create directory\n"); 862 863 hdir = CreateFileW(path, GENERIC_READ, 864 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 865 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 866 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 867 868 memset(&ov, 0, sizeof(ov)); 869 ov.hEvent = (void*)0xdeadbeef; 870 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 871 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 872 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 873 874 hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); 875 ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n"); 876 CloseHandle(hfile); 877 878 r = SleepEx(1000, TRUE); 879 ok(r != 0, "failed to receive file creation event\n"); 880 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 881 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action); 882 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 883 "FileNameLength = %d\n", fni->FileNameLength); 884 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 885 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 886 887 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 888 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 889 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 890 891 /* This event will not be reported */ 892 r = CreateDirectoryW(dir, NULL); 893 ok(r == TRUE, "failed to create directory\n"); 894 895 r = MoveFileW(file, sub_file); 896 ok(r == TRUE, "failed to move file\n"); 897 898 r = SleepEx(1000, TRUE); 899 ok(r != 0, "failed to receive file move event\n"); 900 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 901 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action); 902 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 903 "FileNameLength = %d\n", fni->FileNameLength); 904 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 905 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 906 907 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 908 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 909 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 910 911 r = MoveFileW(sub_file, file); 912 ok(r == TRUE, "failed to move file\n"); 913 914 r = SleepEx(1000, TRUE); 915 ok(r != 0, "failed to receive file move event\n"); 916 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 917 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action); 918 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 919 "FileNameLength = %d\n", fni->FileNameLength); 920 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 921 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 922 923 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 924 FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr); 925 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 926 927 r = DeleteFileW(file); 928 ok(r == TRUE, "failed to delete file\n"); 929 930 r = SleepEx(1000, TRUE); 931 ok(r != 0, "failed to receive file removal event\n"); 932 ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n"); 933 ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action); 934 ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 935 "FileNameLength = %d\n", fni->FileNameLength); 936 ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 937 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 938 939 CloseHandle(hdir); 940 941 hdir = CreateFileW(path, GENERIC_READ, 942 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 943 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 944 ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n"); 945 946 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 947 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 948 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 949 950 r = MoveFileW(dir, file); 951 ok(r == TRUE, "failed to move directory\n"); 952 953 r = SleepEx(1000, TRUE); 954 ok(r != 0, "failed to receive directory move event\n"); 955 if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME) 956 { 957 ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %d\n", fni->Action); 958 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 959 "FileNameLength = %d\n", fni->FileNameLength); 960 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 961 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 962 ok(fni->NextEntryOffset != 0, "no next entry in movement event\n"); 963 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset); 964 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n"); 965 ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %d\n", fni_next->Action); 966 ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR), 967 "FileNameLength = %d\n", fni_next->FileNameLength); 968 ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)), 969 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR))); 970 } 971 else 972 { 973 todo_wine ok(0, "Expected rename event\n"); 974 975 if (fni->NextEntryOffset == 0) 976 { 977 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 978 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 979 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 980 981 r = SleepEx(1000, TRUE); 982 ok(r != 0, "failed to receive directory move event\n"); 983 } 984 } 985 986 r = CreateDirectoryW(dir, NULL); 987 ok(r == TRUE, "failed to create directory\n"); 988 989 r = RemoveDirectoryW(dir); 990 ok(r == TRUE, "failed to remove directory\n"); 991 992 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 993 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 994 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 995 996 r = SleepEx(1000, TRUE); 997 ok(r != 0, "failed to receive directory creation event\n"); 998 ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action); 999 ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 1000 "FileNameLength = %d\n", fni->FileNameLength); 1001 ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 1002 "FileName = %s\n", wine_dbgstr_wn(fni->FileName, fni->FileNameLength/sizeof(WCHAR))); 1003 if (fni->NextEntryOffset) 1004 fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset); 1005 else 1006 { 1007 r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE, 1008 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr); 1009 ok(r == TRUE, "pReadDirectoryChangesW failed\n"); 1010 1011 r = SleepEx(1000, TRUE); 1012 ok(r != 0, "failed to receive directory removal event\n"); 1013 fni_next = fni; 1014 } 1015 ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n"); 1016 ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni_next->Action); 1017 ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR), 1018 "FileNameLength = %d\n", fni_next->FileNameLength); 1019 ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)), 1020 "FileName = %s\n", wine_dbgstr_wn(fni_next->FileName, fni_next->FileNameLength/sizeof(WCHAR))); 1021 1022 CloseHandle(hdir); 1023 RemoveDirectoryW(file); 1024 RemoveDirectoryW(path); 1025 } 1026 1027 static void test_ffcn_directory_overlap(void) 1028 { 1029 HANDLE parent_watch, child_watch, parent_thread, child_thread; 1030 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH]; 1031 char tempfile[MAX_PATH]; 1032 DWORD threadId; 1033 BOOL ret; 1034 1035 /* Setup directory hierarchy */ 1036 ret = GetTempPathA(MAX_PATH, workdir); 1037 ok((ret > 0) && (ret <= MAX_PATH), 1038 "GetTempPathA error: %d\n", GetLastError()); 1039 1040 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile); 1041 ok(ret, "GetTempFileNameA error: %d\n", GetLastError()); 1042 ret = DeleteFileA(tempfile); 1043 ok(ret, "DeleteFileA error: %d\n", GetLastError()); 1044 1045 lstrcpyA(parentdir, tempfile); 1046 ret = CreateDirectoryA(parentdir, NULL); 1047 ok(ret, "CreateDirectoryA error: %d\n", GetLastError()); 1048 1049 lstrcpyA(childdir, parentdir); 1050 lstrcatA(childdir, "\\c"); 1051 ret = CreateDirectoryA(childdir, NULL); 1052 ok(ret, "CreateDirectoryA error: %d\n", GetLastError()); 1053 1054 1055 /* When recursively watching overlapping directories, changes in child 1056 * should trigger notifications for both child and parent */ 1057 parent_thread = StartNotificationThread(parentdir, TRUE, 1058 FILE_NOTIFY_CHANGE_FILE_NAME); 1059 child_thread = StartNotificationThread(childdir, TRUE, 1060 FILE_NOTIFY_CHANGE_FILE_NAME); 1061 1062 /* Create a file in child */ 1063 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile); 1064 ok(ret, "GetTempFileNameA error: %d\n", GetLastError()); 1065 1066 /* Both watches should trigger */ 1067 ret = FinishNotificationThread(parent_thread); 1068 ok(ret, "Missed parent notification\n"); 1069 ret = FinishNotificationThread(child_thread); 1070 ok(ret, "Missed child notification\n"); 1071 1072 ret = DeleteFileA(tempfile); 1073 ok(ret, "DeleteFileA error: %d\n", GetLastError()); 1074 1075 1076 /* Removing a recursive parent watch should not affect child watches. Doing 1077 * so used to crash wineserver. */ 1078 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE, 1079 FILE_NOTIFY_CHANGE_FILE_NAME); 1080 ok(parent_watch != INVALID_HANDLE_VALUE, 1081 "FindFirstChangeNotification error: %d\n", GetLastError()); 1082 child_watch = FindFirstChangeNotificationA(childdir, TRUE, 1083 FILE_NOTIFY_CHANGE_FILE_NAME); 1084 ok(child_watch != INVALID_HANDLE_VALUE, 1085 "FindFirstChangeNotification error: %d\n", GetLastError()); 1086 1087 ret = FindCloseChangeNotification(parent_watch); 1088 ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError()); 1089 1090 child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0, 1091 &threadId); 1092 ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError()); 1093 1094 /* Create a file in child */ 1095 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile); 1096 ok(ret, "GetTempFileNameA error: %d\n", GetLastError()); 1097 1098 /* Child watch should trigger */ 1099 ret = FinishNotificationThread(child_thread); 1100 ok(ret, "Missed child notification\n"); 1101 1102 /* clean up */ 1103 ret = DeleteFileA(tempfile); 1104 ok(ret, "DeleteFileA error: %d\n", GetLastError()); 1105 1106 ret = RemoveDirectoryA(childdir); 1107 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError()); 1108 1109 ret = RemoveDirectoryA(parentdir); 1110 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError()); 1111 } 1112 1113 START_TEST(change) 1114 { 1115 HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); 1116 pReadDirectoryChangesW = (void *)GetProcAddress(hkernel32, "ReadDirectoryChangesW"); 1117 1118 test_ffcnMultipleThreads(); 1119 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the 1120 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be 1121 placed first. */ 1122 test_FindFirstChangeNotification(); 1123 test_ffcn(); 1124 test_readdirectorychanges(); 1125 test_readdirectorychanges_null(); 1126 test_readdirectorychanges_filedir(); 1127 test_readdirectorychanges_cr(); 1128 test_ffcn_directory_overlap(); 1129 } 1130