fa471667342114abeaaee9bb9e3de2eb719956fe
[reactos.git] / reactos / dll / win32 / shell32 / wine / changenotify.c
1 /*
2 * shell change notification
3 *
4 * Copyright 2000 Juergen Schmied
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #define WIN32_NO_STATUS
22 #define _INC_WINDOWS
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
25
26 #include <windef.h>
27 #include <winbase.h>
28 #include <shlobj.h>
29 #include <undocshell.h>
30 #include <shlwapi.h>
31 #include <wine/debug.h>
32 #include <wine/list.h>
33
34 #include "pidl.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(shell);
37
38 static CRITICAL_SECTION SHELL32_ChangenotifyCS;
39 static CRITICAL_SECTION_DEBUG critsect_debug =
40 {
41 0, 0, &SHELL32_ChangenotifyCS,
42 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
43 0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_ChangenotifyCS") }
44 };
45 static CRITICAL_SECTION SHELL32_ChangenotifyCS = { &critsect_debug, -1, 0, 0, 0, 0 };
46
47 typedef SHChangeNotifyEntry *LPNOTIFYREGISTER;
48
49 /* internal list of notification clients (internal) */
50 typedef struct _NOTIFICATIONLIST
51 {
52 struct list entry;
53 HWND hwnd; /* window to notify */
54 DWORD uMsg; /* message to send */
55 LPNOTIFYREGISTER apidl; /* array of entries to watch*/
56 UINT cidl; /* number of pidls in array */
57 LONG wEventMask; /* subscribed events */
58 DWORD dwFlags; /* client flags */
59 ULONG id;
60 } NOTIFICATIONLIST, *LPNOTIFICATIONLIST;
61
62 static struct list notifications = LIST_INIT( notifications );
63 static LONG next_id;
64
65 #define SHCNE_NOITEMEVENTS ( \
66 SHCNE_ASSOCCHANGED )
67
68 #define SHCNE_ONEITEMEVENTS ( \
69 SHCNE_ATTRIBUTES | SHCNE_CREATE | SHCNE_DELETE | SHCNE_DRIVEADD | \
70 SHCNE_DRIVEADDGUI | SHCNE_DRIVEREMOVED | SHCNE_FREESPACE | \
71 SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | SHCNE_MKDIR | \
72 SHCNE_NETSHARE | SHCNE_NETUNSHARE | SHCNE_RMDIR | \
73 SHCNE_SERVERDISCONNECT | SHCNE_UPDATEDIR | SHCNE_UPDATEIMAGE )
74
75 #define SHCNE_TWOITEMEVENTS ( \
76 SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEITEM )
77
78 /* for dumping events */
79 static const char * DumpEvent( LONG event )
80 {
81 if( event == SHCNE_ALLEVENTS )
82 return "SHCNE_ALLEVENTS";
83 #define DUMPEV(x) ,( event & SHCNE_##x )? #x " " : ""
84 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"
85 DUMPEV(RENAMEITEM)
86 DUMPEV(CREATE)
87 DUMPEV(DELETE)
88 DUMPEV(MKDIR)
89 DUMPEV(RMDIR)
90 DUMPEV(MEDIAINSERTED)
91 DUMPEV(MEDIAREMOVED)
92 DUMPEV(DRIVEREMOVED)
93 DUMPEV(DRIVEADD)
94 DUMPEV(NETSHARE)
95 DUMPEV(NETUNSHARE)
96 DUMPEV(ATTRIBUTES)
97 DUMPEV(UPDATEDIR)
98 DUMPEV(UPDATEITEM)
99 DUMPEV(SERVERDISCONNECT)
100 DUMPEV(UPDATEIMAGE)
101 DUMPEV(DRIVEADDGUI)
102 DUMPEV(RENAMEFOLDER)
103 DUMPEV(FREESPACE)
104 DUMPEV(EXTENDED_EVENT)
105 DUMPEV(ASSOCCHANGED)
106 DUMPEV(INTERRUPT)
107 );
108 #undef DUMPEV
109 }
110
111 static const char * NodeName(const NOTIFICATIONLIST *item)
112 {
113 const char *str;
114 WCHAR path[MAX_PATH];
115
116 if(SHGetPathFromIDListW(item->apidl[0].pidl, path ))
117 str = wine_dbg_sprintf("%s", debugstr_w(path));
118 else
119 str = wine_dbg_sprintf("<not a disk file>" );
120 return str;
121 }
122
123 static void DeleteNode(LPNOTIFICATIONLIST item)
124 {
125 UINT i;
126
127 TRACE("item=%p\n", item);
128
129 /* remove item from list */
130 list_remove( &item->entry );
131
132 /* free the item */
133 for (i=0; i<item->cidl; i++)
134 SHFree((LPITEMIDLIST)item->apidl[i].pidl);
135 SHFree(item->apidl);
136 SHFree(item);
137 }
138
139 void InitChangeNotifications(void)
140 {
141 }
142
143 void FreeChangeNotifications(void)
144 {
145 LPNOTIFICATIONLIST ptr, next;
146
147 TRACE("\n");
148
149 EnterCriticalSection(&SHELL32_ChangenotifyCS);
150
151 LIST_FOR_EACH_ENTRY_SAFE( ptr, next, &notifications, NOTIFICATIONLIST, entry )
152 DeleteNode( ptr );
153
154 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
155
156 DeleteCriticalSection(&SHELL32_ChangenotifyCS);
157 }
158
159 /*************************************************************************
160 * SHChangeNotifyRegister [SHELL32.2]
161 *
162 */
163 ULONG WINAPI
164 SHChangeNotifyRegister(
165 HWND hwnd,
166 int fSources,
167 LONG wEventMask,
168 UINT uMsg,
169 int cItems,
170 SHChangeNotifyEntry *lpItems)
171 {
172 LPNOTIFICATIONLIST item;
173 int i;
174
175 item = SHAlloc(sizeof(NOTIFICATIONLIST));
176
177 TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p) item=%p\n",
178 hwnd, fSources, wEventMask, uMsg, cItems, lpItems, item);
179
180 item->cidl = cItems;
181 item->apidl = SHAlloc(sizeof(SHChangeNotifyEntry) * cItems);
182 for(i=0;i<cItems;i++)
183 {
184 item->apidl[i].pidl = ILClone(lpItems[i].pidl);
185 item->apidl[i].fRecursive = lpItems[i].fRecursive;
186 }
187 item->hwnd = hwnd;
188 item->uMsg = uMsg;
189 item->wEventMask = wEventMask;
190 item->dwFlags = fSources;
191 item->id = InterlockedIncrement( &next_id );
192
193 TRACE("new node: %s\n", NodeName( item ));
194
195 EnterCriticalSection(&SHELL32_ChangenotifyCS);
196
197 list_add_tail( &notifications, &item->entry );
198
199 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
200
201 return item->id;
202 }
203
204 /*************************************************************************
205 * SHChangeNotifyDeregister [SHELL32.4]
206 */
207 BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify)
208 {
209 LPNOTIFICATIONLIST node;
210
211 TRACE("(0x%08x)\n", hNotify);
212
213 EnterCriticalSection(&SHELL32_ChangenotifyCS);
214
215 LIST_FOR_EACH_ENTRY( node, &notifications, NOTIFICATIONLIST, entry )
216 {
217 if (node->id == hNotify)
218 {
219 DeleteNode( node );
220 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
221 return TRUE;
222 }
223 }
224 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
225 return FALSE;
226 }
227
228 /*************************************************************************
229 * SHChangeNotifyUpdateEntryList [SHELL32.5]
230 */
231 BOOL WINAPI SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2,
232 DWORD unknown3, DWORD unknown4)
233 {
234 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
235 unknown1, unknown2, unknown3, unknown4);
236
237 return TRUE;
238 }
239
240 struct new_delivery_notification
241 {
242 LONG event;
243 DWORD pidl1_size;
244 DWORD pidl2_size;
245 LPITEMIDLIST pidls[2];
246 BYTE data[1];
247 };
248
249 static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL sub )
250 {
251 TRACE("%p %p %d\n", changed, watched, sub );
252 if ( !watched )
253 return FALSE;
254 if (ILIsEqual( watched, changed ) )
255 return TRUE;
256 if( sub && ILIsParent( watched, changed, FALSE ) )
257 return TRUE;
258 return FALSE;
259 }
260
261 /*************************************************************************
262 * SHChangeNotify [SHELL32.@]
263 */
264 void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
265 {
266 struct notification_recipients {
267 struct list entry;
268 HWND hwnd;
269 DWORD msg;
270 DWORD flags;
271 } *cur, *next;
272
273 HANDLE shared_data = NULL;
274 LPITEMIDLIST Pidls[2];
275 LPNOTIFICATIONLIST ptr;
276 struct list recipients;
277
278 Pidls[0] = NULL;
279 Pidls[1] = NULL;
280
281 TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2);
282
283 if(uFlags & ~(SHCNF_TYPE|SHCNF_FLUSH))
284 FIXME("ignoring unsupported flags: %x\n", uFlags);
285
286 if( ( wEventId & SHCNE_NOITEMEVENTS ) && ( dwItem1 || dwItem2 ) )
287 {
288 TRACE("dwItem1 and dwItem2 are not zero, but should be\n");
289 dwItem1 = 0;
290 dwItem2 = 0;
291 return;
292 }
293 else if( ( wEventId & SHCNE_ONEITEMEVENTS ) && dwItem2 )
294 {
295 TRACE("dwItem2 is not zero, but should be\n");
296 dwItem2 = 0;
297 return;
298 }
299
300 if( ( ( wEventId & SHCNE_NOITEMEVENTS ) &&
301 ( wEventId & ~SHCNE_NOITEMEVENTS ) ) ||
302 ( ( wEventId & SHCNE_ONEITEMEVENTS ) &&
303 ( wEventId & ~SHCNE_ONEITEMEVENTS ) ) ||
304 ( ( wEventId & SHCNE_TWOITEMEVENTS ) &&
305 ( wEventId & ~SHCNE_TWOITEMEVENTS ) ) )
306 {
307 WARN("mutually incompatible events listed\n");
308 return;
309 }
310
311 /* convert paths in IDLists*/
312 switch (uFlags & SHCNF_TYPE)
313 {
314 case SHCNF_PATHA:
315 if (dwItem1) Pidls[0] = SHSimpleIDListFromPathA(dwItem1); //FIXME
316 if (dwItem2) Pidls[1] = SHSimpleIDListFromPathA(dwItem2); //FIXME
317 break;
318 case SHCNF_PATHW:
319 if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW(dwItem1);
320 if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW(dwItem2);
321 #ifdef __REACTOS__
322 if (wEventId & (SHCNE_MKDIR | SHCNE_RMDIR | SHCNE_UPDATEDIR | SHCNE_RENAMEFOLDER))
323 {
324 /*
325 * The last items in the ID are currently files. So we chop off the last
326 * entry, and create a new one using a find data struct.
327 */
328 if (dwItem1 && Pidls[0]){
329 WIN32_FIND_DATAW wfd;
330 LPITEMIDLIST oldpidl, newpidl;
331 LPWSTR p = PathFindFileNameW((LPCWSTR)dwItem1);
332 ILRemoveLastID(Pidls[0]);
333 lstrcpynW(&wfd.cFileName[0], p, MAX_PATH);
334 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
335 newpidl = _ILCreateFromFindDataW(&wfd);
336 oldpidl = ILClone(Pidls[0]);
337 ILFree(Pidls[0]);
338 Pidls[0] = ILCombine(oldpidl, newpidl);
339 ILFree(newpidl);
340 ILFree(oldpidl);
341 }
342 if (dwItem2 && Pidls[1]){
343 WIN32_FIND_DATAW wfd;
344 LPITEMIDLIST oldpidl, newpidl;
345 LPWSTR p = PathFindFileNameW((LPCWSTR)dwItem2);
346 ILRemoveLastID(Pidls[1]);
347 lstrcpynW(&wfd.cFileName[0], p, MAX_PATH);
348 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
349 newpidl = _ILCreateFromFindDataW(&wfd);
350 oldpidl = ILClone(Pidls[0]);
351 ILFree(Pidls[1]);
352 Pidls[1] = ILCombine(oldpidl, newpidl);
353 ILFree(newpidl);
354 ILFree(oldpidl);
355 }
356 }
357 #endif
358 break;
359 case SHCNF_IDLIST:
360 Pidls[0] = ILClone(dwItem1);
361 Pidls[1] = ILClone(dwItem2);
362 break;
363 case SHCNF_PRINTERA:
364 case SHCNF_PRINTERW:
365 FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n");
366 return;
367 case SHCNF_DWORD:
368 default:
369 FIXME("unknown type %08x\n", uFlags & SHCNF_TYPE);
370 return;
371 }
372
373 list_init(&recipients);
374 EnterCriticalSection(&SHELL32_ChangenotifyCS);
375 LIST_FOR_EACH_ENTRY( ptr, &notifications, NOTIFICATIONLIST, entry )
376 {
377 struct notification_recipients *item;
378 BOOL notify = FALSE;
379 DWORD i;
380
381 for( i=0; (i<ptr->cidl) && !notify ; i++ )
382 {
383 LPCITEMIDLIST pidl = ptr->apidl[i].pidl;
384 BOOL subtree = ptr->apidl[i].fRecursive;
385
386 if (wEventId & ptr->wEventMask)
387 {
388 if( !pidl ) /* all ? */
389 notify = TRUE;
390 else if( wEventId & SHCNE_NOITEMEVENTS )
391 notify = TRUE;
392 else if( wEventId & ( SHCNE_ONEITEMEVENTS | SHCNE_TWOITEMEVENTS ) )
393 notify = should_notify( Pidls[0], pidl, subtree );
394 else if( wEventId & SHCNE_TWOITEMEVENTS )
395 notify = should_notify( Pidls[1], pidl, subtree );
396 }
397 }
398
399 if( !notify )
400 continue;
401
402 item = SHAlloc(sizeof(struct notification_recipients));
403 if(!item) {
404 ERR("out of memory\n");
405 continue;
406 }
407
408 item->hwnd = ptr->hwnd;
409 item->msg = ptr->uMsg;
410 item->flags = ptr->dwFlags;
411 list_add_tail(&recipients, &item->entry);
412 }
413 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
414
415 LIST_FOR_EACH_ENTRY_SAFE(cur, next, &recipients, struct notification_recipients, entry)
416 {
417 TRACE("notifying %p, event %s(%x)\n", cur->hwnd, DumpEvent(wEventId), wEventId);
418
419 if (cur->flags & SHCNRF_NewDelivery) {
420 if(!shared_data) {
421 struct new_delivery_notification *notification;
422 UINT size1 = ILGetSize(Pidls[0]), size2 = ILGetSize(Pidls[1]);
423 UINT offset = (size1+sizeof(int)-1)/sizeof(int)*sizeof(int);
424
425 notification = SHAlloc(sizeof(struct new_delivery_notification)+offset+size2);
426 if(!notification) {
427 ERR("out of memory\n");
428 } else {
429 notification->event = wEventId;
430 notification->pidl1_size = size1;
431 notification->pidl2_size = size2;
432 if(size1)
433 memcpy(notification->data, Pidls[0], size1);
434 if(size2)
435 memcpy(notification->data+offset, Pidls[1], size2);
436
437 shared_data = SHAllocShared(notification,
438 sizeof(struct new_delivery_notification)+size1+size2,
439 GetCurrentProcessId());
440 SHFree(notification);
441 }
442 }
443
444 if(shared_data)
445 SendMessageA(cur->hwnd, cur->msg, (WPARAM)shared_data, GetCurrentProcessId());
446 else
447 ERR("out of memory\n");
448 } else {
449 SendMessageA(cur->hwnd, cur->msg, (WPARAM)Pidls, wEventId);
450 }
451
452 list_remove(&cur->entry);
453 SHFree(cur);
454 }
455 SHFreeShared(shared_data, GetCurrentProcessId());
456 SHFree(Pidls[0]);
457 SHFree(Pidls[1]);
458
459 #ifndef __REACTOS__
460 if (wEventId & SHCNE_ASSOCCHANGED)
461 {
462 static const WCHAR args[] = {' ','-','a',0 };
463 TRACE("refreshing file type associations\n");
464 run_winemenubuilder( args );
465 }
466 #endif
467 }
468
469 /*************************************************************************
470 * NTSHChangeNotifyRegister [SHELL32.640]
471 * NOTES
472 * Idlist is an array of structures and Count specifies how many items in the array.
473 * count should always be one when calling SHChangeNotifyRegister, or
474 * SHChangeNotifyDeregister will not work properly.
475 */
476 EXTERN_C ULONG WINAPI NTSHChangeNotifyRegister(
477 HWND hwnd,
478 int fSources,
479 LONG fEvents,
480 UINT msg,
481 int count,
482 SHChangeNotifyEntry *idlist)
483 {
484 return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery,
485 fEvents, msg, count, idlist);
486 }
487
488 /*************************************************************************
489 * SHChangeNotification_Lock [SHELL32.644]
490 */
491 HANDLE WINAPI SHChangeNotification_Lock(
492 HANDLE hChange,
493 DWORD dwProcessId,
494 LPITEMIDLIST **lppidls,
495 LPLONG lpwEventId)
496 {
497 struct new_delivery_notification *ndn;
498 UINT offset;
499
500 TRACE("%p %08x %p %p\n", hChange, dwProcessId, lppidls, lpwEventId);
501
502 ndn = SHLockShared(hChange, dwProcessId);
503 if(!ndn) {
504 WARN("SHLockShared failed\n");
505 return NULL;
506 }
507
508 if(lppidls) {
509 offset = (ndn->pidl1_size+sizeof(int)-1)/sizeof(int)*sizeof(int);
510 ndn->pidls[0] = ndn->pidl1_size ? (LPITEMIDLIST)ndn->data : NULL;
511 ndn->pidls[1] = ndn->pidl2_size ? (LPITEMIDLIST)(ndn->data+offset) : NULL;
512 *lppidls = ndn->pidls;
513 }
514
515 if(lpwEventId)
516 *lpwEventId = ndn->event;
517
518 return ndn;
519 }
520
521 /*************************************************************************
522 * SHChangeNotification_Unlock [SHELL32.645]
523 */
524 BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock)
525 {
526 TRACE("%p\n", hLock);
527 return SHUnlockShared(hLock);
528 }
529
530 /*************************************************************************
531 * NTSHChangeNotifyDeregister [SHELL32.641]
532 */
533 DWORD WINAPI NTSHChangeNotifyDeregister(ULONG x1)
534 {
535 FIXME("(0x%08x):semi stub.\n",x1);
536
537 return SHChangeNotifyDeregister( x1 );
538 }