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