xref: /reactos/dll/win32/shell32/changenotify.cpp (revision 0240a876)
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
GetNotificationServer(BOOL bCreate)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.
InitChangeNotifications(void)46 EXTERN_C void InitChangeNotifications(void)
47 {
48     InitializeCriticalSection(&SHELL32_ChangenotifyCS);
49 }
50 
51 // This function will be called from DllMain.DLL_PROCESS_DETACH.
FreeChangeNotifications(void)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
Shell_ParsePrinterName(_In_ LPCWSTR pszName,_Out_ LPITEMIDLIST * ppidl,_In_ IBindCtx * pBindCtx)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:
CChangeNotifyBroker(HWND hwndClient,UINT uMsg)100     CChangeNotifyBroker(HWND hwndClient, UINT uMsg) :
101         m_hwndClient(hwndClient), m_uMsg(uMsg)
102     {
103     }
104 
105     // Message handlers
OnBrokerNotification(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)106     LRESULT OnBrokerNotification(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
107     {
108         return BrokerNotification((HANDLE)wParam, (DWORD)lParam);
109     }
110 
OnFinalMessage(HWND)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 
BrokerNotification(HANDLE hTicket,DWORD dwOwnerPID)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
CreateNotificationBroker(HWND hwnd,UINT wMsg)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
CreateNotificationParam(LONG wEventId,UINT uFlags,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2,DWORD dwOwnerPID,DWORD dwTick)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
DoGetHandbagFromTicket(HANDLE hTicket,DWORD dwOwnerPID)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
CreateRegistrationParam(ULONG nRegID,HWND hwnd,UINT wMsg,INT fSources,LONG fEvents,LONG fRecursive,LPCITEMIDLIST pidl,DWORD dwOwnerPID,HWND hwndBroker)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
CreateNotificationParamAndSend(LONG wEventId,UINT uFlags,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2,DWORD dwTick)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 
DoInitAliasPIDLs(void)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 
DoGetAliasPIDLs(LPITEMIDLIST apidls[2],PCIDLIST_ABSOLUTE pidl)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
SHChangeNotifyRegister(HWND hwnd,INT fSources,LONG wEventMask,UINT uMsg,INT cItems,SHChangeNotifyEntry * lpItems)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
SHChangeNotifyDeregister(ULONG hNotify)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
SHChangeNotifyUpdateEntryList(DWORD unknown1,DWORD unknown2,DWORD unknown3,DWORD unknown4)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 */
DumpEvent(LONG event)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
SHChangeRegistrationReceive(LPVOID lpUnknown1,DWORD dwUnknown2)619 SHChangeRegistrationReceive(LPVOID lpUnknown1, DWORD dwUnknown2)
620 {
621     TRACE("\n");
622     return FALSE; /* Just return FALSE */
623 }
624 
625 EXTERN_C VOID WINAPI
SHChangeNotifyReceiveEx(LONG lEvent,UINT uFlags,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2,DWORD dwTick)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
SHChangeNotifyReceive(LONG lEvent,UINT uFlags,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)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
SHChangeNotifyTransmit(LONG lEvent,UINT uFlags,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2,DWORD dwTick)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
SHChangeNotify(LONG wEventId,UINT uFlags,LPCVOID dwItem1,LPCVOID dwItem2)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
NTSHChangeNotifyRegister(HWND hwnd,INT fSources,LONG fEvents,UINT msg,INT count,SHChangeNotifyEntry * idlist)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
SHChangeNotification_Lock(HANDLE hTicket,DWORD dwOwnerPID,LPITEMIDLIST ** lppidls,LPLONG lpwEventId)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
SHChangeNotification_Unlock(HANDLE hLock)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
NTSHChangeNotifyDeregister(ULONG hNotify)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
SHChangeNotifySuspendResume(BOOL bSuspend,LPITEMIDLIST pidl,BOOL bRecursive,DWORD dwReserved)867 SHChangeNotifySuspendResume(BOOL bSuspend,
868                             LPITEMIDLIST pidl,
869                             BOOL bRecursive,
870                             DWORD dwReserved)
871 {
872     FIXME("SHChangeNotifySuspendResume() stub\n");
873     return FALSE;
874 }
875