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
23 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
27 extern CRITICAL_SECTION SHELL32_ChangenotifyCS
;
28 CRITICAL_SECTION_DEBUG critsect_debug
=
30 0, 0, &SHELL32_ChangenotifyCS
,
31 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
32 0, 0, { (DWORD_PTR
)(__FILE__
": SHELL32_ChangenotifyCS") }
34 CRITICAL_SECTION SHELL32_ChangenotifyCS
= { &critsect_debug
, -1, 0, 0, 0, 0 };
37 typedef SHChangeNotifyEntry
*LPNOTIFYREGISTER
;
39 /* internal list of notification clients (internal) */
40 typedef struct _NOTIFICATIONLIST
42 struct _NOTIFICATIONLIST
*next
;
43 struct _NOTIFICATIONLIST
*prev
;
44 HWND hwnd
; /* window to notify */
45 DWORD uMsg
; /* message to send */
46 LPNOTIFYREGISTER apidl
; /* array of entries to watch*/
47 UINT cidl
; /* number of pidls in array */
48 LONG wEventMask
; /* subscribed events */
49 LONG wSignalledEvent
; /* event that occurred */
50 DWORD dwFlags
; /* client flags */
51 LPCITEMIDLIST pidlSignaled
; /*pidl of the path that caused the signal*/
53 } NOTIFICATIONLIST
, *LPNOTIFICATIONLIST
;
55 static NOTIFICATIONLIST
*head
, *tail
;
57 #define SHCNE_NOITEMEVENTS ( \
60 #define SHCNE_ONEITEMEVENTS ( \
61 SHCNE_ATTRIBUTES | SHCNE_CREATE | SHCNE_DELETE | SHCNE_DRIVEADD | \
62 SHCNE_DRIVEADDGUI | SHCNE_DRIVEREMOVED | SHCNE_FREESPACE | \
63 SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | SHCNE_MKDIR | \
64 SHCNE_NETSHARE | SHCNE_NETUNSHARE | SHCNE_RMDIR | \
65 SHCNE_SERVERDISCONNECT | SHCNE_UPDATEDIR | SHCNE_UPDATEIMAGE )
67 #define SHCNE_TWOITEMEVENTS ( \
68 SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEITEM )
70 /* for dumping events */
71 static const char * DumpEvent( LONG event
)
73 if( event
== SHCNE_ALLEVENTS
)
74 return "SHCNE_ALLEVENTS";
75 #define DUMPEV(x) ,( event & SHCNE_##x )? #x " " : ""
76 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"
91 DUMPEV(SERVERDISCONNECT
)
96 DUMPEV(EXTENDED_EVENT
)
103 static const char * NodeName(const NOTIFICATIONLIST
*item
)
106 WCHAR path
[MAX_PATH
];
108 if(SHGetPathFromIDListW(item
->apidl
[0].pidl
, path
))
109 str
= wine_dbg_sprintf("%s", debugstr_w(path
));
111 str
= wine_dbg_sprintf("<not a disk file>" );
115 static void AddNode(LPNOTIFICATIONLIST item
)
117 TRACE("item %p\n", item
);
129 static LPNOTIFICATIONLIST
FindNode( HANDLE hitem
)
131 LPNOTIFICATIONLIST ptr
;
132 for( ptr
= head
; ptr
; ptr
= ptr
->next
)
133 if( ptr
== (LPNOTIFICATIONLIST
) hitem
)
138 static void DeleteNode(LPNOTIFICATIONLIST item
)
142 TRACE("item=%p prev=%p next=%p\n", item
, item
->prev
, item
->next
);
144 /* remove item from list */
146 item
->prev
->next
= item
->next
;
150 item
->next
->prev
= item
->prev
;
155 for (i
=0; i
<item
->cidl
; i
++)
156 SHFree((LPITEMIDLIST
)item
->apidl
[i
].pidl
);
161 void InitChangeNotifications(void)
165 void FreeChangeNotifications(void)
169 EnterCriticalSection(&SHELL32_ChangenotifyCS
);
174 LeaveCriticalSection(&SHELL32_ChangenotifyCS
);
176 // DeleteCriticalSection(&SHELL32_ChangenotifyCS); // static
179 /*************************************************************************
180 * SHChangeNotifyRegister [SHELL32.2]
184 SHChangeNotifyRegister(
190 SHChangeNotifyEntry
*lpItems
)
192 LPNOTIFICATIONLIST item
;
195 item
= (NOTIFICATIONLIST
*)SHAlloc(sizeof(NOTIFICATIONLIST
));
197 TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p) item=%p\n",
198 hwnd
, fSources
, wEventMask
, uMsg
, cItems
, lpItems
, item
);
203 item
->apidl
= (SHChangeNotifyEntry
*)SHAlloc(sizeof(SHChangeNotifyEntry
) * cItems
);
204 for(i
=0;i
<cItems
;i
++)
206 item
->apidl
[i
].pidl
= ILClone(lpItems
[i
].pidl
);
207 item
->apidl
[i
].fRecursive
= lpItems
[i
].fRecursive
;
211 item
->wEventMask
= wEventMask
;
212 item
->wSignalledEvent
= 0;
213 item
->dwFlags
= fSources
;
215 TRACE("new node: %s\n", NodeName( item
));
217 EnterCriticalSection(&SHELL32_ChangenotifyCS
);
221 LeaveCriticalSection(&SHELL32_ChangenotifyCS
);
226 /*************************************************************************
227 * SHChangeNotifyDeregister [SHELL32.4]
229 BOOL WINAPI
SHChangeNotifyDeregister(ULONG hNotify
)
231 LPNOTIFICATIONLIST node
;
233 TRACE("(0x%08x)\n", hNotify
);
235 EnterCriticalSection(&SHELL32_ChangenotifyCS
);
237 node
= FindNode((HANDLE
)hNotify
);
241 LeaveCriticalSection(&SHELL32_ChangenotifyCS
);
243 return node
?TRUE
:FALSE
;
246 /*************************************************************************
247 * SHChangeNotifyUpdateEntryList [SHELL32.5]
249 EXTERN_C BOOL WINAPI
SHChangeNotifyUpdateEntryList(DWORD unknown1
, DWORD unknown2
,
250 DWORD unknown3
, DWORD unknown4
)
252 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
253 unknown1
, unknown2
, unknown3
, unknown4
);
258 static BOOL
should_notify( LPCITEMIDLIST changed
, LPCITEMIDLIST watched
, BOOL sub
)
260 TRACE("%p %p %d\n", changed
, watched
, sub
);
263 if (ILIsEqual( watched
, changed
) )
265 if( sub
&& ILIsParent( watched
, changed
, TRUE
) )
267 if (sub
&& _ILIsDesktop(watched
)) {
268 WCHAR wszPath
[MAX_PATH
];
269 SHGetSpecialFolderPathW(0, wszPath
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
270 LPITEMIDLIST deskpidl
= SHSimpleIDListFromPathW(wszPath
);
271 if (ILIsParent(deskpidl
, changed
, TRUE
))
277 SHGetSpecialFolderPathW(0, wszPath
, CSIDL_COMMON_DESKTOPDIRECTORY
, FALSE
);
278 deskpidl
= SHSimpleIDListFromPathW(wszPath
);
279 if (ILIsParent(deskpidl
, changed
, TRUE
))
289 /*************************************************************************
290 * SHChangeNotify [SHELL32.@]
292 void WINAPI
SHChangeNotify(LONG wEventId
, UINT uFlags
, LPCVOID dwItem1
, LPCVOID dwItem2
)
294 LPITEMIDLIST Pidls
[2];
295 LPNOTIFICATIONLIST ptr
;
296 UINT typeFlag
= uFlags
& SHCNF_TYPE
;
301 TRACE("(0x%08x,0x%08x,%p,%p):stub.\n", wEventId
, uFlags
, dwItem1
, dwItem2
);
303 if( ( wEventId
& SHCNE_NOITEMEVENTS
) && ( dwItem1
|| dwItem2
) )
305 TRACE("dwItem1 and dwItem2 are not zero, but should be\n");
310 else if( ( wEventId
& SHCNE_ONEITEMEVENTS
) && dwItem2
)
312 TRACE("dwItem2 is not zero, but should be\n");
317 if( ( ( wEventId
& SHCNE_NOITEMEVENTS
) &&
318 ( wEventId
& ~SHCNE_NOITEMEVENTS
) ) ||
319 ( ( wEventId
& SHCNE_ONEITEMEVENTS
) &&
320 ( wEventId
& ~SHCNE_ONEITEMEVENTS
) ) ||
321 ( ( wEventId
& SHCNE_TWOITEMEVENTS
) &&
322 ( wEventId
& ~SHCNE_TWOITEMEVENTS
) ) )
324 WARN("mutually incompatible events listed\n");
328 /* convert paths in IDLists*/
332 if (dwItem1
) Pidls
[0] = SHSimpleIDListFromPathA((LPCSTR
)dwItem1
); //FIXME
333 if (dwItem2
) Pidls
[1] = SHSimpleIDListFromPathA((LPCSTR
)dwItem2
); //FIXME
336 if (dwItem1
) Pidls
[0] = SHSimpleIDListFromPathW((LPCWSTR
)dwItem1
);
337 if (dwItem2
) Pidls
[1] = SHSimpleIDListFromPathW((LPCWSTR
)dwItem2
);
338 if (wEventId
& (SHCNE_MKDIR
| SHCNE_RMDIR
| SHCNE_UPDATEDIR
| SHCNE_RENAMEFOLDER
))
341 * The last items in the ID are currently files. So we chop off the last
342 * entry, and create a new one using a find data struct.
344 if (dwItem1
&& Pidls
[0]){
345 ILRemoveLastID(Pidls
[0]);
346 WIN32_FIND_DATAW wfd
;
347 LPWSTR p
= PathFindFileNameW((LPCWSTR
)dwItem1
);
348 lstrcpynW(&wfd
.cFileName
[0], p
, MAX_PATH
);
349 wfd
.dwFileAttributes
= FILE_ATTRIBUTE_DIRECTORY
;
350 LPITEMIDLIST newpidl
= _ILCreateFromFindDataW(&wfd
);
351 LPITEMIDLIST oldpidl
= ILClone(Pidls
[0]);
353 Pidls
[0] = ILCombine(oldpidl
, newpidl
);
357 if (dwItem2
&& Pidls
[1]){
358 ILRemoveLastID(Pidls
[1]);
359 WIN32_FIND_DATAW wfd
;
360 LPWSTR p
= PathFindFileNameW((LPCWSTR
)dwItem2
);
361 lstrcpynW(&wfd
.cFileName
[0], p
, MAX_PATH
);
362 wfd
.dwFileAttributes
= FILE_ATTRIBUTE_DIRECTORY
;
363 LPITEMIDLIST newpidl
= _ILCreateFromFindDataW(&wfd
);
364 LPITEMIDLIST oldpidl
= ILClone(Pidls
[0]);
366 Pidls
[1] = ILCombine(oldpidl
, newpidl
);
373 Pidls
[0] = (LPITEMIDLIST
)dwItem1
;
374 Pidls
[1] = (LPITEMIDLIST
)dwItem2
;
378 FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n");
382 FIXME("unknown type %08x\n",typeFlag
);
387 WCHAR path
[MAX_PATH
];
389 if( Pidls
[0] && SHGetPathFromIDListW(Pidls
[0], path
))
390 TRACE("notify %08x on item1 = %s\n", wEventId
, debugstr_w(path
));
392 if( Pidls
[1] && SHGetPathFromIDListW(Pidls
[1], path
))
393 TRACE("notify %08x on item2 = %s\n", wEventId
, debugstr_w(path
));
396 EnterCriticalSection(&SHELL32_ChangenotifyCS
);
398 /* loop through the list */
399 for( ptr
= head
; ptr
; ptr
= ptr
->next
)
406 TRACE("trying %p\n", ptr
);
408 for( i
=0; (i
<ptr
->cidl
) && !notify
; i
++ )
410 LPCITEMIDLIST pidl
= ptr
->apidl
[i
].pidl
;
411 BOOL subtree
= ptr
->apidl
[i
].fRecursive
;
413 if (wEventId
& ptr
->wEventMask
)
415 if( !pidl
) /* all ? */
417 else if( wEventId
& SHCNE_NOITEMEVENTS
)
419 else if( wEventId
& ( SHCNE_ONEITEMEVENTS
| SHCNE_TWOITEMEVENTS
) )
420 notify
= should_notify( Pidls
[0], pidl
, subtree
);
421 else if( wEventId
& SHCNE_TWOITEMEVENTS
)
422 notify
= should_notify( Pidls
[1], pidl
, subtree
);
429 ptr
->pidlSignaled
= ILClone(Pidls
[0]);
431 TRACE("notifying %s, event %s(%x) before\n", NodeName( ptr
), DumpEvent(
432 wEventId
),wEventId
);
434 ptr
->wSignalledEvent
|= wEventId
;
436 if (ptr
->dwFlags
& SHCNRF_NewDelivery
)
437 SendMessageW(ptr
->hwnd
, ptr
->uMsg
, (WPARAM
) ptr
, (LPARAM
) GetCurrentProcessId());
439 SendMessageW(ptr
->hwnd
, ptr
->uMsg
, (WPARAM
)Pidls
, wEventId
);
441 TRACE("notifying %s, event %s(%x) after\n", NodeName( ptr
), DumpEvent(
442 wEventId
),wEventId
);
445 TRACE("notify Done\n");
446 LeaveCriticalSection(&SHELL32_ChangenotifyCS
);
448 /* if we allocated it, free it. The ANSI flag is also set in its Unicode sibling. */
449 if ((typeFlag
& SHCNF_PATHA
) || (typeFlag
& SHCNF_PRINTERA
))
451 SHFree((LPITEMIDLIST
)Pidls
[0]);
452 SHFree((LPITEMIDLIST
)Pidls
[1]);
456 /*************************************************************************
457 * NTSHChangeNotifyRegister [SHELL32.640]
459 * Idlist is an array of structures and Count specifies how many items in the array.
460 * count should always be one when calling SHChangeNotifyRegister, or
461 * SHChangeNotifyDeregister will not work properly.
463 EXTERN_C ULONG WINAPI
NTSHChangeNotifyRegister(
469 SHChangeNotifyEntry
*idlist
)
471 return SHChangeNotifyRegister(hwnd
, fSources
| SHCNRF_NewDelivery
,
472 fEvents
, msg
, count
, idlist
);
475 /*************************************************************************
476 * SHChangeNotification_Lock [SHELL32.644]
478 HANDLE WINAPI
SHChangeNotification_Lock(
481 LPITEMIDLIST
**lppidls
,
485 LPNOTIFICATIONLIST node
;
486 LPCITEMIDLIST
*idlist
;
488 TRACE("%p %08x %p %p\n", hChange
, dwProcessId
, lppidls
, lpwEventId
);
490 /* EnterCriticalSection(&SHELL32_ChangenotifyCS); */
492 node
= FindNode( hChange
);
495 idlist
= (LPCITEMIDLIST
*)SHAlloc( sizeof(LPCITEMIDLIST
*) * node
->cidl
);
496 for(i
=0; i
<node
->cidl
; i
++)
497 idlist
[i
] = (LPCITEMIDLIST
)node
->pidlSignaled
;
498 *lpwEventId
= node
->wSignalledEvent
;
499 *lppidls
= (LPITEMIDLIST
*)idlist
;
500 node
->wSignalledEvent
= 0;
503 ERR("Couldn't find %p\n", hChange
);
505 /* LeaveCriticalSection(&SHELL32_ChangenotifyCS); */
507 return (HANDLE
) node
;
510 /*************************************************************************
511 * SHChangeNotification_Unlock [SHELL32.645]
513 BOOL WINAPI
SHChangeNotification_Unlock ( HANDLE hLock
)
519 /*************************************************************************
520 * NTSHChangeNotifyDeregister [SHELL32.641]
522 EXTERN_C DWORD WINAPI
NTSHChangeNotifyDeregister(ULONG x1
)
524 FIXME("(0x%08x):semi stub.\n",x1
);
526 return SHChangeNotifyDeregister( x1
);