ReactOS 0.4.15-dev-8102-g108db8f
CChangeNotifyServer.cpp
Go to the documentation of this file.
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#include "shelldesktop.h"
8#include "shlwapi_undoc.h"
9#include "CDirectoryWatcher.h"
10#include <assert.h> // for assert
11
13
15
16// notification target item
18{
19 UINT nRegID; // The registration ID.
20 DWORD dwUserPID; // The user PID; that is the process ID of the target window.
21 LPREGENTRY pRegEntry; // The registration entry.
22 HWND hwndBroker; // Client broker window (if any).
23 CDirectoryWatcher *pDirWatch; // for filesystem notification
24};
25
27// CChangeNotifyServer
28//
29// CChangeNotifyServer implements a window that handles all shell change notifications.
30// It runs in the context of explorer and specifically in the thread of the shell desktop.
31// Shell change notification api exported from shell32 forwards all their calls
32// to this window where all processing takes place.
33
35 public CWindowImpl<CChangeNotifyServer, CWindow, CWorkerTraits>,
36 public CComObjectRootEx<CComMultiThreadModelNoCS>,
37 public IOleWindow
38{
39public:
41 virtual ~CChangeNotifyServer();
43
44 // *** IOleWindow methods ***
45 STDMETHOD(GetWindow)(HWND *lphwnd) override;
46 STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override;
47
48 // Message handlers
55
57
62
63 DECLARE_WND_CLASS_EX(L"WorkerW", 0, 0)
64
73
74private:
77
78 BOOL AddItem(CWatchItem *pItem);
81 void DestroyItem(CWatchItem *pItem, HWND *phwndBroker);
82 void DestroyAllItems();
83
85 BOOL DeliverNotification(HANDLE hTicket, DWORD dwOwnerPID);
86 BOOL ShouldNotify(LPDELITICKET pTicket, LPREGENTRY pRegEntry);
87};
88
91{
92}
93
95{
96}
97
99{
100 // find the empty room
101 for (INT i = 0; i < m_items.GetSize(); ++i)
102 {
103 if (m_items[i] == NULL)
104 {
105 // found the room, populate it
106 m_items[i] = pItem;
107 return TRUE;
108 }
109 }
110
111 // no empty room found
112 m_items.Add(pItem);
113 return TRUE;
114}
115
117{
118 assert(pItem);
119
120 // destroy broker if any and if first time
121 HWND hwndBroker = pItem->hwndBroker;
122 pItem->hwndBroker = NULL;
123 if (hwndBroker && hwndBroker != *phwndBroker)
124 {
125 ::DestroyWindow(hwndBroker);
126 *phwndBroker = hwndBroker;
127 }
128
129 // request termination of pDirWatch if any
130 CDirectoryWatcher *pDirWatch = pItem->pDirWatch;
131 pItem->pDirWatch = NULL;
132 if (pDirWatch)
133 pDirWatch->RequestTermination();
134
135 // free
136 SHFree(pItem->pRegEntry);
137 delete pItem;
138}
139
141{
142 for (INT i = 0; i < m_items.GetSize(); ++i)
143 {
144 if (m_items[i])
145 {
146 HWND hwndBroker = NULL;
147 DestroyItem(m_items[i], &hwndBroker);
148 m_items[i] = NULL;
149 }
150 }
151 m_items.RemoveAll();
152}
153
155{
156 BOOL bFound = FALSE;
157 HWND hwndBroker = NULL;
158 assert(nRegID != INVALID_REG_ID);
159 for (INT i = 0; i < m_items.GetSize(); ++i)
160 {
161 if (m_items[i] && m_items[i]->nRegID == nRegID)
162 {
163 bFound = TRUE;
164 DestroyItem(m_items[i], &hwndBroker);
165 m_items[i] = NULL;
166 }
167 }
168 return bFound;
169}
170
172{
173 BOOL bFound = FALSE;
174 HWND hwndBroker = NULL;
175 assert(dwUserPID != 0);
176 for (INT i = 0; i < m_items.GetSize(); ++i)
177 {
178 if (m_items[i] && m_items[i]->dwUserPID == dwUserPID)
179 {
180 bFound = TRUE;
181 DestroyItem(m_items[i], &hwndBroker);
182 m_items[i] = NULL;
183 }
184 }
185 return bFound;
186}
187
188// create a CDirectoryWatcher from a REGENTRY
189static CDirectoryWatcher *
191{
192 if (pRegEntry->ibPidl == 0)
193 return NULL;
194
195 // get the path
197 LPITEMIDLIST pidl = (LPITEMIDLIST)((LPBYTE)pRegEntry + pRegEntry->ibPidl);
199 return NULL;
200
201 // create a CDirectoryWatcher
202 CDirectoryWatcher *pDirectoryWatcher = CDirectoryWatcher::Create(szPath, pRegEntry->fRecursive);
203 if (pDirectoryWatcher == NULL)
204 return NULL;
205
206 return pDirectoryWatcher;
207}
208
209// Message CN_REGISTER: Register the registration entry.
210// wParam: The handle of registration entry.
211// lParam: The owner PID of registration entry.
212// return: TRUE if successful.
214{
215 TRACE("OnRegister(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam);
216
217 // lock the registration entry
218 HANDLE hRegEntry = (HANDLE)wParam;
219 DWORD dwOwnerPID = (DWORD)lParam;
220 LPREGENTRY pRegEntry = (LPREGENTRY)SHLockSharedEx(hRegEntry, dwOwnerPID, TRUE);
221 if (pRegEntry == NULL || pRegEntry->dwMagic != REGENTRY_MAGIC)
222 {
223 ERR("pRegEntry is invalid\n");
224 SHUnlockShared(pRegEntry);
225 return FALSE;
226 }
227
228 // update registration ID if necessary
229 if (pRegEntry->nRegID == INVALID_REG_ID)
230 pRegEntry->nRegID = GetNextRegID();
231
232 TRACE("pRegEntry->nRegID: %u\n", pRegEntry->nRegID);
233
234 // get the user PID; that is the process ID of the target window
235 DWORD dwUserPID;
236 GetWindowThreadProcessId(pRegEntry->hwnd, &dwUserPID);
237
238 // get broker if any
239 HWND hwndBroker = pRegEntry->hwndBroker;
240
241 // clone the registration entry
242 LPREGENTRY pNewEntry = (LPREGENTRY)SHAlloc(pRegEntry->cbSize);
243 if (pNewEntry == NULL)
244 {
245 ERR("Out of memory\n");
246 pRegEntry->nRegID = INVALID_REG_ID;
247 SHUnlockShared(pRegEntry);
248 return FALSE;
249 }
250 CopyMemory(pNewEntry, pRegEntry, pRegEntry->cbSize);
251
252 // create a directory watch if necessary
253 CDirectoryWatcher *pDirWatch = NULL;
254 if (pRegEntry->ibPidl && (pRegEntry->fSources & SHCNRF_InterruptLevel))
255 {
256 pDirWatch = CreateDirectoryWatcherFromRegEntry(pRegEntry);
257 if (pDirWatch && !pDirWatch->RequestAddWatcher())
258 {
259 ERR("RequestAddWatcher failed: %u\n", pRegEntry->nRegID);
260 pRegEntry->nRegID = INVALID_REG_ID;
261 SHUnlockShared(pRegEntry);
262 delete pDirWatch;
263 return FALSE;
264 }
265 }
266
267 // unlock the registry entry
268 SHUnlockShared(pRegEntry);
269
270 // add an item
271 CWatchItem *pItem = new CWatchItem { m_nNextRegID, dwUserPID, pNewEntry, hwndBroker, pDirWatch };
272 return AddItem(pItem);
273}
274
275// Message CN_UNREGISTER: Unregister registration entries.
276// wParam: The registration ID.
277// lParam: Ignored.
278// return: TRUE if successful.
280{
281 TRACE("OnUnRegister(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam);
282
283 // validate registration ID
284 UINT nRegID = (UINT)wParam;
285 if (nRegID == INVALID_REG_ID)
286 {
287 ERR("INVALID_REG_ID\n");
288 return FALSE;
289 }
290
291 // remove it
292 return RemoveItemsByRegID(nRegID);
293}
294
295// Message CN_DELIVER_NOTIFICATION: Perform a delivery.
296// wParam: The handle of delivery ticket.
297// lParam: The owner PID of delivery ticket.
298// return: TRUE if necessary.
300{
301 TRACE("OnDeliverNotification(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam);
302
303 HANDLE hTicket = (HANDLE)wParam;
304 DWORD dwOwnerPID = (DWORD)lParam;
305
306 // do delivery
307 BOOL ret = DeliverNotification(hTicket, dwOwnerPID);
308
309 // free the ticket
310 SHFreeShared(hTicket, dwOwnerPID);
311 return ret;
312}
313
314// Message CN_SUSPEND_RESUME: Suspend or resume the change notification.
315// (specification is unknown)
317{
318 TRACE("OnSuspendResume\n");
319
320 // FIXME
321 return FALSE;
322}
323
324// Message CN_UNREGISTER_PROCESS: Remove registration entries by PID.
325// wParam: The user PID.
326// lParam: Ignored.
327// return: Zero.
329{
330 DWORD dwUserPID = (DWORD)wParam;
331 RemoveItemsByProcess(dwUserPID);
332 return 0;
333}
334
336{
339 return 0;
340}
341
342// get next valid registration ID
344{
345 m_nNextRegID++;
347 m_nNextRegID++;
348 return m_nNextRegID;
349}
350
351// This function is called from CChangeNotifyServer::OnDeliverNotification.
352// The function notifies to the registration entries that should be notified.
354{
355 TRACE("DeliverNotification(%p, %p, 0x%lx)\n", m_hWnd, hTicket, dwOwnerPID);
356
357 // lock the delivery ticket
358 LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, FALSE);
359 if (pTicket == NULL || pTicket->dwMagic != DELITICKET_MAGIC)
360 {
361 ERR("pTicket is invalid\n");
362 SHUnlockShared(pTicket);
363 return FALSE;
364 }
365
366 // for all items
367 for (INT i = 0; i < m_items.GetSize(); ++i)
368 {
369 if (m_items[i] == NULL)
370 continue;
371
372 LPREGENTRY pRegEntry = m_items[i]->pRegEntry;
373 if (pRegEntry == NULL || pRegEntry->dwMagic != REGENTRY_MAGIC)
374 {
375 ERR("pRegEntry is invalid\n");
376 continue;
377 }
378
379 // should we notify for it?
380 BOOL bNotify = ShouldNotify(pTicket, pRegEntry);
381 if (bNotify)
382 {
383 // do notify
384 TRACE("Notifying: %p, 0x%x, %p, %lu\n",
385 pRegEntry->hwnd, pRegEntry->uMsg, hTicket, dwOwnerPID);
386 SendMessageW(pRegEntry->hwnd, pRegEntry->uMsg, (WPARAM)hTicket, dwOwnerPID);
387 TRACE("GetLastError(): %ld\n", ::GetLastError());
388 }
389 }
390
391 // unlock the ticket
392 SHUnlockShared(pTicket);
393
394 return TRUE;
395}
396
398{
399#define RETURN(x) do { \
400 TRACE("ShouldNotify return %d\n", (x)); \
401 return (x); \
402} while (0)
403
404 if (pTicket->wEventId & SHCNE_INTERRUPT)
405 {
406 if (!(pRegEntry->fSources & SHCNRF_InterruptLevel))
407 RETURN(FALSE);
408 if (!pRegEntry->ibPidl)
409 RETURN(FALSE);
410 }
411 else
412 {
413 if (!(pRegEntry->fSources & SHCNRF_ShellLevel))
414 RETURN(FALSE);
415 }
416
417 if (!(pTicket->wEventId & pRegEntry->fEvents))
418 RETURN(FALSE);
419
420 LPITEMIDLIST pidl = NULL, pidl1 = NULL, pidl2 = NULL;
421 if (pRegEntry->ibPidl)
422 pidl = (LPITEMIDLIST)((LPBYTE)pRegEntry + pRegEntry->ibPidl);
423 if (pTicket->ibOffset1)
424 pidl1 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset1);
425 if (pTicket->ibOffset2)
426 pidl2 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset2);
427
428 if (pidl == NULL || (pTicket->wEventId & SHCNE_GLOBALEVENTS))
429 RETURN(TRUE);
430
431 if (pRegEntry->fRecursive)
432 {
433 if (ILIsParent(pidl, pidl1, FALSE) ||
434 (pidl2 && ILIsParent(pidl, pidl2, FALSE)))
435 {
436 RETURN(TRUE);
437 }
438 }
439 else
440 {
441 if (ILIsEqual(pidl, pidl1) ||
442 ILIsParent(pidl, pidl1, TRUE) ||
443 (pidl2 && ILIsParent(pidl, pidl2, TRUE)))
444 {
445 RETURN(TRUE);
446 }
447 }
448
449 RETURN(FALSE);
450#undef RETURN
451}
452
454{
455 if (!phwnd)
456 return E_INVALIDARG;
457 *phwnd = m_hWnd;
458 return S_OK;
459}
460
462{
463 return E_NOTIMPL;
464}
465
467{
468 // This is called by CChangeNotifyServer_CreateInstance right after instantiation.
470 if (!hwnd)
471 return E_FAIL;
473 return S_OK;
474}
475
477{
478 return ShellObjectCreatorInit<CChangeNotifyServer>(riid, ppv);
479}
static CDirectoryWatcher * CreateDirectoryWatcherFromRegEntry(LPREGENTRY pRegEntry)
#define RETURN(x)
HRESULT CChangeNotifyServer_CreateInstance(REFIID riid, void **ppv)
#define CN_UNREGISTER
struct DELITICKET * LPDELITICKET
#define CN_DELIVER_NOTIFICATION
#define CN_UNREGISTER_PROCESS
#define REGENTRY_MAGIC
struct REGENTRY * LPREGENTRY
#define DELITICKET_MAGIC
#define CN_REGISTER
HWND SHCreateDefaultWorkerWindow(VOID)
#define CN_SUSPEND_RESUME
#define INVALID_REG_ID
#define WINE_DEFAULT_DEBUG_CHANNEL(t)
Definition: precomp.h:23
#define STDMETHOD(m)
Definition: basetyps.h:62
#define ERR(fmt,...)
Definition: debug.h:113
STDMETHOD() GetWindow(HWND *lphwnd) override
LRESULT OnRegister(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
STDMETHOD() ContextSensitiveHelp(BOOL fEnterMode) override
BOOL ShouldNotify(LPDELITICKET pTicket, LPREGENTRY pRegEntry)
CSimpleArray< CWatchItem * > m_items
LRESULT OnDeliverNotification(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
BOOL RemoveItemsByRegID(UINT nRegID)
LRESULT OnRemoveByPID(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
BOOL RemoveItemsByProcess(DWORD dwUserPID)
LRESULT OnSuspendResume(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
BOOL DeliverNotification(HANDLE hTicket, DWORD dwOwnerPID)
void DestroyItem(CWatchItem *pItem, HWND *phwndBroker)
BOOL AddItem(CWatchItem *pItem)
LRESULT OnUnRegister(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
static CDirectoryWatcher * Create(LPCWSTR pszDirectoryPath, BOOL fSubTree)
static void RequestAllWatchersTermination()
WPARAM wParam
Definition: combotst.c:138
LPARAM lParam
Definition: combotst.c:139
#define E_INVALIDARG
Definition: ddrawi.h:101
#define E_NOTIMPL
Definition: ddrawi.h:99
#define E_FAIL
Definition: ddrawi.h:102
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define MAX_PATH
Definition: compat.h:34
void WINAPI SHFree(LPVOID pv)
Definition: shellole.c:326
LPVOID WINAPI SHAlloc(SIZE_T len)
Definition: shellole.c:304
BOOL WINAPI SHFreeShared(HANDLE hShared, DWORD dwProcId)
Definition: ordinal.c:311
BOOL WINAPI SHUnlockShared(LPVOID lpView)
Definition: ordinal.c:291
BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
Definition: path.c:1723
#define assert(x)
Definition: debug.h:53
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
REFIID riid
Definition: atlbase.h:39
REFIID LPVOID * ppv
Definition: atlbase.h:39
#define S_OK
Definition: intsafe.h:52
#define BEGIN_COM_MAP(x)
Definition: atlcom.h:581
#define COM_INTERFACE_ENTRY_IID(iid, x)
Definition: atlcom.h:601
#define DECLARE_PROTECT_FINAL_CONSTRUCT()
Definition: atlcom.h:679
#define DECLARE_NOT_AGGREGATABLE(x)
Definition: atlcom.h:651
#define END_COM_MAP()
Definition: atlcom.h:592
#define MESSAGE_HANDLER(msg, func)
Definition: atlwin.h:1926
#define BEGIN_MSG_MAP(theClass)
Definition: atlwin.h:1898
#define END_MSG_MAP()
Definition: atlwin.h:1917
#define DECLARE_WND_CLASS_EX(WndClassName, style, bkgnd)
Definition: atlwin.h:2004
LPCWSTR szPath
Definition: env.c:37
unsigned int UINT
Definition: ndis.h:50
#define DWORD
Definition: nt_native.h:44
#define L(x)
Definition: ntvdm.h:50
const GUID IID_IOleWindow
BOOL WINAPI ILIsParent(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild, BOOL bImmediate)
Definition: pidl.c:605
BOOL WINAPI SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath)
Definition: pidl.c:1340
BOOL WINAPI ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
Definition: pidl.c:548
#define REFIID
Definition: guiddef.h:118
#define SHCNRF_ShellLevel
Definition: shlobj.h:1922
#define SHCNRF_InterruptLevel
Definition: shlobj.h:1921
#define SHCNE_GLOBALEVENTS
Definition: shlobj.h:1899
#define SHCNE_INTERRUPT
Definition: shlobj.h:1901
PVOID WINAPI SHLockSharedEx(HANDLE hData, DWORD dwProcessId, BOOL bWriteAccess)
ITEMIDLIST UNALIGNED * LPITEMIDLIST
Definition: shtypes.idl:41
#define TRACE(s)
Definition: solgame.cpp:4
CDirectoryWatcher * pDirWatch
LPREGENTRY pRegEntry
unsigned char * LPBYTE
Definition: typedefs.h:53
PVOID HANDLE
Definition: typedefs.h:73
int32_t INT
Definition: typedefs.h:58
#define AddItem
Definition: userenv.h:209
int ret
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
DWORD WINAPI GetWindowThreadProcessId(HWND hWnd, PDWORD lpdwProcessId)
#define CopyMemory
Definition: winbase.h:1710
_In_ LONG _In_ HWND hwnd
Definition: winddi.h:4023
LONG_PTR LPARAM
Definition: windef.h:208
LONG_PTR LRESULT
Definition: windef.h:209
UINT_PTR WPARAM
Definition: windef.h:207
#define WINAPI
Definition: msvc.h:6
#define SubclassWindow(hwnd, lpfn)
Definition: windowsx.h:542
#define WM_DESTROY
Definition: winuser.h:1609
BOOL WINAPI DestroyWindow(_In_ HWND)
LRESULT WINAPI SendMessageW(_In_ HWND, _In_ UINT, _In_ WPARAM, _In_ LPARAM)
__wchar_t WCHAR
Definition: xmlstorage.h:180