Reactos
at master 898 lines 27 kB view raw
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 10WINE_DEFAULT_DEBUG_CHANNEL(shcn); 11 12CRITICAL_SECTION SHELL32_ChangenotifyCS; 13 14// This function requests creation of the server window if it doesn't exist yet 15static HWND 16GetNotificationServer(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. 46EXTERN_C void InitChangeNotifications(void) 47{ 48 InitializeCriticalSection(&SHELL32_ChangenotifyCS); 49} 50 51// This function will be called from DllMain.DLL_PROCESS_DETACH. 52EXTERN_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 60static HRESULT 61Shell_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 96class CChangeNotifyBroker : 97 public CWindowImpl<CChangeNotifyBroker, CWindow, CWorkerTraits> 98{ 99public: 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 124private: 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. 152static HWND 153CreateNotificationBroker(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. 177static HANDLE 178CreateNotificationParam(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. 233static LPHANDBAG 234DoGetHandbagFromTicket(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. 268static HANDLE 269CreateRegistrationParam(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. 319static void 320CreateNotificationParamAndSend(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 // Create alias PIDLs 338 CComHeapPtr<ITEMIDLIST> pidl1Alias, pidl2Alias; 339 if (pidl1) 340 SHELL32_AliasTranslatePidl(pidl1, &pidl1Alias, ALIAS_ANY); 341 if (pidl2) 342 SHELL32_AliasTranslatePidl(pidl2, &pidl2Alias, ALIAS_ANY); 343 344 HANDLE hTicket2 = NULL; 345 if (((pidl1 && pidl1Alias && !ILIsEqual(pidl1, pidl1Alias)) || 346 (pidl2 && pidl2Alias && !ILIsEqual(pidl2, pidl2Alias)))) 347 { 348 hTicket2 = CreateNotificationParam(wEventId, uFlags, pidl1Alias, pidl2Alias, 349 pid, dwTick); 350 if (!hTicket2) 351 { 352 SHFreeShared(hTicket, pid); 353 return; 354 } 355 } 356 357 TRACE("hTicket:%p, hTicket2:%p, pid:0x%lx\n", hTicket, hTicket2, pid); 358 359 // send the ticket by using CN_DELIVER_NOTIFICATION 360 if (pid != GetCurrentProcessId() || 361 (uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH) 362 { 363 SendMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket, pid); 364 if (hTicket2) 365 SendMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket2, pid); 366 } 367 else 368 { 369 SendNotifyMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket, pid); 370 if (hTicket2) 371 SendNotifyMessageW(hwndServer, CN_DELIVER_NOTIFICATION, (WPARAM)hTicket2, pid); 372 } 373} 374 375struct ALIAS_PIDL 376{ 377 INT csidl1; // from 378 INT csidl2; // to 379 LPITEMIDLIST pidl1; // from 380 LPITEMIDLIST pidl2; // to 381 WCHAR szPath1[MAX_PATH]; // from 382 WCHAR szPath2[MAX_PATH]; // to 383}; 384 385static ALIAS_PIDL AliasPIDLs[] = 386{ 387 { CSIDL_PERSONAL, CSIDL_PERSONAL }, 388 { CSIDL_DESKTOP, CSIDL_COMMON_DESKTOPDIRECTORY, }, 389 { CSIDL_DESKTOP, CSIDL_DESKTOPDIRECTORY }, 390}; 391 392static VOID DoInitAliasPIDLs(void) 393{ 394 static BOOL s_bInit = FALSE; 395 if (!s_bInit) 396 { 397 for (SIZE_T i = 0; i < _countof(AliasPIDLs); ++i) 398 { 399 ALIAS_PIDL *alias = &AliasPIDLs[i]; 400 401 SHGetSpecialFolderLocation(NULL, alias->csidl1, &alias->pidl1); 402 SHGetPathFromIDListW(alias->pidl1, alias->szPath1); 403 404 SHGetSpecialFolderLocation(NULL, alias->csidl2, &alias->pidl2); 405 SHGetPathFromIDListW(alias->pidl2, alias->szPath2); 406 } 407 s_bInit = TRUE; 408 } 409} 410 411static BOOL DoGetAliasPIDLs(LPITEMIDLIST apidls[2], PCIDLIST_ABSOLUTE pidl) 412{ 413 DoInitAliasPIDLs(); 414 415 apidls[0] = apidls[1] = NULL; 416 417 INT k = 0; 418 for (SIZE_T i = 0; i < _countof(AliasPIDLs); ++i) 419 { 420 const ALIAS_PIDL *alias = &AliasPIDLs[i]; 421 if (ILIsEqual(pidl, alias->pidl1)) 422 { 423 if (alias->csidl1 == alias->csidl2) 424 { 425 apidls[k++] = ILCreateFromPathW(alias->szPath2); 426 } 427 else 428 { 429 apidls[k++] = ILClone(alias->pidl2); 430 } 431 if (k >= 2) 432 break; 433 } 434 } 435 436 return k > 0; 437} 438 439/************************************************************************* 440 * SHChangeNotifyRegister [SHELL32.2] 441 */ 442EXTERN_C ULONG WINAPI 443SHChangeNotifyRegister(HWND hwnd, INT fSources, LONG wEventMask, UINT uMsg, 444 INT cItems, SHChangeNotifyEntry *lpItems) 445{ 446 HWND hwndServer, hwndBroker = NULL; 447 HANDLE hRegEntry; 448 INT iItem; 449 ULONG nRegID = INVALID_REG_ID; 450 DWORD dwOwnerPID; 451 LPREGENTRY pRegEntry; 452 453 TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p)\n", 454 hwnd, fSources, wEventMask, uMsg, cItems, lpItems); 455 456 // sanity check 457 if (wEventMask == 0 || cItems <= 0 || cItems > 0x7FFF || lpItems == NULL || 458 hwnd == NULL || !IsWindow(hwnd)) 459 { 460 return INVALID_REG_ID; 461 } 462 463 // request the window of the server 464 hwndServer = GetNotificationServer(TRUE); 465 if (hwndServer == NULL) 466 return INVALID_REG_ID; 467 468 // disable recursive interrupt in specific condition 469 if ((fSources & SHCNRF_RecursiveInterrupt) && !(fSources & SHCNRF_InterruptLevel)) 470 { 471 fSources &= ~SHCNRF_RecursiveInterrupt; 472 } 473 474 // if it is old delivery method, then create a broker window 475 if ((fSources & SHCNRF_NewDelivery) == 0) 476 { 477 hwndBroker = hwnd = CreateNotificationBroker(hwnd, uMsg); 478 uMsg = WM_BROKER_NOTIFICATION; 479 } 480 481 // The owner PID is the process ID of the server 482 GetWindowThreadProcessId(hwndServer, &dwOwnerPID); 483 484 EnterCriticalSection(&SHELL32_ChangenotifyCS); 485 for (iItem = 0; iItem < cItems; ++iItem) 486 { 487 // create a registration entry 488 hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask, 489 lpItems[iItem].fRecursive, lpItems[iItem].pidl, 490 dwOwnerPID, hwndBroker); 491 if (hRegEntry) 492 { 493 TRACE("CN_REGISTER: hwnd:%p, hRegEntry:%p, pid:0x%lx\n", 494 hwndServer, hRegEntry, dwOwnerPID); 495 496 // send CN_REGISTER to the server 497 SendMessageW(hwndServer, CN_REGISTER, (WPARAM)hRegEntry, dwOwnerPID); 498 499 // update nRegID 500 pRegEntry = (LPREGENTRY)SHLockSharedEx(hRegEntry, dwOwnerPID, FALSE); 501 if (pRegEntry) 502 { 503 nRegID = pRegEntry->nRegID; 504 SHUnlockShared(pRegEntry); 505 } 506 507 // free registration entry 508 SHFreeShared(hRegEntry, dwOwnerPID); 509 } 510 511 if (nRegID == INVALID_REG_ID) 512 { 513 ERR("Delivery failed\n"); 514 515 if (hwndBroker) 516 { 517 // destroy the broker 518 DestroyWindow(hwndBroker); 519 } 520 break; 521 } 522 523 // PIDL alias 524 LPITEMIDLIST apidlAlias[2]; 525 if (DoGetAliasPIDLs(apidlAlias, lpItems[iItem].pidl)) 526 { 527 if (apidlAlias[0]) 528 { 529 // create another registration entry 530 hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask, 531 lpItems[iItem].fRecursive, apidlAlias[0], 532 dwOwnerPID, hwndBroker); 533 if (hRegEntry) 534 { 535 TRACE("CN_REGISTER: hwnd:%p, hRegEntry:%p, pid:0x%lx\n", 536 hwndServer, hRegEntry, dwOwnerPID); 537 538 // send CN_REGISTER to the server 539 SendMessageW(hwndServer, CN_REGISTER, (WPARAM)hRegEntry, dwOwnerPID); 540 541 // free registration entry 542 SHFreeShared(hRegEntry, dwOwnerPID); 543 } 544 ILFree(apidlAlias[0]); 545 } 546 547 if (apidlAlias[1]) 548 { 549 // create another registration entry 550 hRegEntry = CreateRegistrationParam(nRegID, hwnd, uMsg, fSources, wEventMask, 551 lpItems[iItem].fRecursive, apidlAlias[1], 552 dwOwnerPID, hwndBroker); 553 if (hRegEntry) 554 { 555 TRACE("CN_REGISTER: hwnd:%p, hRegEntry:%p, pid:0x%lx\n", 556 hwndServer, hRegEntry, dwOwnerPID); 557 558 // send CN_REGISTER to the server 559 SendMessageW(hwndServer, CN_REGISTER, (WPARAM)hRegEntry, dwOwnerPID); 560 561 // free registration entry 562 SHFreeShared(hRegEntry, dwOwnerPID); 563 } 564 ILFree(apidlAlias[1]); 565 } 566 } 567 } 568 LeaveCriticalSection(&SHELL32_ChangenotifyCS); 569 570 return nRegID; 571} 572 573/************************************************************************* 574 * SHChangeNotifyDeregister [SHELL32.4] 575 */ 576EXTERN_C BOOL WINAPI 577SHChangeNotifyDeregister(ULONG hNotify) 578{ 579 TRACE("(0x%08x)\n", hNotify); 580 581 // get the server window 582 HWND hwndServer = GetNotificationServer(FALSE); 583 if (hwndServer == NULL) 584 return FALSE; 585 586 // send CN_UNREGISTER message and try to unregister 587 BOOL ret = (BOOL)SendMessageW(hwndServer, CN_UNREGISTER, hNotify, 0); 588 if (!ret) 589 ERR("CN_UNREGISTER failed\n"); 590 591 return ret; 592} 593 594/************************************************************************* 595 * SHChangeNotifyUpdateEntryList [SHELL32.5] 596 */ 597EXTERN_C BOOL WINAPI 598SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2, 599 DWORD unknown3, DWORD unknown4) 600{ 601 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", 602 unknown1, unknown2, unknown3, unknown4); 603 return TRUE; 604} 605 606/* for dumping events */ 607static LPCSTR DumpEvent(LONG event) 608{ 609 if (event == SHCNE_ALLEVENTS) 610 return "SHCNE_ALLEVENTS"; 611#define DUMPEV(x) ,( event & SHCNE_##x )? #x " " : "" 612 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" 613 DUMPEV(RENAMEITEM) 614 DUMPEV(CREATE) 615 DUMPEV(DELETE) 616 DUMPEV(MKDIR) 617 DUMPEV(RMDIR) 618 DUMPEV(MEDIAINSERTED) 619 DUMPEV(MEDIAREMOVED) 620 DUMPEV(DRIVEREMOVED) 621 DUMPEV(DRIVEADD) 622 DUMPEV(NETSHARE) 623 DUMPEV(NETUNSHARE) 624 DUMPEV(ATTRIBUTES) 625 DUMPEV(UPDATEDIR) 626 DUMPEV(UPDATEITEM) 627 DUMPEV(SERVERDISCONNECT) 628 DUMPEV(UPDATEIMAGE) 629 DUMPEV(DRIVEADDGUI) 630 DUMPEV(RENAMEFOLDER) 631 DUMPEV(FREESPACE) 632 DUMPEV(EXTENDED_EVENT) 633 DUMPEV(ASSOCCHANGED) 634 DUMPEV(INTERRUPT) 635 ); 636#undef DUMPEV 637} 638 639/************************************************************************* 640 * SHChangeRegistrationReceive [SHELL32.646] 641 */ 642EXTERN_C BOOL WINAPI 643SHChangeRegistrationReceive(LPVOID lpUnknown1, DWORD dwUnknown2) 644{ 645 TRACE("\n"); 646 return FALSE; /* Just return FALSE */ 647} 648 649EXTERN_C VOID WINAPI 650SHChangeNotifyReceiveEx(LONG lEvent, UINT uFlags, 651 LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwTick) 652{ 653 // TODO: Queueing notifications 654 CreateNotificationParamAndSend(lEvent, uFlags, pidl1, pidl2, dwTick); 655} 656 657/************************************************************************* 658 * SHChangeNotifyReceive [SHELL32.643] 659 */ 660EXTERN_C VOID WINAPI 661SHChangeNotifyReceive(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) 662{ 663 SHChangeNotifyReceiveEx(lEvent, uFlags, pidl1, pidl2, GetTickCount()); 664} 665 666EXTERN_C VOID WINAPI 667SHChangeNotifyTransmit(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwTick) 668{ 669 SHChangeNotifyReceiveEx(lEvent, uFlags, pidl1, pidl2, dwTick); 670} 671 672/************************************************************************* 673 * SHChangeNotify [SHELL32.@] 674 */ 675EXTERN_C void WINAPI 676SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2) 677{ 678 TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2); 679 680 if (!GetNotificationServer(FALSE)) 681 return; 682 683 DWORD dwTick = GetTickCount(); 684 685 WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; 686 LPITEMIDLIST pidl1 = NULL, pidl2 = NULL; 687 LPWSTR psz1, psz2; 688 SHChangeDWORDAsIDList dwidl; 689 DWORD dwType; 690 691Retry: 692 dwType = (uFlags & SHCNF_TYPE); 693 switch (dwType) 694 { 695 case SHCNF_IDLIST: 696 { 697 if (wEventId == SHCNE_FREESPACE) 698 { 699 szPath1[0] = szPath2[0] = UNICODE_NULL; 700 if (dwItem1) 701 SHGetPathFromIDList((LPCITEMIDLIST)dwItem1, szPath1); 702 if (dwItem2) 703 SHGetPathFromIDList((LPCITEMIDLIST)dwItem2, szPath2); 704 705 uFlags = SHCNF_PATHW; 706 dwItem1 = (szPath1[0] ? szPath1 : NULL); 707 dwItem2 = (szPath2[0] ? szPath2 : NULL); 708 goto Retry; 709 } 710 711 pidl1 = (LPITEMIDLIST)dwItem1; 712 pidl2 = (LPITEMIDLIST)dwItem2; 713 break; 714 } 715 case SHCNF_PATHA: 716 case SHCNF_PRINTERA: 717 { 718 psz1 = psz2 = NULL; 719 szPath1[0] = szPath2[0] = UNICODE_NULL; 720 if (dwItem1) 721 { 722 SHAnsiToUnicode((LPCSTR)dwItem1, szPath1, _countof(szPath1)); 723 psz1 = szPath1; 724 } 725 if (dwItem2) 726 { 727 SHAnsiToUnicode((LPCSTR)dwItem2, szPath2, _countof(szPath2)); 728 psz2 = szPath2; 729 } 730 731 uFlags = ((dwType == SHCNF_PRINTERA) ? SHCNF_PRINTERW : SHCNF_PATHW); 732 dwItem1 = psz1; 733 dwItem2 = psz2; 734 goto Retry; 735 } 736 case SHCNF_PATHW: 737 { 738 if (wEventId == SHCNE_FREESPACE) 739 { 740 INT iDrive1 = -1, iDrive2 = -1; 741 if (dwItem1) 742 iDrive1 = PathGetDriveNumberW((LPCWSTR)dwItem1); 743 if (dwItem2) 744 iDrive2 = PathGetDriveNumberW((LPCWSTR)dwItem2); 745 746 DWORD dwValue = 0; 747 if (iDrive1 >= 0) 748 dwValue |= (1 << iDrive1); 749 if (iDrive2 >= 0) 750 dwValue |= (1 << iDrive2); 751 752 if (!dwValue) 753 return; 754 755 uFlags = SHCNF_DWORD; 756 dwItem1 = UlongToPtr(dwValue); 757 dwItem2 = NULL; 758 goto Retry; 759 } 760 761 if (dwItem1) 762 pidl1 = SHSimpleIDListFromPathW((LPCWSTR)dwItem1); 763 if (dwItem2) 764 pidl2 = SHSimpleIDListFromPathW((LPCWSTR)dwItem2); 765 break; 766 } 767 case SHCNF_PRINTERW: 768 { 769 if (dwItem1) 770 { 771 HRESULT hr = Shell_ParsePrinterName((LPCWSTR)dwItem1, &pidl1, NULL); 772 if (FAILED_UNEXPECTEDLY(hr)) 773 return; 774 } 775 776 if (dwItem2) 777 { 778 HRESULT hr = Shell_ParsePrinterName((LPCWSTR)dwItem2, &pidl2, NULL); 779 if (FAILED_UNEXPECTEDLY(hr)) 780 { 781 ILFree(pidl1); 782 return; 783 } 784 } 785 break; 786 } 787 case SHCNF_DWORD: 788 { 789 dwidl.cb = offsetof(SHChangeDWORDAsIDList, cbZero); 790 dwidl.dwItem1 = PtrToUlong(dwItem1); 791 dwidl.dwItem2 = PtrToUlong(dwItem2); 792 dwidl.cbZero = 0; 793 pidl1 = (LPITEMIDLIST)&dwidl; 794 break; 795 } 796 default: 797 { 798 FIXME("Unknown type: 0x%X\n", dwType); 799 return; 800 } 801 } 802 803 if (pidl1 || !wEventId || (wEventId & SHCNE_ASSOCCHANGED)) 804 { 805 TRACE("notifying event %s(%x)\n", DumpEvent(wEventId), wEventId); 806 SHChangeNotifyTransmit(wEventId, uFlags, pidl1, pidl2, dwTick); 807 } 808 809 if ((dwType == SHCNF_PATHW) || (dwType == SHCNF_PRINTERW)) 810 { 811 ILFree(pidl1); 812 ILFree(pidl2); 813 } 814} 815 816/************************************************************************* 817 * NTSHChangeNotifyRegister [SHELL32.640] 818 */ 819EXTERN_C ULONG WINAPI 820NTSHChangeNotifyRegister(HWND hwnd, INT fSources, LONG fEvents, UINT msg, 821 INT count, SHChangeNotifyEntry *idlist) 822{ 823 return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery, 824 fEvents, msg, count, idlist); 825} 826 827/************************************************************************* 828 * SHChangeNotification_Lock [SHELL32.644] 829 */ 830EXTERN_C HANDLE WINAPI 831SHChangeNotification_Lock(HANDLE hTicket, DWORD dwOwnerPID, LPITEMIDLIST **lppidls, 832 LPLONG lpwEventId) 833{ 834 TRACE("%p %08x %p %p\n", hTicket, dwOwnerPID, lppidls, lpwEventId); 835 836 // create a handbag from the ticket 837 LPHANDBAG pHandbag = DoGetHandbagFromTicket(hTicket, dwOwnerPID); 838 if (pHandbag == NULL || pHandbag->dwMagic != HANDBAG_MAGIC) 839 { 840 ERR("pHandbag is invalid\n"); 841 return NULL; 842 } 843 844 // populate parameters from the handbag 845 if (lppidls) 846 *lppidls = pHandbag->pidls; 847 if (lpwEventId) 848 *lpwEventId = (pHandbag->pTicket->wEventId & ~SHCNE_INTERRUPT); 849 850 // return the handbag 851 return pHandbag; 852} 853 854/************************************************************************* 855 * SHChangeNotification_Unlock [SHELL32.645] 856 */ 857EXTERN_C BOOL WINAPI 858SHChangeNotification_Unlock(HANDLE hLock) 859{ 860 TRACE("%p\n", hLock); 861 862 // validate the handbag 863 LPHANDBAG pHandbag = (LPHANDBAG)hLock; 864 if (pHandbag == NULL || pHandbag->dwMagic != HANDBAG_MAGIC) 865 { 866 ERR("pHandbag is invalid\n"); 867 return FALSE; 868 } 869 870 // free the handbag 871 BOOL ret = SHUnlockShared(pHandbag->pTicket); 872 LocalFree(hLock); 873 return ret; 874} 875 876/************************************************************************* 877 * NTSHChangeNotifyDeregister [SHELL32.641] 878 */ 879EXTERN_C DWORD WINAPI 880NTSHChangeNotifyDeregister(ULONG hNotify) 881{ 882 FIXME("(0x%08x):semi stub.\n", hNotify); 883 return SHChangeNotifyDeregister(hNotify); 884} 885 886/************************************************************************* 887 * SHChangeNotifySuspendResume [SHELL32.277] 888 */ 889EXTERN_C BOOL 890WINAPI 891SHChangeNotifySuspendResume(BOOL bSuspend, 892 LPITEMIDLIST pidl, 893 BOOL bRecursive, 894 DWORD dwReserved) 895{ 896 FIXME("SHChangeNotifySuspendResume() stub\n"); 897 return FALSE; 898}