mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / shell32 / changenotify.c
bloba87913c1f0e6c1ec127c169d7853bd888e438c4f
1 /*
2 * shell change notification
4 * Copyright 2000 Juergen Schmied
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <string.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wine/list.h"
27 #include "wine/debug.h"
28 #include "shell32_main.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(shell);
32 static CRITICAL_SECTION SHELL32_ChangenotifyCS;
33 static CRITICAL_SECTION_DEBUG critsect_debug =
35 0, 0, &SHELL32_ChangenotifyCS,
36 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
37 0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_ChangenotifyCS") }
39 static CRITICAL_SECTION SHELL32_ChangenotifyCS = { &critsect_debug, -1, 0, 0, 0, 0 };
41 typedef SHChangeNotifyEntry *LPNOTIFYREGISTER;
43 /* internal list of notification clients (internal) */
44 typedef struct _NOTIFICATIONLIST
46 struct list entry;
47 HWND hwnd; /* window to notify */
48 DWORD uMsg; /* message to send */
49 LPNOTIFYREGISTER apidl; /* array of entries to watch*/
50 UINT cidl; /* number of pidls in array */
51 LONG wEventMask; /* subscribed events */
52 DWORD dwFlags; /* client flags */
53 ULONG id;
54 } NOTIFICATIONLIST, *LPNOTIFICATIONLIST;
56 static struct list notifications = LIST_INIT( notifications );
57 static LONG next_id;
59 #define SHCNE_NOITEMEVENTS ( \
60 SHCNE_ASSOCCHANGED )
62 #define SHCNE_ONEITEMEVENTS ( \
63 SHCNE_ATTRIBUTES | SHCNE_CREATE | SHCNE_DELETE | SHCNE_DRIVEADD | \
64 SHCNE_DRIVEADDGUI | SHCNE_DRIVEREMOVED | SHCNE_FREESPACE | \
65 SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | SHCNE_MKDIR | \
66 SHCNE_NETSHARE | SHCNE_NETUNSHARE | SHCNE_RMDIR | \
67 SHCNE_SERVERDISCONNECT | SHCNE_UPDATEDIR | SHCNE_UPDATEIMAGE | \
68 SHCNE_UPDATEITEM )
70 #define SHCNE_TWOITEMEVENTS ( \
71 SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM )
73 /* for dumping events */
74 static const char * DumpEvent( LONG event )
76 if( event == SHCNE_ALLEVENTS )
77 return "SHCNE_ALLEVENTS";
78 #define DUMPEV(x) ,( event & SHCNE_##x )? #x " " : ""
79 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"
80 DUMPEV(RENAMEITEM)
81 DUMPEV(CREATE)
82 DUMPEV(DELETE)
83 DUMPEV(MKDIR)
84 DUMPEV(RMDIR)
85 DUMPEV(MEDIAINSERTED)
86 DUMPEV(MEDIAREMOVED)
87 DUMPEV(DRIVEREMOVED)
88 DUMPEV(DRIVEADD)
89 DUMPEV(NETSHARE)
90 DUMPEV(NETUNSHARE)
91 DUMPEV(ATTRIBUTES)
92 DUMPEV(UPDATEDIR)
93 DUMPEV(UPDATEITEM)
94 DUMPEV(SERVERDISCONNECT)
95 DUMPEV(UPDATEIMAGE)
96 DUMPEV(DRIVEADDGUI)
97 DUMPEV(RENAMEFOLDER)
98 DUMPEV(FREESPACE)
99 DUMPEV(EXTENDED_EVENT)
100 DUMPEV(ASSOCCHANGED)
101 DUMPEV(INTERRUPT)
103 #undef DUMPEV
106 static const char * NodeName(const NOTIFICATIONLIST *item)
108 const char *str;
109 WCHAR path[MAX_PATH];
111 if(SHGetPathFromIDListW(item->apidl[0].pidl, path ))
112 str = wine_dbg_sprintf("%s", debugstr_w(path));
113 else
114 str = wine_dbg_sprintf("<not a disk file>" );
115 return str;
118 static void DeleteNode(LPNOTIFICATIONLIST item)
120 UINT i;
122 TRACE("item=%p\n", item);
124 /* remove item from list */
125 list_remove( &item->entry );
127 /* free the item */
128 for (i=0; i<item->cidl; i++)
129 SHFree((LPITEMIDLIST)item->apidl[i].pidl);
130 SHFree(item->apidl);
131 SHFree(item);
134 void InitChangeNotifications(void)
138 void FreeChangeNotifications(void)
140 LPNOTIFICATIONLIST ptr, next;
142 TRACE("\n");
144 EnterCriticalSection(&SHELL32_ChangenotifyCS);
146 LIST_FOR_EACH_ENTRY_SAFE( ptr, next, &notifications, NOTIFICATIONLIST, entry )
147 DeleteNode( ptr );
149 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
151 DeleteCriticalSection(&SHELL32_ChangenotifyCS);
154 /*************************************************************************
155 * SHChangeNotifyRegister [SHELL32.2]
158 ULONG WINAPI
159 SHChangeNotifyRegister(
160 HWND hwnd,
161 int fSources,
162 LONG wEventMask,
163 UINT uMsg,
164 int cItems,
165 SHChangeNotifyEntry *lpItems)
167 LPNOTIFICATIONLIST item;
168 int i;
170 item = SHAlloc(sizeof(NOTIFICATIONLIST));
172 TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p) item=%p\n",
173 hwnd, fSources, wEventMask, uMsg, cItems, lpItems, item);
175 item->cidl = cItems;
176 item->apidl = SHAlloc(sizeof(SHChangeNotifyEntry) * cItems);
177 for(i=0;i<cItems;i++)
179 item->apidl[i].pidl = ILClone(lpItems[i].pidl);
180 item->apidl[i].fRecursive = lpItems[i].fRecursive;
182 item->hwnd = hwnd;
183 item->uMsg = uMsg;
184 item->wEventMask = wEventMask;
185 item->dwFlags = fSources;
186 item->id = InterlockedIncrement( &next_id );
188 TRACE("new node: %s\n", NodeName( item ));
190 EnterCriticalSection(&SHELL32_ChangenotifyCS);
192 list_add_tail( &notifications, &item->entry );
194 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
196 return item->id;
199 /*************************************************************************
200 * SHChangeNotifyDeregister [SHELL32.4]
202 BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify)
204 LPNOTIFICATIONLIST node;
206 TRACE("(0x%08x)\n", hNotify);
208 EnterCriticalSection(&SHELL32_ChangenotifyCS);
210 LIST_FOR_EACH_ENTRY( node, &notifications, NOTIFICATIONLIST, entry )
212 if (node->id == hNotify)
214 DeleteNode( node );
215 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
216 return TRUE;
219 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
220 return FALSE;
223 /*************************************************************************
224 * SHChangeNotifyUpdateEntryList [SHELL32.5]
226 BOOL WINAPI SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2,
227 DWORD unknown3, DWORD unknown4)
229 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
230 unknown1, unknown2, unknown3, unknown4);
232 return TRUE;
235 struct new_delivery_notification
237 LONG event;
238 DWORD pidl1_size;
239 DWORD pidl2_size;
240 LPITEMIDLIST pidls[2];
241 BYTE data[1];
244 static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL sub )
246 TRACE("%p %p %d\n", changed, watched, sub );
247 if ( !watched )
248 return FALSE;
249 if (ILIsEqual( watched, changed ) )
250 return TRUE;
251 if( sub && ILIsParent( watched, changed, FALSE ) )
252 return TRUE;
253 return FALSE;
256 /*************************************************************************
257 * SHChangeNotify [SHELL32.@]
259 void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
261 struct notification_recipients {
262 struct list entry;
263 HWND hwnd;
264 DWORD msg;
265 DWORD flags;
266 } *cur, *next;
268 HANDLE shared_data = NULL;
269 LPITEMIDLIST Pidls[2];
270 LPNOTIFICATIONLIST ptr;
271 struct list recipients;
273 Pidls[0] = NULL;
274 Pidls[1] = NULL;
276 TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2);
278 if(uFlags & ~(SHCNF_TYPE|SHCNF_FLUSH))
279 FIXME("ignoring unsupported flags: %x\n", uFlags);
281 if( ( wEventId & SHCNE_NOITEMEVENTS ) && ( dwItem1 || dwItem2 ) )
283 TRACE("dwItem1 and dwItem2 are not zero, but should be\n");
284 dwItem1 = 0;
285 dwItem2 = 0;
286 return;
288 else if( ( wEventId & SHCNE_ONEITEMEVENTS ) && dwItem2 )
290 TRACE("dwItem2 is not zero, but should be\n");
291 dwItem2 = 0;
292 return;
295 if( ( ( wEventId & SHCNE_NOITEMEVENTS ) &&
296 ( wEventId & ~SHCNE_NOITEMEVENTS ) ) ||
297 ( ( wEventId & SHCNE_ONEITEMEVENTS ) &&
298 ( wEventId & ~SHCNE_ONEITEMEVENTS ) ) ||
299 ( ( wEventId & SHCNE_TWOITEMEVENTS ) &&
300 ( wEventId & ~SHCNE_TWOITEMEVENTS ) ) )
302 WARN("mutually incompatible events listed\n");
303 return;
306 /* convert paths in IDLists*/
307 switch (uFlags & SHCNF_TYPE)
309 case SHCNF_PATHA:
310 if (dwItem1) Pidls[0] = SHSimpleIDListFromPathA(dwItem1);
311 if (dwItem2) Pidls[1] = SHSimpleIDListFromPathA(dwItem2);
312 break;
313 case SHCNF_PATHW:
314 if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW(dwItem1);
315 if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW(dwItem2);
316 break;
317 case SHCNF_IDLIST:
318 Pidls[0] = ILClone(dwItem1);
319 Pidls[1] = ILClone(dwItem2);
320 break;
321 case SHCNF_PRINTERA:
322 case SHCNF_PRINTERW:
323 FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n");
324 return;
325 case SHCNF_DWORD:
326 default:
327 FIXME("unknown type %08x\n", uFlags & SHCNF_TYPE);
328 return;
331 list_init(&recipients);
332 EnterCriticalSection(&SHELL32_ChangenotifyCS);
333 LIST_FOR_EACH_ENTRY( ptr, &notifications, NOTIFICATIONLIST, entry )
335 struct notification_recipients *item;
336 BOOL notify = FALSE;
337 DWORD i;
339 for( i=0; (i<ptr->cidl) && !notify ; i++ )
341 LPCITEMIDLIST pidl = ptr->apidl[i].pidl;
342 BOOL subtree = ptr->apidl[i].fRecursive;
344 if (wEventId & ptr->wEventMask)
346 if( !pidl ) /* all ? */
347 notify = TRUE;
348 else if( wEventId & SHCNE_NOITEMEVENTS )
349 notify = TRUE;
350 else if( wEventId & ( SHCNE_ONEITEMEVENTS | SHCNE_TWOITEMEVENTS ) )
351 notify = should_notify( Pidls[0], pidl, subtree );
352 else if( wEventId & SHCNE_TWOITEMEVENTS )
353 notify = should_notify( Pidls[1], pidl, subtree );
357 if( !notify )
358 continue;
360 item = SHAlloc(sizeof(struct notification_recipients));
361 if(!item) {
362 ERR("out of memory\n");
363 continue;
366 item->hwnd = ptr->hwnd;
367 item->msg = ptr->uMsg;
368 item->flags = ptr->dwFlags;
369 list_add_tail(&recipients, &item->entry);
371 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
373 LIST_FOR_EACH_ENTRY_SAFE(cur, next, &recipients, struct notification_recipients, entry)
375 TRACE("notifying %p, event %s(%x)\n", cur->hwnd, DumpEvent(wEventId), wEventId);
377 if (cur->flags & SHCNRF_NewDelivery) {
378 if(!shared_data) {
379 struct new_delivery_notification *notification;
380 UINT size1 = ILGetSize(Pidls[0]), size2 = ILGetSize(Pidls[1]);
381 UINT offset = (size1+sizeof(int)-1)/sizeof(int)*sizeof(int);
383 notification = SHAlloc(sizeof(struct new_delivery_notification)+offset+size2);
384 if(!notification) {
385 ERR("out of memory\n");
386 } else {
387 notification->event = wEventId;
388 notification->pidl1_size = size1;
389 notification->pidl2_size = size2;
390 if(size1)
391 memcpy(notification->data, Pidls[0], size1);
392 if(size2)
393 memcpy(notification->data+offset, Pidls[1], size2);
395 shared_data = SHAllocShared(notification,
396 sizeof(struct new_delivery_notification)+size1+size2,
397 GetCurrentProcessId());
398 SHFree(notification);
402 if(shared_data)
403 SendMessageA(cur->hwnd, cur->msg, (WPARAM)shared_data, GetCurrentProcessId());
404 else
405 ERR("out of memory\n");
406 } else {
407 SendMessageA(cur->hwnd, cur->msg, (WPARAM)Pidls, wEventId);
410 list_remove(&cur->entry);
411 SHFree(cur);
413 SHFreeShared(shared_data, GetCurrentProcessId());
414 SHFree(Pidls[0]);
415 SHFree(Pidls[1]);
417 if (wEventId & SHCNE_ASSOCCHANGED)
419 static const WCHAR args[] = {' ','-','a',0 };
420 TRACE("refreshing file type associations\n");
421 run_winemenubuilder( args );
425 /*************************************************************************
426 * NTSHChangeNotifyRegister [SHELL32.640]
427 * NOTES
428 * Idlist is an array of structures and Count specifies how many items in the array
429 * (usually just one I think).
431 DWORD WINAPI NTSHChangeNotifyRegister(
432 HWND hwnd,
433 LONG events1,
434 LONG events2,
435 DWORD msg,
436 int count,
437 SHChangeNotifyEntry *idlist)
439 FIXME("(%p,0x%08x,0x%08x,0x%08x,0x%08x,%p):semi stub.\n",
440 hwnd,events1,events2,msg,count,idlist);
442 return SHChangeNotifyRegister(hwnd, events1, events2, msg, count, idlist);
445 /*************************************************************************
446 * SHChangeNotification_Lock [SHELL32.644]
448 HANDLE WINAPI SHChangeNotification_Lock(
449 HANDLE hChange,
450 DWORD dwProcessId,
451 LPITEMIDLIST **lppidls,
452 LPLONG lpwEventId)
454 struct new_delivery_notification *ndn;
455 UINT offset;
457 TRACE("%p %08x %p %p\n", hChange, dwProcessId, lppidls, lpwEventId);
459 ndn = SHLockShared(hChange, dwProcessId);
460 if(!ndn) {
461 WARN("SHLockShared failed\n");
462 return NULL;
465 if(lppidls) {
466 offset = (ndn->pidl1_size+sizeof(int)-1)/sizeof(int)*sizeof(int);
467 ndn->pidls[0] = ndn->pidl1_size ? (LPITEMIDLIST)ndn->data : NULL;
468 ndn->pidls[1] = ndn->pidl2_size ? (LPITEMIDLIST)(ndn->data+offset) : NULL;
469 *lppidls = ndn->pidls;
472 if(lpwEventId)
473 *lpwEventId = ndn->event;
475 return ndn;
478 /*************************************************************************
479 * SHChangeNotification_Unlock [SHELL32.645]
481 BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock)
483 TRACE("%p\n", hLock);
484 return SHUnlockShared(hLock);
487 /*************************************************************************
488 * NTSHChangeNotifyDeregister [SHELL32.641]
490 DWORD WINAPI NTSHChangeNotifyDeregister(ULONG x1)
492 FIXME("(0x%08x):semi stub.\n",x1);
494 return SHChangeNotifyDeregister( x1 );