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