ReactOS 0.4.15-dev-8434-g155a7c7
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 =
203 CDirectoryWatcher::Create(pRegEntry->hwnd, szPath, pRegEntry->fRecursive);
204 if (pDirectoryWatcher == NULL)
205 return NULL;
206
207 return pDirectoryWatcher;
208}
209
210// Message CN_REGISTER: Register the registration entry.
211// wParam: The handle of registration entry.
212// lParam: The owner PID of registration entry.
213// return: TRUE if successful.
215{
216 TRACE("OnRegister(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam);
217
218 // lock the registration entry
219 HANDLE hRegEntry = (HANDLE)wParam;
220 DWORD dwOwnerPID = (DWORD)lParam;
221 LPREGENTRY pRegEntry = (LPREGENTRY)SHLockSharedEx(hRegEntry, dwOwnerPID, TRUE);
222 if (pRegEntry == NULL || pRegEntry->dwMagic != REGENTRY_MAGIC)
223 {
224 ERR("pRegEntry is invalid\n");
225 SHUnlockShared(pRegEntry);
226 return FALSE;
227 }
228
229 // update registration ID if necessary
230 if (pRegEntry->nRegID == INVALID_REG_ID)
231 pRegEntry->nRegID = GetNextRegID();
232
233 TRACE("pRegEntry->nRegID: %u\n", pRegEntry->nRegID);
234
235 // get the user PID; that is the process ID of the target window
236 DWORD dwUserPID;
237 GetWindowThreadProcessId(pRegEntry->hwnd, &dwUserPID);
238
239 // get broker if any
240 HWND hwndBroker = pRegEntry->hwndBroker;
241
242 // clone the registration entry
243 LPREGENTRY pNewEntry = (LPREGENTRY)SHAlloc(pRegEntry->cbSize);
244 if (pNewEntry == NULL)
245 {
246 ERR("Out of memory\n");
247 pRegEntry->nRegID = INVALID_REG_ID;
248 SHUnlockShared(pRegEntry);
249 return FALSE;
250 }
251 CopyMemory(pNewEntry, pRegEntry, pRegEntry->cbSize);
252
253 // create a directory watch if necessary
254 CDirectoryWatcher *pDirWatch = NULL;
255 if (pRegEntry->ibPidl && (pRegEntry->fSources & SHCNRF_InterruptLevel))
256 {
257 pDirWatch = CreateDirectoryWatcherFromRegEntry(pRegEntry);
258 if (pDirWatch && !pDirWatch->RequestAddWatcher())
259 {
260 ERR("RequestAddWatcher failed: %u\n", pRegEntry->nRegID);
261 pRegEntry->nRegID = INVALID_REG_ID;
262 SHUnlockShared(pRegEntry);
263 delete pDirWatch;
264 return FALSE;
265 }
266 }
267
268 // unlock the registry entry
269 SHUnlockShared(pRegEntry);
270
271 // add an item
272 CWatchItem *pItem = new CWatchItem { m_nNextRegID, dwUserPID, pNewEntry, hwndBroker, pDirWatch };
273 return AddItem(pItem);
274}
275
276// Message CN_UNREGISTER: Unregister registration entries.
277// wParam: The registration ID.
278// lParam: Ignored.
279// return: TRUE if successful.
281{
282 TRACE("OnUnRegister(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam);
283
284 // validate registration ID
285 UINT nRegID = (UINT)wParam;
286 if (nRegID == INVALID_REG_ID)
287 {
288 ERR("INVALID_REG_ID\n");
289 return FALSE;
290 }
291
292 // remove it
293 return RemoveItemsByRegID(nRegID);
294}
295
296// Message CN_DELIVER_NOTIFICATION: Perform a delivery.
297// wParam: The handle of delivery ticket.
298// lParam: The owner PID of delivery ticket.
299// return: TRUE if necessary.
301{
302 TRACE("OnDeliverNotification(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam);
303
304 HANDLE hTicket = (HANDLE)wParam;
305 DWORD dwOwnerPID = (DWORD)lParam;
306
307 // do delivery
308 BOOL ret = DeliverNotification(hTicket, dwOwnerPID);
309
310 // free the ticket
311 SHFreeShared(hTicket, dwOwnerPID);
312 return ret;
313}
314
315// Message CN_SUSPEND_RESUME: Suspend or resume the change notification.
316// (specification is unknown)
318{
319 TRACE("OnSuspendResume\n");
320
321 // FIXME
322 return FALSE;
323}
324
325// Message CN_UNREGISTER_PROCESS: Remove registration entries by PID.
326// wParam: The user PID.
327// lParam: Ignored.
328// return: Zero.
330{
331 DWORD dwUserPID = (DWORD)wParam;
332 RemoveItemsByProcess(dwUserPID);
333 return 0;
334}
335
337{
340 return 0;
341}
342
343// get next valid registration ID
345{
346 m_nNextRegID++;
348 m_nNextRegID++;
349 return m_nNextRegID;
350}
351
352// This function is called from CChangeNotifyServer::OnDeliverNotification.
353// The function notifies to the registration entries that should be notified.
355{
356 TRACE("DeliverNotification(%p, %p, 0x%lx)\n", m_hWnd, hTicket, dwOwnerPID);
357
358 // lock the delivery ticket
359 LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, FALSE);
360 if (pTicket == NULL || pTicket->dwMagic != DELITICKET_MAGIC)
361 {
362 ERR("pTicket is invalid\n");
363 SHUnlockShared(pTicket);
364 return FALSE;
365 }
366
367 // for all items
368 for (INT i = 0; i < m_items.GetSize(); ++i)
369 {
370 if (m_items[i] == NULL)
371 continue;
372
373 LPREGENTRY pRegEntry = m_items[i]->pRegEntry;
374 if (pRegEntry == NULL || pRegEntry->dwMagic != REGENTRY_MAGIC)
375 {
376 ERR("pRegEntry is invalid\n");
377 continue;
378 }
379
380 // should we notify for it?
381 BOOL bNotify = ShouldNotify(pTicket, pRegEntry);
382 if (bNotify)
383 {
384 // do notify
385 TRACE("Notifying: %p, 0x%x, %p, %lu\n",
386 pRegEntry->hwnd, pRegEntry->uMsg, hTicket, dwOwnerPID);
387 SendMessageW(pRegEntry->hwnd, pRegEntry->uMsg, (WPARAM)hTicket, dwOwnerPID);
388 TRACE("GetLastError(): %ld\n", ::GetLastError());
389 }
390 }
391
392 // unlock the ticket
393 SHUnlockShared(pTicket);
394
395 return TRUE;
396}
397
399{
400#define RETURN(x) do { \
401 TRACE("ShouldNotify return %d\n", (x)); \
402 return (x); \
403} while (0)
404
405 if (pTicket->wEventId & SHCNE_INTERRUPT)
406 {
407 if (!(pRegEntry->fSources & SHCNRF_InterruptLevel))
408 RETURN(FALSE);
409 if (!pRegEntry->ibPidl)
410 RETURN(FALSE);
411 }
412 else
413 {
414 if (!(pRegEntry->fSources & SHCNRF_ShellLevel))
415 RETURN(FALSE);
416 }
417
418 if (!(pTicket->wEventId & pRegEntry->fEvents))
419 RETURN(FALSE);
420
421 LPITEMIDLIST pidl = NULL, pidl1 = NULL, pidl2 = NULL;
422 if (pRegEntry->ibPidl)
423 pidl = (LPITEMIDLIST)((LPBYTE)pRegEntry + pRegEntry->ibPidl);
424 if (pTicket->ibOffset1)
425 pidl1 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset1);
426 if (pTicket->ibOffset2)
427 pidl2 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset2);
428
429 if (pidl == NULL || (pTicket->wEventId & SHCNE_GLOBALEVENTS))
430 RETURN(TRUE);
431
432 if (pRegEntry->fRecursive)
433 {
434 if (ILIsParent(pidl, pidl1, FALSE) ||
435 (pidl2 && ILIsParent(pidl, pidl2, FALSE)))
436 {
437 RETURN(TRUE);
438 }
439 }
440 else
441 {
442 if (ILIsEqual(pidl, pidl1) ||
443 ILIsParent(pidl, pidl1, TRUE) ||
444 (pidl2 && ILIsParent(pidl, pidl2, TRUE)))
445 {
446 RETURN(TRUE);
447 }
448 }
449
450 RETURN(FALSE);
451#undef RETURN
452}
453
455{
456 if (!phwnd)
457 return E_INVALIDARG;
458 *phwnd = m_hWnd;
459 return S_OK;
460}
461
463{
464 return E_NOTIMPL;
465}
466
468{
469 // This is called by CChangeNotifyServer_CreateInstance right after instantiation.
471 if (!hwnd)
472 return E_FAIL;
474 return S_OK;
475}
476
478{
479 return ShellObjectCreatorInit<CChangeNotifyServer>(riid, ppv);
480}
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 ERR(fmt,...)
Definition: precomp.h:57
#define STDMETHOD(m)
Definition: basetyps.h:62
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(HWND hNotifyWnd, 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:1342
BOOL WINAPI ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
Definition: pidl.c:548
#define REFIID
Definition: guiddef.h:118
#define SHCNRF_ShellLevel
Definition: shlobj.h:1935
#define SHCNRF_InterruptLevel
Definition: shlobj.h:1934
#define SHCNE_GLOBALEVENTS
Definition: shlobj.h:1912
#define SHCNE_INTERRUPT
Definition: shlobj.h:1914
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