1 /* 2 * PROJECT: shell32 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: Shell change notification 5 * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 6 */ 7 8 #include "precomp.h" 9 10 WINE_DEFAULT_DEBUG_CHANNEL(shcn); 11 12 CRITICAL_SECTION SHELL32_ChangenotifyCS; 13 14 // This function requests creation of the server window if it doesn't exist yet 15 static HWND 16 GetNotificationServer(BOOL bCreate) 17 { 18 static HWND s_hwndServer = NULL; 19 20 // use cache if any 21 if (s_hwndServer && IsWindow(s_hwndServer)) 22 return s_hwndServer; 23 24 // get the shell window 25 HWND hwndShell = GetShellWindow(); 26 if (hwndShell == NULL) 27 { 28 TRACE("GetShellWindow() returned NULL\n"); 29 return NULL; 30 } 31 32 // Get the window of the notification server that runs in explorer 33 HWND hwndServer = (HWND)SendMessageW(hwndShell, WM_DESKTOP_GET_CNOTIFY_SERVER, bCreate, 0); 34 if (!IsWindow(hwndServer)) 35 { 36 ERR("Unable to get server window\n"); 37 hwndServer = NULL; 38 } 39 40 // save and return 41 s_hwndServer = hwndServer; 42 return hwndServer; 43 } 44 45 // This function will be called from DllMain.DLL_PROCESS_ATTACH. 46 EXTERN_C void InitChangeNotifications(void) 47 { 48 InitializeCriticalSection(&SHELL32_ChangenotifyCS); 49 } 50 51 // This function will be called from DllMain.DLL_PROCESS_DETACH. 52 EXTERN_C void FreeChangeNotifications(void) 53 { 54 HWND hwndServer = GetNotificationServer(FALSE); 55 if (hwndServer) 56 SendMessageW(hwndServer, CN_UNREGISTER_PROCESS, GetCurrentProcessId(), 0); 57 DeleteCriticalSection(&SHELL32_ChangenotifyCS); 58 } 59 60 ////////////////////////////////////////////////////////////////////////////////////// 61 // There are two delivery methods: "old delivery method" and "new delivery method". 62 // 63 // The old delivery method creates a broker window in the caller process 64 // for message trampoline. The old delivery method is slow and deprecated. 65 // 66 // The new delivery method is enabled by SHCNRF_NewDelivery flag. 67 // With the new delivery method the server directly sends the delivery message. 68 69 // This class brokers all notifications that don't have the SHCNRF_NewDelivery flag 70 class CChangeNotifyBroker : 71 public CWindowImpl<CChangeNotifyBroker, CWindow, CWorkerTraits> 72 { 73 public: 74 CChangeNotifyBroker(HWND hwndClient, UINT uMsg) : 75 m_hwndClient(hwndClient), m_uMsg(uMsg) 76 { 77 } 78 79 // Message handlers 80 LRESULT OnBrokerNotification(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 81 { 82 return BrokerNotification((HANDLE)wParam, (DWORD)lParam); 83 } 84 85 void OnFinalMessage(HWND) 86 { 87 // The server will destroy this window. 88 // After the window gets destroyed we can delete this broker here. 89 delete this; 90 } 91 92 DECLARE_WND_CLASS_EX(L"WorkerW", 0, 0) 93 94 BEGIN_MSG_MAP(CChangeNotifyBroker) 95 MESSAGE_HANDLER(WM_BROKER_NOTIFICATION, OnBrokerNotification) 96 END_MSG_MAP() 97 98 private: 99 HWND m_hwndClient; 100 UINT m_uMsg; 101 102 BOOL BrokerNotification(HANDLE hTicket, DWORD dwOwnerPID) 103 { 104 // lock the ticket 105 PIDLIST_ABSOLUTE *ppidl = NULL; 106 LONG lEvent; 107 HANDLE hLock = SHChangeNotification_Lock(hTicket, dwOwnerPID, &ppidl, &lEvent); 108 if (hLock == NULL) 109 { 110 ERR("hLock is NULL\n"); 111 return FALSE; 112 } 113 114 // perform the delivery 115 TRACE("broker notifying: %p, 0x%x, %p, 0x%lx\n", 116 m_hwndClient, m_uMsg, ppidl, lEvent); 117 SendMessageW(m_hwndClient, m_uMsg, (WPARAM)ppidl, lEvent); 118 119 // unlock the ticket 120 SHChangeNotification_Unlock(hLock); 121 return TRUE; 122 } 123 }; 124 125 // This function creates a notification broker for old method. Used in SHChangeNotifyRegister. 126 static HWND 127 CreateNotificationBroker(HWND hwnd, UINT wMsg) 128 { 129 // Create a new broker. It will be freed when the window gets destroyed 130 CChangeNotifyBroker* pBroker = new CChangeNotifyBroker(hwnd, wMsg); 131 if (pBroker == NULL) 132 { 133 ERR("Out of memory\n"); 134 return NULL; 135 } 136 137 HWND hwndBroker = SHCreateDefaultWorkerWindow(); 138 if (hwndBroker == NULL) 139 { 140 ERR("hwndBroker == NULL\n"); 141 delete pBroker; 142 return NULL; 143 } 144 145 pBroker->SubclassWindow(hwndBroker); 146 return hwndBroker; 147 } 148 149 // This function creates a delivery ticket for shell change nofitication. 150 // Used in SHChangeNotify. 151 static HANDLE 152 CreateNotificationParam(LONG wEventId, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, 153 DWORD dwOwnerPID, DWORD dwTick) 154 { 155 // pidl1 and pidl2 have variable length. To store them into the delivery ticket, 156 // we have to consider the offsets and the sizes of pidl1 and pidl2. 157 DWORD cbPidl1 = 0, cbPidl2 = 0, ibOffset1 = 0, ibOffset2 = 0; 158 if (pidl1) 159 { 160 cbPidl1 = ILGetSize(pidl1); 161 ibOffset1 = DWORD_ALIGNMENT(sizeof(DELITICKET)); 162 } 163 if (pidl2) 164 { 165 cbPidl2 = ILGetSize(pidl2); 166 ibOffset2 = DWORD_ALIGNMENT(ibOffset1 + cbPidl1); 167 } 168 169 // allocate the delivery ticket 170 DWORD cbSize = ibOffset2 + cbPidl2; 171 HANDLE hTicket = SHAllocShared(NULL, cbSize, dwOwnerPID); 172 if (hTicket == NULL) 173 { 174 ERR("Out of memory\n"); 175 return NULL; 176 } 177 178 // lock the ticket 179 LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, TRUE); 180 if (pTicket == NULL) 181 { 182 ERR("SHLockSharedEx failed\n"); 183 SHFreeShared(hTicket, dwOwnerPID); 184 return NULL; 185 } 186 187 // populate the ticket 188 pTicket->dwMagic = DELITICKET_MAGIC; 189 pTicket->wEventId = wEventId; 190 pTicket->uFlags = uFlags; 191 pTicket->ibOffset1 = ibOffset1; 192 pTicket->ibOffset2 = ibOffset2; 193 if (pidl1) 194 memcpy((LPBYTE)pTicket + ibOffset1, pidl1, cbPidl1); 195 if (pidl2) 196 memcpy((LPBYTE)pTicket + ibOffset2, pidl2, cbPidl2); 197 198 // unlock the ticket and return 199 SHUnlockShared(pTicket); 200 return hTicket; 201 } 202 203 // This function creates a "handbag" by using a delivery ticket. 204 // The handbag is created in SHChangeNotification_Lock and used in OnBrokerNotification. 205 // hTicket is a ticket handle of a shared memory block and dwOwnerPID is 206 // the owner PID of the ticket. 207 static LPHANDBAG 208 DoGetHandbagFromTicket(HANDLE hTicket, DWORD dwOwnerPID) 209 { 210 // lock and validate the delivery ticket 211 LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, FALSE); 212 if (pTicket == NULL || pTicket->dwMagic != DELITICKET_MAGIC) 213 { 214 ERR("pTicket is invalid\n"); 215 SHUnlockShared(pTicket); 216 return NULL; 217 } 218 219 // allocate the handbag 220 LPHANDBAG pHandbag = (LPHANDBAG)LocalAlloc(LMEM_FIXED, sizeof(HANDBAG)); 221 if (pHandbag == NULL) 222 { 223 ERR("Out of memory\n"); 224 SHUnlockShared(pTicket); 225 return NULL; 226 } 227 228 // populate the handbag 229 pHandbag->dwMagic = HANDBAG_MAGIC; 230 pHandbag->pTicket = pTicket; 231 232 pHandbag->pidls[0] = pHandbag->pidls[1] = NULL; 233 if (pTicket->ibOffset1) 234 pHandbag->pidls[0] = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset1); 235 if (pTicket->ibOffset2) 236 pHandbag->pidls[1] = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset2); 237 238 return pHandbag; 239 } 240 241 // This function creates a registration entry in SHChangeNotifyRegister function. 242 static HANDLE 243 CreateRegistrationParam(ULONG nRegID, HWND hwnd, UINT wMsg, INT fSources, LONG fEvents, 244 LONG fRecursive, LPCITEMIDLIST pidl, DWORD dwOwnerPID, 245 HWND hwndBroker) 246 { 247 // pidl has variable length. To store it into the registration entry, 248 // we have to consider the length of pidl. 249 DWORD cbPidl = ILGetSize(pidl); 250 DWORD ibPidl = DWORD_ALIGNMENT(sizeof(REGENTRY)); 251 DWORD cbSize = ibPidl + cbPidl; 252 253 // create the registration entry and lock it 254 HANDLE hRegEntry = SHAllocShared(NULL, cbSize, dwOwnerPID); 255 if (hRegEntry == NULL) 256 { 257 ERR("Out of memory\n"); 258 return NULL; 259 } 260 LPREGENTRY pRegEntry = (LPREGENTRY)SHLockSharedEx(hRegEntry, dwOwnerPID, TRUE); 261 if (pRegEntry == NULL) 262 { 263 ERR("SHLockSharedEx failed\n"); 264 SHFreeShared(hRegEntry, dwOwnerPID); 265 return NULL; 266 } 267 268 // populate the registration entry 269 pRegEntry->dwMagic = REGENTRY_MAGIC; 270 pRegEntry->cbSize = cbSize; 271 pRegEntry->nRegID = nRegID; 272 pRegEntry->hwnd = hwnd; 273 pRegEntry->uMsg = wMsg; 274 pRegEntry->fSources = fSources; 275 pRegEntry->fEvents = fEvents; 276 pRegEntry->fRecursive = fRecursive; 277 pRegEntry->hwndBroker = hwndBroker; 278 pRegEntry->ibPidl = 0; 279 if (pidl) 280 { 281 pRegEntry->ibPidl = ibPidl; 282 memcpy((LPBYTE)pRegEntry + ibPidl, pidl, cbPidl); 283 } 284 285 // unlock and return 286 SHUnlockShared(pRegEntry); 287 return hRegEntry; 288 } 289 290 // This function is the body of SHChangeNotify function. 291 // It creates a delivery ticket and send CN_DELIVER_NOTIFICATION message to 292 // transport the change. 293 static void 294 CreateNotificationParamAndSend(LONG wEventId, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, 295 DWORD dwTick) 296 { 297 // get server window 298 HWND hwndServer = GetNotificationServer(FALSE); 299 if (hwndServer == NULL) 300 return; 301 302 // the ticket owner is the process of the notification server 303 DWORD pid; 304 GetWindowThreadProcessId(hwndServer, &pid); 305 306 // create a delivery ticket 307 HANDLE hTicket = CreateNotificationParam(wEventId, uFlags, pidl1, pidl2, pid, dwTick); 308 if (hTicket == NULL) 309 return; 310 311 TRACE("hTicket: %p, 0x%lx\n", hTicket, pid); 312 313 // send the ticket by using CN_DELIVER_NOTIFICATION 314 if (pid != GetCurrentProcessId() || 315 (uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH) 316 { 317 SendMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket, pid); 318 } 319 else 320 { 321 SendNotifyMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket, pid); 322 } 323 } 324 325 struct ALIAS_PIDL 326 { 327 INT csidl1; // from 328 INT csidl2; // to 329 LPITEMIDLIST pidl1; // from 330 LPITEMIDLIST pidl2; // to 331 WCHAR szPath1[MAX_PATH]; // from 332 WCHAR szPath2[MAX_PATH]; // to 333 }; 334 335 static ALIAS_PIDL AliasPIDLs[] = 336 { 337 { CSIDL_PERSONAL, CSIDL_PERSONAL }, 338 { CSIDL_DESKTOP, CSIDL_COMMON_DESKTOPDIRECTORY, }, 339 { CSIDL_DESKTOP, CSIDL_DESKTOPDIRECTORY }, 340 }; 341 342 static VOID DoInitAliasPIDLs(void) 343 { 344 static BOOL s_bInit = FALSE; 345 if (!s_bInit) 346 { 347 for (SIZE_T i = 0; i < _countof(AliasPIDLs); ++i) 348 { 349 ALIAS_PIDL *alias = &AliasPIDLs[i]; 350 351 SHGetSpecialFolderLocation(NULL, alias->csidl1, &alias->pidl1); 352 SHGetPathFromIDListW(alias->pidl1, alias->szPath1); 353 354 SHGetSpecialFolderLocation(NULL, alias->csidl2, &alias->pidl2); 355 SHGetPathFromIDListW(alias->pidl2, alias->szPath2); 356 } 357 s_bInit = TRUE; 358 } 359 } 360 361 static BOOL DoGetAliasPIDLs(LPITEMIDLIST apidls[2], PCIDLIST_ABSOLUTE pidl) 362 { 363 DoInitAliasPIDLs(); 364 365 apidls[0] = apidls[1] = NULL; 366 367 INT k = 0; 368 for (SIZE_T i = 0; i < _countof(AliasPIDLs); ++i) 369 { 370 const ALIAS_PIDL *alias = &AliasPIDLs[i]; 371 if (ILIsEqual(pidl, alias->pidl1)) 372 { 373 if (alias->csidl1 == alias->csidl2) 374 { 375 apidls[k++] = ILCreateFromPathW(alias->szPath2); 376 } 377 else 378 { 379 apidls[k++] = ILClone(alias->pidl2); 380 } 381 if (k >= 2) 382 break; 383 } 384 } 385 386 return k > 0; 387 } 388 389 /************************************************************************* 390 * SHChangeNotifyRegister [SHELL32.2] 391 */ 392 EXTERN_C ULONG WINAPI 393 SHChangeNotifyRegister(HWND hwnd, INT fSources, LONG wEventMask, UINT uMsg, 394 INT cItems, SHChangeNotifyEntry *lpItems) 395 { 396 HWND hwndServer, hwndBroker = NULL; 397 HANDLE hRegEntry; 398 INT iItem; 399 ULONG nRegID = INVALID_REG_ID; 400 DWORD dwOwnerPID; 401 LPREGENTRY pRegEntry; 402 403 TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p)\n", 404 hwnd, fSources, wEventMask, uMsg, cItems, lpItems); 405 406 // sanity check 407 if (wEventMask == 0 || cItems <= 0 || cItems > 0x7FFF || lpItems == NULL || 408 hwnd == NULL || !IsWindow(hwnd)) 409 { 410 return INVALID_REG_ID; 411 } 412 413 // request the window of the server 414 hwndServer = GetNotificationServer(TRUE); 415 if (hwndServer == NULL) 416 return INVALID_REG_ID; 417 418 // disable recursive interrupt in specific condition 419 if ((fSources & SHCNRF_RecursiveInterrupt) && !(fSources & SHCNRF_InterruptLevel)) 420 { 421 fSources &= ~SHCNRF_RecursiveInterrupt; 422 } 423 424 // if it is old delivery method, then create a broker window 425 if ((fSources & SHCNRF_NewDelivery) == 0) 426 { 427 hwndBroker = hwnd = CreateNotificationBroker(hwnd, uMsg); 428 uMsg = WM_BROKER_NOTIFICATION; 429 } 430 431 // The owner PID is the process ID of the server 432 GetWindowThreadProcessId(hwndServer, &dwOwnerPID); 433 434 EnterCriticalSection(&SHELL32_ChangenotifyCS); 435 for (iItem = 0; iItem < cItems; ++iItem) 436 { 437 // create a registration entry 438 hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask, 439 lpItems[iItem].fRecursive, lpItems[iItem].pidl, 440 dwOwnerPID, hwndBroker); 441 if (hRegEntry) 442 { 443 TRACE("CN_REGISTER: hwnd:%p, hRegEntry:%p, pid:0x%lx\n", 444 hwndServer, hRegEntry, dwOwnerPID); 445 446 // send CN_REGISTER to the server 447 SendMessageW(hwndServer, CN_REGISTER, (WPARAM)hRegEntry, dwOwnerPID); 448 449 // update nRegID 450 pRegEntry = (LPREGENTRY)SHLockSharedEx(hRegEntry, dwOwnerPID, FALSE); 451 if (pRegEntry) 452 { 453 nRegID = pRegEntry->nRegID; 454 SHUnlockShared(pRegEntry); 455 } 456 457 // free registration entry 458 SHFreeShared(hRegEntry, dwOwnerPID); 459 } 460 461 if (nRegID == INVALID_REG_ID) 462 { 463 ERR("Delivery failed\n"); 464 465 if (hwndBroker) 466 { 467 // destroy the broker 468 DestroyWindow(hwndBroker); 469 } 470 break; 471 } 472 473 // PIDL alias 474 LPITEMIDLIST apidlAlias[2]; 475 if (DoGetAliasPIDLs(apidlAlias, lpItems[iItem].pidl)) 476 { 477 if (apidlAlias[0]) 478 { 479 // create another registration entry 480 hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask, 481 lpItems[iItem].fRecursive, apidlAlias[0], 482 dwOwnerPID, hwndBroker); 483 if (hRegEntry) 484 { 485 TRACE("CN_REGISTER: hwnd:%p, hRegEntry:%p, pid:0x%lx\n", 486 hwndServer, hRegEntry, dwOwnerPID); 487 488 // send CN_REGISTER to the server 489 SendMessageW(hwndServer, CN_REGISTER, (WPARAM)hRegEntry, dwOwnerPID); 490 491 // free registration entry 492 SHFreeShared(hRegEntry, dwOwnerPID); 493 } 494 ILFree(apidlAlias[0]); 495 } 496 497 if (apidlAlias[1]) 498 { 499 // create another registration entry 500 hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask, 501 lpItems[iItem].fRecursive, apidlAlias[1], 502 dwOwnerPID, hwndBroker); 503 if (hRegEntry) 504 { 505 TRACE("CN_REGISTER: hwnd:%p, hRegEntry:%p, pid:0x%lx\n", 506 hwndServer, hRegEntry, dwOwnerPID); 507 508 // send CN_REGISTER to the server 509 SendMessageW(hwndServer, CN_REGISTER, (WPARAM)hRegEntry, dwOwnerPID); 510 511 // free registration entry 512 SHFreeShared(hRegEntry, dwOwnerPID); 513 } 514 ILFree(apidlAlias[1]); 515 } 516 } 517 } 518 LeaveCriticalSection(&SHELL32_ChangenotifyCS); 519 520 return nRegID; 521 } 522 523 /************************************************************************* 524 * SHChangeNotifyDeregister [SHELL32.4] 525 */ 526 EXTERN_C BOOL WINAPI 527 SHChangeNotifyDeregister(ULONG hNotify) 528 { 529 TRACE("(0x%08x)\n", hNotify); 530 531 // get the server window 532 HWND hwndServer = GetNotificationServer(FALSE); 533 if (hwndServer == NULL) 534 return FALSE; 535 536 // send CN_UNREGISTER message and try to unregister 537 BOOL ret = (BOOL)SendMessageW(hwndServer, CN_UNREGISTER, hNotify, 0); 538 if (!ret) 539 ERR("CN_UNREGISTER failed\n"); 540 541 return ret; 542 } 543 544 /************************************************************************* 545 * SHChangeNotifyUpdateEntryList [SHELL32.5] 546 */ 547 EXTERN_C BOOL WINAPI 548 SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2, 549 DWORD unknown3, DWORD unknown4) 550 { 551 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", 552 unknown1, unknown2, unknown3, unknown4); 553 return TRUE; 554 } 555 556 /* for dumping events */ 557 static LPCSTR DumpEvent(LONG event) 558 { 559 if (event == SHCNE_ALLEVENTS) 560 return "SHCNE_ALLEVENTS"; 561 #define DUMPEV(x) ,( event & SHCNE_##x )? #x " " : "" 562 return wine_dbg_sprintf( "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" 563 DUMPEV(RENAMEITEM) 564 DUMPEV(CREATE) 565 DUMPEV(DELETE) 566 DUMPEV(MKDIR) 567 DUMPEV(RMDIR) 568 DUMPEV(MEDIAINSERTED) 569 DUMPEV(MEDIAREMOVED) 570 DUMPEV(DRIVEREMOVED) 571 DUMPEV(DRIVEADD) 572 DUMPEV(NETSHARE) 573 DUMPEV(NETUNSHARE) 574 DUMPEV(ATTRIBUTES) 575 DUMPEV(UPDATEDIR) 576 DUMPEV(UPDATEITEM) 577 DUMPEV(SERVERDISCONNECT) 578 DUMPEV(UPDATEIMAGE) 579 DUMPEV(DRIVEADDGUI) 580 DUMPEV(RENAMEFOLDER) 581 DUMPEV(FREESPACE) 582 DUMPEV(EXTENDED_EVENT) 583 DUMPEV(ASSOCCHANGED) 584 DUMPEV(INTERRUPT) 585 ); 586 #undef DUMPEV 587 } 588 589 /************************************************************************* 590 * SHChangeRegistrationReceive [SHELL32.646] 591 */ 592 EXTERN_C BOOL WINAPI 593 SHChangeRegistrationReceive(LPVOID lpUnknown1, DWORD dwUnknown2) 594 { 595 FIXME("SHChangeRegistrationReceive() stub\n"); 596 return FALSE; 597 } 598 599 EXTERN_C VOID WINAPI 600 SHChangeNotifyReceiveEx(LONG lEvent, UINT uFlags, 601 LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwTick) 602 { 603 // TODO: Queueing notifications 604 CreateNotificationParamAndSend(lEvent, uFlags, pidl1, pidl2, dwTick); 605 } 606 607 /************************************************************************* 608 * SHChangeNotifyReceive [SHELL32.643] 609 */ 610 EXTERN_C VOID WINAPI 611 SHChangeNotifyReceive(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) 612 { 613 SHChangeNotifyReceiveEx(lEvent, uFlags, pidl1, pidl2, GetTickCount()); 614 } 615 616 EXTERN_C VOID WINAPI 617 SHChangeNotifyTransmit(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwTick) 618 { 619 SHChangeNotifyReceiveEx(lEvent, uFlags, pidl1, pidl2, dwTick); 620 } 621 622 /************************************************************************* 623 * SHChangeNotify [SHELL32.@] 624 */ 625 EXTERN_C void WINAPI 626 SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2) 627 { 628 LPITEMIDLIST pidl1 = NULL, pidl2 = NULL, pidlTemp1 = NULL, pidlTemp2 = NULL; 629 DWORD dwTick = GetTickCount(); 630 WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; 631 LPWSTR psz1, psz2; 632 TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2); 633 634 switch (uFlags & SHCNF_TYPE) 635 { 636 case SHCNF_IDLIST: 637 pidl1 = (LPITEMIDLIST)dwItem1; 638 pidl2 = (LPITEMIDLIST)dwItem2; 639 break; 640 641 case SHCNF_PATHA: 642 psz1 = psz2 = NULL; 643 if (dwItem1) 644 { 645 SHAnsiToUnicode((LPCSTR)dwItem1, szPath1, _countof(szPath1)); 646 psz1 = szPath1; 647 } 648 if (dwItem2) 649 { 650 SHAnsiToUnicode((LPCSTR)dwItem2, szPath2, _countof(szPath2)); 651 psz2 = szPath2; 652 } 653 uFlags &= ~SHCNF_TYPE; 654 uFlags |= SHCNF_PATHW; 655 SHChangeNotify(wEventId, uFlags, psz1, psz2); 656 return; 657 658 case SHCNF_PATHW: 659 if (dwItem1) 660 { 661 pidl1 = pidlTemp1 = SHSimpleIDListFromPathW((LPCWSTR)dwItem1); 662 } 663 if (dwItem2) 664 { 665 pidl2 = pidlTemp2 = SHSimpleIDListFromPathW((LPCWSTR)dwItem2); 666 } 667 break; 668 669 case SHCNF_PRINTERA: 670 case SHCNF_PRINTERW: 671 FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n"); 672 return; 673 674 default: 675 FIXME("unknown type %08x\n", uFlags & SHCNF_TYPE); 676 return; 677 } 678 679 if (wEventId == 0 || (wEventId & SHCNE_ASSOCCHANGED) || pidl1 != NULL) 680 { 681 TRACE("notifying event %s(%x)\n", DumpEvent(wEventId), wEventId); 682 SHChangeNotifyTransmit(wEventId, uFlags, pidl1, pidl2, dwTick); 683 } 684 685 if (pidlTemp1) 686 ILFree(pidlTemp1); 687 if (pidlTemp2) 688 ILFree(pidlTemp2); 689 } 690 691 /************************************************************************* 692 * NTSHChangeNotifyRegister [SHELL32.640] 693 */ 694 EXTERN_C ULONG WINAPI 695 NTSHChangeNotifyRegister(HWND hwnd, INT fSources, LONG fEvents, UINT msg, 696 INT count, SHChangeNotifyEntry *idlist) 697 { 698 return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery, 699 fEvents, msg, count, idlist); 700 } 701 702 /************************************************************************* 703 * SHChangeNotification_Lock [SHELL32.644] 704 */ 705 EXTERN_C HANDLE WINAPI 706 SHChangeNotification_Lock(HANDLE hTicket, DWORD dwOwnerPID, LPITEMIDLIST **lppidls, 707 LPLONG lpwEventId) 708 { 709 TRACE("%p %08x %p %p\n", hTicket, dwOwnerPID, lppidls, lpwEventId); 710 711 // create a handbag from the ticket 712 LPHANDBAG pHandbag = DoGetHandbagFromTicket(hTicket, dwOwnerPID); 713 if (pHandbag == NULL || pHandbag->dwMagic != HANDBAG_MAGIC) 714 { 715 ERR("pHandbag is invalid\n"); 716 return NULL; 717 } 718 719 // populate parameters from the handbag 720 if (lppidls) 721 *lppidls = pHandbag->pidls; 722 if (lpwEventId) 723 *lpwEventId = (pHandbag->pTicket->wEventId & ~SHCNE_INTERRUPT); 724 725 // return the handbag 726 return pHandbag; 727 } 728 729 /************************************************************************* 730 * SHChangeNotification_Unlock [SHELL32.645] 731 */ 732 EXTERN_C BOOL WINAPI 733 SHChangeNotification_Unlock(HANDLE hLock) 734 { 735 TRACE("%p\n", hLock); 736 737 // validate the handbag 738 LPHANDBAG pHandbag = (LPHANDBAG)hLock; 739 if (pHandbag == NULL || pHandbag->dwMagic != HANDBAG_MAGIC) 740 { 741 ERR("pHandbag is invalid\n"); 742 return FALSE; 743 } 744 745 // free the handbag 746 BOOL ret = SHUnlockShared(pHandbag->pTicket); 747 LocalFree(hLock); 748 return ret; 749 } 750 751 /************************************************************************* 752 * NTSHChangeNotifyDeregister [SHELL32.641] 753 */ 754 EXTERN_C DWORD WINAPI 755 NTSHChangeNotifyDeregister(ULONG hNotify) 756 { 757 FIXME("(0x%08x):semi stub.\n", hNotify); 758 return SHChangeNotifyDeregister(hNotify); 759 } 760 761 /************************************************************************* 762 * SHChangeNotifySuspendResume [SHELL32.277] 763 */ 764 EXTERN_C BOOL 765 WINAPI 766 SHChangeNotifySuspendResume(BOOL bSuspend, 767 LPITEMIDLIST pidl, 768 BOOL bRecursive, 769 DWORD dwReserved) 770 { 771 FIXME("SHChangeNotifySuspendResume() stub\n"); 772 return FALSE; 773 } 774