ReactOS  0.4.15-dev-506-ga3ec01c
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 
14 // TODO: SHCNRF_RecursiveInterrupt
15 
17 
18 // notification target item
19 struct ITEM
20 {
21  UINT nRegID; // The registration ID.
22  DWORD dwUserPID; // The user PID; that is the process ID of the target window.
23  HANDLE hRegEntry; // The registration entry.
24  HWND hwndBroker; // Client broker window (if any).
25  CDirectoryWatcher *pDirWatch; // for filesystem notification
26 };
27 
28 typedef CWinTraits <
32 
34 // CChangeNotifyServer
35 //
36 // CChangeNotifyServer implements a window that handles all shell change notifications.
37 // It runs in the context of explorer and specifically in the thread of the shell desktop.
38 // Shell change notification api exported from shell32 forwards all their calls
39 // to this window where all processing takes place.
40 
42  public CWindowImpl<CChangeNotifyServer, CWindow, CChangeNotifyServerTraits>,
43  public CComObjectRootEx<CComMultiThreadModelNoCS>,
44  public IOleWindow
45 {
46 public:
48  virtual ~CChangeNotifyServer();
50 
51  // *** IOleWindow methods ***
52  virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *lphwnd);
54 
55  // Message handlers
56  LRESULT OnRegister(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
61  LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
62 
64 
68  END_COM_MAP()
69 
70  DECLARE_WND_CLASS_EX(L"WorkerW", 0, 0)
71 
79  END_MSG_MAP()
80 
81 private:
84 
85  BOOL AddItem(UINT nRegID, DWORD dwUserPID, HANDLE hRegEntry, HWND hwndBroker,
86  CDirectoryWatcher *pDirWatch);
87  BOOL RemoveItemsByRegID(UINT nRegID, DWORD dwOwnerPID);
88  void RemoveItemsByProcess(DWORD dwOwnerPID, DWORD dwUserPID);
89  void DestroyItem(ITEM& item, DWORD dwOwnerPID, HWND *phwndBroker);
90 
92  BOOL DeliverNotification(HANDLE hTicket, DWORD dwOwnerPID);
93  BOOL ShouldNotify(LPDELITICKET pTicket, LPREGENTRY pRegEntry);
94 };
95 
97  : m_nNextRegID(INVALID_REG_ID)
98 {
99 }
100 
102 {
103 }
104 
105 BOOL CChangeNotifyServer::AddItem(UINT nRegID, DWORD dwUserPID, HANDLE hRegEntry,
106  HWND hwndBroker, CDirectoryWatcher *pDirWatch)
107 {
108  // find the empty room
109  for (INT i = 0; i < m_items.GetSize(); ++i)
110  {
111  if (m_items[i].nRegID == INVALID_REG_ID)
112  {
113  // found the room, populate it
114  m_items[i].nRegID = nRegID;
115  m_items[i].dwUserPID = dwUserPID;
116  m_items[i].hRegEntry = hRegEntry;
117  m_items[i].hwndBroker = hwndBroker;
118  m_items[i].pDirWatch = pDirWatch;
119  return TRUE;
120  }
121  }
122 
123  // no empty room found
124  ITEM item = { nRegID, dwUserPID, hRegEntry, hwndBroker, pDirWatch };
125  m_items.Add(item);
126  return TRUE;
127 }
128 
129 void CChangeNotifyServer::DestroyItem(ITEM& item, DWORD dwOwnerPID, HWND *phwndBroker)
130 {
131  // destroy broker if any and if first time
132  HWND hwndBroker = item.hwndBroker;
133  item.hwndBroker = NULL;
134  if (hwndBroker && hwndBroker != *phwndBroker)
135  {
136  ::DestroyWindow(hwndBroker);
137  *phwndBroker = hwndBroker;
138  }
139 
140  // request termination of pDirWatch if any
141  CDirectoryWatcher *pDirWatch = item.pDirWatch;
142  item.pDirWatch = NULL;
143  if (pDirWatch)
144  pDirWatch->RequestTermination();
145 
146  // free
147  SHFreeShared(item.hRegEntry, dwOwnerPID);
148  item.nRegID = INVALID_REG_ID;
149  item.dwUserPID = 0;
150  item.hRegEntry = NULL;
151  item.hwndBroker = NULL;
152  item.pDirWatch = NULL;
153 }
154 
156 {
157  BOOL bFound = FALSE;
158  HWND hwndBroker = NULL;
159  assert(nRegID != INVALID_REG_ID);
160  for (INT i = 0; i < m_items.GetSize(); ++i)
161  {
162  if (m_items[i].nRegID == nRegID)
163  {
164  bFound = TRUE;
165  DestroyItem(m_items[i], dwOwnerPID, &hwndBroker);
166  }
167  }
168  return bFound;
169 }
170 
172 {
173  HWND hwndBroker = NULL;
174  assert(dwUserPID != 0);
175  for (INT i = 0; i < m_items.GetSize(); ++i)
176  {
177  if (m_items[i].dwUserPID == dwUserPID)
178  {
179  DestroyItem(m_items[i], dwOwnerPID, &hwndBroker);
180  }
181  }
182 }
183 
184 // create a CDirectoryWatcher from a REGENTRY
185 static CDirectoryWatcher *
187 {
188  if (pRegEntry->ibPidl == 0)
189  return NULL;
190 
191  // it must be interrupt level if pRegEntry is a filesystem watch
192  if (!(pRegEntry->fSources & SHCNRF_InterruptLevel))
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  HANDLE hNewEntry = SHAllocShared(pRegEntry, pRegEntry->cbSize, dwOwnerPID);
243  if (hNewEntry == NULL)
244  {
245  ERR("Out of memory\n");
246  pRegEntry->nRegID = INVALID_REG_ID;
247  SHUnlockShared(pRegEntry);
248  return FALSE;
249  }
250 
251  // create a directory watch if necessary
253  if (pDirWatch && !pDirWatch->RequestAddWatcher())
254  {
255  pRegEntry->nRegID = INVALID_REG_ID;
256  SHUnlockShared(pRegEntry);
257  delete pDirWatch;
258  return FALSE;
259  }
260 
261  // unlock the registry entry
262  SHUnlockShared(pRegEntry);
263 
264  // add an ITEM
265  return AddItem(m_nNextRegID, dwUserPID, hNewEntry, hwndBroker, pDirWatch);
266 }
267 
268 // Message CN_UNREGISTER: Unregister registration entries.
269 // wParam: The registration ID.
270 // lParam: Ignored.
271 // return: TRUE if successful.
273 {
274  TRACE("OnUnRegister(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam);
275 
276  // validate registration ID
277  UINT nRegID = (UINT)wParam;
278  if (nRegID == INVALID_REG_ID)
279  {
280  ERR("INVALID_REG_ID\n");
281  return FALSE;
282  }
283 
284  // remove it
285  DWORD dwOwnerPID;
286  GetWindowThreadProcessId(m_hWnd, &dwOwnerPID);
287  return RemoveItemsByRegID(nRegID, dwOwnerPID);
288 }
289 
290 // Message CN_DELIVER_NOTIFICATION: Perform a delivery.
291 // wParam: The handle of delivery ticket.
292 // lParam: The owner PID of delivery ticket.
293 // return: TRUE if necessary.
295 {
296  TRACE("OnDeliverNotification(%p, %u, %p, %p)\n", m_hWnd, uMsg, wParam, lParam);
297 
298  HANDLE hTicket = (HANDLE)wParam;
299  DWORD dwOwnerPID = (DWORD)lParam;
300 
301  // do delivery
302  BOOL ret = DeliverNotification(hTicket, dwOwnerPID);
303 
304  // free the ticket
305  SHFreeShared(hTicket, dwOwnerPID);
306  return ret;
307 }
308 
309 // Message CN_SUSPEND_RESUME: Suspend or resume the change notification.
310 // (specification is unknown)
312 {
313  TRACE("OnSuspendResume\n");
314 
315  // FIXME
316  return FALSE;
317 }
318 
319 // Message CN_UNREGISTER_PROCESS: Remove registration entries by PID.
320 // wParam: The user PID.
321 // lParam: Ignored.
322 // return: Zero.
324 {
325  DWORD dwOwnerPID, dwUserPID = (DWORD)wParam;
326  GetWindowThreadProcessId(m_hWnd, &dwOwnerPID);
327  RemoveItemsByProcess(dwOwnerPID, dwUserPID);
328  return 0;
329 }
330 
332 {
334  return 0;
335 }
336 
337 // get next valid registration ID
339 {
340  m_nNextRegID++;
342  m_nNextRegID++;
343  return m_nNextRegID;
344 }
345 
346 // This function is called from CChangeNotifyServer::OnDeliverNotification.
347 // The function notifies to the registration entries that should be notified.
349 {
350  TRACE("DeliverNotification(%p, %p, 0x%lx)\n", m_hWnd, hTicket, dwOwnerPID);
351 
352  // lock the delivery ticket
353  LPDELITICKET pTicket = (LPDELITICKET)SHLockSharedEx(hTicket, dwOwnerPID, FALSE);
354  if (pTicket == NULL || pTicket->dwMagic != DELITICKET_MAGIC)
355  {
356  ERR("pTicket is invalid\n");
357  SHUnlockShared(pTicket);
358  return FALSE;
359  }
360 
361  // for all items
362  for (INT i = 0; i < m_items.GetSize(); ++i)
363  {
364  // validate the item
365  if (m_items[i].nRegID == INVALID_REG_ID)
366  continue;
367 
368  HANDLE hRegEntry = m_items[i].hRegEntry;
369  if (hRegEntry == NULL)
370  continue;
371 
372  // lock the registration entry
373  LPREGENTRY pRegEntry = (LPREGENTRY)SHLockSharedEx(hRegEntry, dwOwnerPID, FALSE);
374  if (pRegEntry == NULL || pRegEntry->dwMagic != REGENTRY_MAGIC)
375  {
376  ERR("pRegEntry is invalid\n");
377  SHUnlockShared(pRegEntry);
378  continue;
379  }
380 
381  // should we notify for it?
382  BOOL bNotify = ShouldNotify(pTicket, pRegEntry);
383  if (bNotify)
384  {
385  // do notify
386  TRACE("Notifying: %p, 0x%x, %p, %lu\n",
387  pRegEntry->hwnd, pRegEntry->uMsg, hTicket, dwOwnerPID);
388  SendMessageW(pRegEntry->hwnd, pRegEntry->uMsg, (WPARAM)hTicket, dwOwnerPID);
389  }
390 
391  // unlock the registration entry
392  SHUnlockShared(pRegEntry);
393  }
394 
395  // unlock the ticket
396  SHUnlockShared(pTicket);
397 
398  return TRUE;
399 }
400 
402 {
403  LPITEMIDLIST pidl, pidl1 = NULL, pidl2 = NULL;
404  WCHAR szPath[MAX_PATH], szPath1[MAX_PATH], szPath2[MAX_PATH];
405  INT cch, cch1, cch2;
406 
407  // check fSources
408  if (pTicket->uFlags & SHCNE_INTERRUPT)
409  {
410  if (!(pRegEntry->fSources & SHCNRF_InterruptLevel))
411  return FALSE;
412  }
413  else
414  {
415  if (!(pRegEntry->fSources & SHCNRF_ShellLevel))
416  return FALSE;
417  }
418 
419  if (pRegEntry->ibPidl == 0)
420  return TRUE; // there is no PIDL
421 
422  // get the stored pidl
423  pidl = (LPITEMIDLIST)((LPBYTE)pRegEntry + pRegEntry->ibPidl);
424  if (pidl->mkid.cb == 0 && pRegEntry->fRecursive)
425  return TRUE; // desktop is the root
426 
427  // check pidl1
428  if (pTicket->ibOffset1)
429  {
430  pidl1 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset1);
431  if (ILIsEqual(pidl, pidl1) || ILIsParent(pidl, pidl1, !pRegEntry->fRecursive))
432  return TRUE;
433  }
434 
435  // check pidl2
436  if (pTicket->ibOffset2)
437  {
438  pidl2 = (LPITEMIDLIST)((LPBYTE)pTicket + pTicket->ibOffset2);
439  if (ILIsEqual(pidl, pidl2) || ILIsParent(pidl, pidl2, !pRegEntry->fRecursive))
440  return TRUE;
441  }
442 
443  // The paths:
444  // "C:\\Path\\To\\File1"
445  // "C:\\Path\\To\\File1Test"
446  // should be distinguished in comparison, so we add backslash at last as follows:
447  // "C:\\Path\\To\\File1\\"
448  // "C:\\Path\\To\\File1Test\\"
449  if (SHGetPathFromIDListW(pidl, szPath))
450  {
452  cch = lstrlenW(szPath);
453 
454  if (pidl1 && SHGetPathFromIDListW(pidl1, szPath1))
455  {
456  PathAddBackslashW(szPath1);
457  cch1 = lstrlenW(szPath1);
458 
459  // Is szPath1 a subfile or subdirectory of szPath?
460  if (cch < cch1 &&
461  (pRegEntry->fRecursive ||
462  wcschr(&szPath1[cch], L'\\') == &szPath1[cch1 - 1]))
463  {
464  szPath1[cch] = 0;
465  if (lstrcmpiW(szPath, szPath1) == 0)
466  return TRUE;
467  }
468  }
469 
470  if (pidl2 && SHGetPathFromIDListW(pidl2, szPath2))
471  {
472  PathAddBackslashW(szPath2);
473  cch2 = lstrlenW(szPath2);
474 
475  // Is szPath2 a subfile or subdirectory of szPath?
476  if (cch < cch2 &&
477  (pRegEntry->fRecursive ||
478  wcschr(&szPath2[cch], L'\\') == &szPath2[cch2 - 1]))
479  {
480  szPath2[cch] = 0;
481  if (lstrcmpiW(szPath, szPath2) == 0)
482  return TRUE;
483  }
484  }
485  }
486 
487  return FALSE;
488 }
489 
491 {
492  if (!phwnd)
493  return E_INVALIDARG;
494  *phwnd = m_hWnd;
495  return S_OK;
496 }
497 
499 {
500  return E_NOTIMPL;
501 }
502 
504 {
505  // This is called by CChangeNotifyServer_CreateInstance right after instantiation.
506  // Create the window of the server here.
507  Create(0);
508  if (!m_hWnd)
509  return E_FAIL;
510  return S_OK;
511 }
512 
514 {
515  return ShellObjectCreatorInit<CChangeNotifyServer>(riid, ppv);
516 }
#define WS_CLIPSIBLINGS
Definition: pedump.c:618
BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath)
Definition: path.c:1702
LRESULT OnRemoveByPID(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
#define REFIID
Definition: guiddef.h:118
#define TRUE
Definition: types.h:120
PVOID WINAPI SHLockSharedEx(HANDLE hData, DWORD dwProcessId, BOOL bWriteAccess)
LRESULT OnUnRegister(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
HRESULT CChangeNotifyServer_CreateInstance(REFIID riid, void **ppv)
REFIID riid
Definition: precomp.h:44
_In_ BOOLEAN Create
Definition: pstypes.h:519
BOOL WINAPI SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath)
Definition: pidl.c:1288
#define DECLARE_WND_CLASS_EX(WndClassName, style, bkgnd)
Definition: atlwin.h:1886
#define SHCNE_INTERRUPT
Definition: shlobj.h:1754
REFIID LPVOID * ppv
Definition: atlbase.h:39
#define DELITICKET_MAGIC
#define assert(x)
Definition: debug.h:53
#define DECLARE_NOT_AGGREGATABLE(x)
Definition: atlcom.h:611
LRESULT OnRegister(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
BOOL WINAPI ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
Definition: pidl.c:535
UINT_PTR WPARAM
Definition: windef.h:207
CSimpleArray< ITEM > m_items
#define REGENTRY_MAGIC
#define WS_CLIPCHILDREN
Definition: pedump.c:619
#define lstrlenW
Definition: compat.h:416
#define E_FAIL
Definition: ddrawi.h:102
BOOL WINAPI DestroyWindow(_In_ HWND)
#define WS_EX_TOOLWINDOW
Definition: winuser.h:404
#define DWORD
Definition: nt_native.h:44
CDirectoryWatcher * pDirWatch
int32_t INT
Definition: typedefs.h:57
WPARAM wParam
Definition: combotst.c:138
int WINAPI lstrcmpiW(LPCWSTR lpString1, LPCWSTR lpString2)
Definition: lstring.c:194
BOOL AddItem(UINT nRegID, DWORD dwUserPID, HANDLE hRegEntry, HWND hwndBroker, CDirectoryWatcher *pDirWatch)
LRESULT OnSuspendResume(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
static CDirectoryWatcher * CreateDirectoryWatcherFromRegEntry(LPREGENTRY pRegEntry)
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
#define INVALID_REG_ID
unsigned char * LPBYTE
Definition: typedefs.h:53
#define CN_REGISTER
void RemoveItemsByProcess(DWORD dwOwnerPID, DWORD dwUserPID)
LPWSTR WINAPI PathAddBackslashW(LPWSTR lpszPath)
Definition: path.c:289
unsigned int BOOL
Definition: ntddk_ex.h:94
BOOL RemoveItemsByRegID(UINT nRegID, DWORD dwOwnerPID)
DWORD WINAPI GetWindowThreadProcessId(HWND, PDWORD)
#define E_INVALIDARG
Definition: ddrawi.h:101
static DWORD DWORD void LPSTR DWORD cch
Definition: str.c:201
#define SHCNRF_InterruptLevel
Definition: shlobj.h:1774
smooth NULL
Definition: ftsmooth.c:416
#define CN_UNREGISTER_PROCESS
LONG_PTR LPARAM
Definition: windef.h:208
CWinTraits< WS_POPUP|WS_CLIPSIBLINGS|WS_CLIPCHILDREN, WS_EX_TOOLWINDOW > CChangeNotifyServerTraits
static void RequestAllWatchersTermination()
MESSAGE_HANDLER(CN_UNREGISTER_PROCESS, OnRemoveByPID)
#define WM_DESTROY
Definition: winuser.h:1591
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode)
static CDirectoryWatcher * Create(LPCWSTR pszDirectoryPath, BOOL fSubTree)
#define BEGIN_COM_MAP(x)
Definition: atlcom.h:541
#define TRACE(s)
Definition: solgame.cpp:4
_CONST_RETURN wchar_t *__cdecl wcschr(_In_z_ const wchar_t *_Str, wchar_t _Ch)
virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *lphwnd)
LRESULT OnDeliverNotification(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
HANDLE WINAPI SHAllocShared(LPCVOID lpvData, DWORD dwSize, DWORD dwProcId)
Definition: ordinal.c:162
__wchar_t WCHAR
Definition: xmlstorage.h:180
LONG HRESULT
Definition: typedefs.h:78
#define END_MSG_MAP()
Definition: atlwin.h:1799
#define MAX_PATH
Definition: compat.h:26
#define WINAPI
Definition: msvc.h:6
#define SHCNRF_ShellLevel
Definition: shlobj.h:1775
#define STDMETHODCALLTYPE
Definition: bdasup.h:9
PVOID HANDLE
Definition: typedefs.h:72
unsigned long DWORD
Definition: ntddk_ex.h:95
BOOL WINAPI SHFreeShared(HANDLE hShared, DWORD dwProcId)
Definition: ordinal.c:308
int ret
static const WCHAR L[]
Definition: oid.c:1250
#define CN_DELIVER_NOTIFICATION
BOOL ShouldNotify(LPDELITICKET pTicket, LPREGENTRY pRegEntry)
HANDLE hRegEntry
#define ERR(fmt,...)
Definition: debug.h:110
#define CN_UNREGISTER
BOOL WINAPI ILIsParent(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild, BOOL bImmediate)
Definition: pidl.c:592
#define S_OK
Definition: intsafe.h:59
static ATOM item
Definition: dde.c:856
LPCWSTR szPath
Definition: env.c:35
BOOL DeliverNotification(HANDLE hTicket, DWORD dwOwnerPID)
#define COM_INTERFACE_ENTRY_IID(iid, x)
Definition: atlcom.h:561
LRESULT WINAPI SendMessageW(_In_ HWND, _In_ UINT, _In_ WPARAM, _In_ LPARAM)
#define E_NOTIMPL
Definition: ddrawi.h:99
#define WS_POPUP
Definition: pedump.c:616
unsigned int UINT
Definition: ndis.h:50
struct DELITICKET * LPDELITICKET
#define BEGIN_MSG_MAP(theClass)
Definition: atlwin.h:1780
#define DECLARE_PROTECT_FINAL_CONSTRUCT()
Definition: atlcom.h:639
WINE_DEFAULT_DEBUG_CHANNEL(shcn)
const GUID IID_IOleWindow
BOOL WINAPI SHUnlockShared(LPVOID lpView)
Definition: ordinal.c:288
#define END_COM_MAP()
Definition: atlcom.h:552
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
LONG_PTR LRESULT
Definition: windef.h:209
void DestroyItem(ITEM &item, DWORD dwOwnerPID, HWND *phwndBroker)
ITEMIDLIST UNALIGNED * LPITEMIDLIST
Definition: shtypes.idl:41
LPARAM lParam
Definition: combotst.c:139
struct REGENTRY * LPREGENTRY
#define CN_SUSPEND_RESUME