d3c75eeb3810786a3656cbcd12d7a9461d4db48a
[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 COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26 #define BUFFER_SIZE 1024
27
28 #include <windef.h>
29 #include <winbase.h>
30 #include <shlobj.h>
31 #include <strsafe.h>
32 #include <undocshell.h>
33 #include <shlwapi.h>
34 #include <wine/debug.h>
35 #include <wine/list.h>
36 #include <process.h>
37 #include <shellutils.h>
38
39 #include "pidl.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(shell);
42
43 static CRITICAL_SECTION SHELL32_ChangenotifyCS;
44 static CRITICAL_SECTION_DEBUG critsect_debug =
45 {
46 0, 0, &SHELL32_ChangenotifyCS,
47 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
48 0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_ChangenotifyCS") }
49 };
50 static CRITICAL_SECTION SHELL32_ChangenotifyCS = { &critsect_debug, -1, 0, 0, 0, 0 };
51
52 #ifdef __REACTOS__
53 typedef struct {
54 PCIDLIST_ABSOLUTE pidl;
55 BOOL fRecursive;
56 /* File system notification items */
57 HANDLE hDirectory; /* Directory handle */
58 WCHAR wstrDirectory[MAX_PATH]; /* Directory name */
59 OVERLAPPED overlapped; /* Overlapped structure */
60 BYTE *buffer; /* Async buffer to fill */
61 BYTE *backBuffer; /* Back buffer to swap buffer into */
62 struct _NOTIFICATIONLIST * pParent;
63 } SHChangeNotifyEntryInternal, *LPNOTIFYREGISTER;
64 #else
65 typedef SHChangeNotifyEntry *LPNOTIFYREGISTER;
66 #endif
67
68 /* internal list of notification clients (internal) */
69 typedef struct _NOTIFICATIONLIST
70 {
71 struct list entry;
72 HWND hwnd; /* window to notify */
73 DWORD uMsg; /* message to send */
74 LPNOTIFYREGISTER apidl; /* array of entries to watch*/
75 UINT cidl; /* number of pidls in array */
76 LONG wEventMask; /* subscribed events */
77 DWORD dwFlags; /* client flags */
78 ULONG id;
79 #ifdef __REACTOS__
80 volatile LONG wQueuedCount;
81 #endif
82 } NOTIFICATIONLIST, *LPNOTIFICATIONLIST;
83
84 #ifdef __REACTOS__
85 VOID _ProcessNotification(LPNOTIFYREGISTER item);
86 BOOL _OpenDirectory(LPNOTIFYREGISTER item);
87 static void CALLBACK _RequestTermination(ULONG_PTR arg);
88 static void CALLBACK _RequestAllTermination(ULONG_PTR arg);
89 static void CALLBACK _AddDirectoryProc(ULONG_PTR arg);
90 static VOID _BeginRead(LPNOTIFYREGISTER item);
91 static unsigned int WINAPI _RunAsyncThreadProc(LPVOID arg);
92 #endif
93
94 static struct list notifications = LIST_INIT( notifications );
95 static LONG next_id;
96
97 #ifdef __REACTOS__
98 HANDLE m_hThread;
99 UINT m_dwThreadId;
100 BOOL m_bTerminate;
101 #endif
102
103 #define SHCNE_NOITEMEVENTS ( \
104 SHCNE_ASSOCCHANGED )
105
106 #define SHCNE_ONEITEMEVENTS ( \
107 SHCNE_ATTRIBUTES | SHCNE_CREATE | SHCNE_DELETE | SHCNE_DRIVEADD | \
108 SHCNE_DRIVEADDGUI | SHCNE_DRIVEREMOVED | SHCNE_FREESPACE | \
109 SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | SHCNE_MKDIR | \
110 SHCNE_NETSHARE | SHCNE_NETUNSHARE | SHCNE_RMDIR | \
111 SHCNE_SERVERDISCONNECT | SHCNE_UPDATEDIR | SHCNE_UPDATEIMAGE )
112
113 #define SHCNE_TWOITEMEVENTS ( \
114 SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEITEM )
115
116 /* for dumping events */
117 static const char * DumpEvent( LONG event )
118 {
119 if( event == SHCNE_ALLEVENTS )
120 return "SHCNE_ALLEVENTS";
121 #define DUMPEV(x) ,( event & SHCNE_##x )? #x " " : ""
122 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"
123 DUMPEV(RENAMEITEM)
124 DUMPEV(CREATE)
125 DUMPEV(DELETE)
126 DUMPEV(MKDIR)
127 DUMPEV(RMDIR)
128 DUMPEV(MEDIAINSERTED)
129 DUMPEV(MEDIAREMOVED)
130 DUMPEV(DRIVEREMOVED)
131 DUMPEV(DRIVEADD)
132 DUMPEV(NETSHARE)
133 DUMPEV(NETUNSHARE)
134 DUMPEV(ATTRIBUTES)
135 DUMPEV(UPDATEDIR)
136 DUMPEV(UPDATEITEM)
137 DUMPEV(SERVERDISCONNECT)
138 DUMPEV(UPDATEIMAGE)
139 DUMPEV(DRIVEADDGUI)
140 DUMPEV(RENAMEFOLDER)
141 DUMPEV(FREESPACE)
142 DUMPEV(EXTENDED_EVENT)
143 DUMPEV(ASSOCCHANGED)
144 DUMPEV(INTERRUPT)
145 );
146 #undef DUMPEV
147 }
148
149 static const char * NodeName(const NOTIFICATIONLIST *item)
150 {
151 const char *str;
152 WCHAR path[MAX_PATH];
153
154 if(SHGetPathFromIDListW(item->apidl[0].pidl, path ))
155 str = wine_dbg_sprintf("%s", debugstr_w(path));
156 else
157 str = wine_dbg_sprintf("<not a disk file>" );
158 return str;
159 }
160
161 static void DeleteNode(LPNOTIFICATIONLIST item)
162 {
163 UINT i;
164 #ifdef __REACTOS__
165 LONG queued;
166 #endif
167
168 TRACE("item=%p\n", item);
169
170 #ifdef __REACTOS__
171 queued = InterlockedCompareExchange(&item->wQueuedCount, 0, 0);
172 if (queued != 0)
173 {
174 ERR("Not freeing, still %d queued events\n", queued);
175 return;
176 }
177 TRACE("Freeing for real! %p (%d) \n", item, item->cidl);
178 #endif
179
180 /* remove item from list */
181 list_remove( &item->entry );
182
183 /* free the item */
184 for (i=0; i<item->cidl; i++)
185 #ifdef __REACTOS__
186 {
187 QueueUserAPC(_RequestTermination, m_hThread, (ULONG_PTR) &item->apidl[i] );
188 WaitForSingleObjectEx(m_hThread, 100, FALSE);
189 #endif
190 SHFree((LPITEMIDLIST)item->apidl[i].pidl);
191 #ifdef __REACTOS__
192 SHFree(item->apidl[i].buffer);
193 SHFree(item->apidl[i].backBuffer);
194 }
195 #endif
196 SHFree(item->apidl);
197 SHFree(item);
198 }
199
200 void InitChangeNotifications(void)
201 {
202 #ifdef __REACTOS__
203 m_hThread = NULL;
204 #endif
205 }
206
207 void FreeChangeNotifications(void)
208 {
209 LPNOTIFICATIONLIST ptr, next;
210
211 TRACE("\n");
212
213 EnterCriticalSection(&SHELL32_ChangenotifyCS);
214
215 LIST_FOR_EACH_ENTRY_SAFE( ptr, next, &notifications, NOTIFICATIONLIST, entry )
216 DeleteNode( ptr );
217
218 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
219
220 #ifdef __REACTOS__
221 QueueUserAPC(_RequestAllTermination, m_hThread, (ULONG_PTR) NULL );
222 #endif
223
224 DeleteCriticalSection(&SHELL32_ChangenotifyCS);
225 }
226
227 /*************************************************************************
228 * SHChangeNotifyRegister [SHELL32.2]
229 *
230 */
231 ULONG WINAPI
232 SHChangeNotifyRegister(
233 HWND hwnd,
234 int fSources,
235 LONG wEventMask,
236 UINT uMsg,
237 int cItems,
238 SHChangeNotifyEntry *lpItems)
239 {
240 LPNOTIFICATIONLIST item;
241 int i;
242
243 item = SHAlloc(sizeof(NOTIFICATIONLIST));
244
245 TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p) item=%p\n",
246 hwnd, fSources, wEventMask, uMsg, cItems, lpItems, item);
247
248 #ifdef __REACTOS__
249 if (!m_hThread)
250 m_hThread = (HANDLE) _beginthreadex(NULL, 0, _RunAsyncThreadProc, NULL, 0, &m_dwThreadId);
251 #endif
252
253 item->cidl = cItems;
254 #ifdef __REACTOS__
255 item->apidl = SHAlloc(sizeof(SHChangeNotifyEntryInternal) * cItems);
256 item->wQueuedCount = 0;
257 #else
258 item->apidl = SHAlloc(sizeof(SHChangeNotifyEntry) * cItems);
259 #endif
260 for(i=0;i<cItems;i++)
261 {
262 #ifdef __REACTOS__
263 ZeroMemory(&item->apidl[i], sizeof(SHChangeNotifyEntryInternal));
264 #endif
265 item->apidl[i].pidl = ILClone(lpItems[i].pidl);
266 item->apidl[i].fRecursive = lpItems[i].fRecursive;
267 #ifdef __REACTOS__
268 item->apidl[i].buffer = SHAlloc(BUFFER_SIZE);
269 item->apidl[i].backBuffer = SHAlloc(BUFFER_SIZE);
270 item->apidl[i].overlapped.hEvent = &item->apidl[i];
271 item->apidl[i].pParent = item;
272
273 if (fSources & SHCNRF_InterruptLevel)
274 {
275 if (_OpenDirectory( &item->apidl[i] ))
276 {
277 InterlockedIncrement(&item->wQueuedCount);
278 QueueUserAPC( _AddDirectoryProc, m_hThread, (ULONG_PTR) &item->apidl[i] );
279 }
280 }
281 #endif
282 }
283 item->hwnd = hwnd;
284 item->uMsg = uMsg;
285 item->wEventMask = wEventMask;
286 item->dwFlags = fSources;
287 item->id = InterlockedIncrement( &next_id );
288
289 TRACE("new node: %s\n", NodeName( item ));
290
291 EnterCriticalSection(&SHELL32_ChangenotifyCS);
292
293 list_add_tail( &notifications, &item->entry );
294
295 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
296
297 return item->id;
298 }
299
300 /*************************************************************************
301 * SHChangeNotifyDeregister [SHELL32.4]
302 */
303 BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify)
304 {
305 LPNOTIFICATIONLIST node;
306
307 TRACE("(0x%08x)\n", hNotify);
308
309 EnterCriticalSection(&SHELL32_ChangenotifyCS);
310
311 LIST_FOR_EACH_ENTRY( node, &notifications, NOTIFICATIONLIST, entry )
312 {
313 if (node->id == hNotify)
314 {
315 DeleteNode( node );
316 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
317 return TRUE;
318 }
319 }
320 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
321 return FALSE;
322 }
323
324 /*************************************************************************
325 * SHChangeNotifyUpdateEntryList [SHELL32.5]
326 */
327 BOOL WINAPI SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2,
328 DWORD unknown3, DWORD unknown4)
329 {
330 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
331 unknown1, unknown2, unknown3, unknown4);
332
333 return TRUE;
334 }
335
336 struct new_delivery_notification
337 {
338 LONG event;
339 DWORD pidl1_size;
340 DWORD pidl2_size;
341 LPITEMIDLIST pidls[2];
342 BYTE data[1];
343 };
344
345 static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL sub )
346 {
347 TRACE("%p %p %d\n", changed, watched, sub );
348 if ( !watched )
349 return FALSE;
350 if (ILIsEqual( watched, changed ) )
351 return TRUE;
352 if( sub && ILIsParent( watched, changed, FALSE ) )
353 return TRUE;
354 return FALSE;
355 }
356
357 /*************************************************************************
358 * SHChangeNotify [SHELL32.@]
359 */
360 void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
361 {
362 struct notification_recipients {
363 struct list entry;
364 HWND hwnd;
365 DWORD msg;
366 DWORD flags;
367 } *cur, *next;
368
369 HANDLE shared_data = NULL;
370 LPITEMIDLIST Pidls[2];
371 LPNOTIFICATIONLIST ptr;
372 struct list recipients;
373
374 Pidls[0] = NULL;
375 Pidls[1] = NULL;
376
377 TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2);
378
379 if(uFlags & ~(SHCNF_TYPE|SHCNF_FLUSH))
380 FIXME("ignoring unsupported flags: %x\n", uFlags);
381
382 if( ( wEventId & SHCNE_NOITEMEVENTS ) && ( dwItem1 || dwItem2 ) )
383 {
384 TRACE("dwItem1 and dwItem2 are not zero, but should be\n");
385 dwItem1 = 0;
386 dwItem2 = 0;
387 return;
388 }
389 else if( ( wEventId & SHCNE_ONEITEMEVENTS ) && dwItem2 )
390 {
391 TRACE("dwItem2 is not zero, but should be\n");
392 dwItem2 = 0;
393 return;
394 }
395
396 if( ( ( wEventId & SHCNE_NOITEMEVENTS ) &&
397 ( wEventId & ~(SHCNE_NOITEMEVENTS | SHCNE_INTERRUPT) ) ) ||
398 ( ( wEventId & SHCNE_ONEITEMEVENTS ) &&
399 ( wEventId & ~(SHCNE_ONEITEMEVENTS | SHCNE_INTERRUPT) ) ) ||
400 ( ( wEventId & SHCNE_TWOITEMEVENTS ) &&
401 ( wEventId & ~(SHCNE_TWOITEMEVENTS | SHCNE_INTERRUPT) ) ) )
402 {
403 WARN("mutually incompatible events listed\n");
404 return;
405 }
406
407 /* convert paths in IDLists*/
408 switch (uFlags & SHCNF_TYPE)
409 {
410 case SHCNF_PATHA:
411 if (dwItem1) Pidls[0] = SHSimpleIDListFromPathA(dwItem1); //FIXME
412 if (dwItem2) Pidls[1] = SHSimpleIDListFromPathA(dwItem2); //FIXME
413 break;
414 case SHCNF_PATHW:
415 if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW(dwItem1);
416 if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW(dwItem2);
417 #ifdef __REACTOS__
418 if (wEventId & (SHCNE_MKDIR | SHCNE_RMDIR | SHCNE_UPDATEDIR | SHCNE_RENAMEFOLDER))
419 {
420 /*
421 * The last items in the ID are currently files. So we chop off the last
422 * entry, and create a new one using a find data struct.
423 */
424 if (dwItem1 && Pidls[0]){
425 WIN32_FIND_DATAW wfd;
426 LPITEMIDLIST oldpidl, newpidl;
427 LPWSTR p = PathFindFileNameW((LPCWSTR)dwItem1);
428 ILRemoveLastID(Pidls[0]);
429 lstrcpynW(&wfd.cFileName[0], p, MAX_PATH);
430 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
431 newpidl = _ILCreateFromFindDataW(&wfd);
432 oldpidl = ILClone(Pidls[0]);
433 ILFree(Pidls[0]);
434 Pidls[0] = ILCombine(oldpidl, newpidl);
435 ILFree(newpidl);
436 ILFree(oldpidl);
437 }
438 if (dwItem2 && Pidls[1]){
439 WIN32_FIND_DATAW wfd;
440 LPITEMIDLIST oldpidl, newpidl;
441 LPWSTR p = PathFindFileNameW((LPCWSTR)dwItem2);
442 ILRemoveLastID(Pidls[1]);
443 lstrcpynW(&wfd.cFileName[0], p, MAX_PATH);
444 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
445 newpidl = _ILCreateFromFindDataW(&wfd);
446 oldpidl = ILClone(Pidls[0]);
447 ILFree(Pidls[1]);
448 Pidls[1] = ILCombine(oldpidl, newpidl);
449 ILFree(newpidl);
450 ILFree(oldpidl);
451 }
452 }
453 #endif
454 break;
455 case SHCNF_IDLIST:
456 Pidls[0] = ILClone(dwItem1);
457 Pidls[1] = ILClone(dwItem2);
458 break;
459 case SHCNF_PRINTERA:
460 case SHCNF_PRINTERW:
461 FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n");
462 return;
463 case SHCNF_DWORD:
464 default:
465 FIXME("unknown type %08x\n", uFlags & SHCNF_TYPE);
466 return;
467 }
468
469 list_init(&recipients);
470 EnterCriticalSection(&SHELL32_ChangenotifyCS);
471 LIST_FOR_EACH_ENTRY( ptr, &notifications, NOTIFICATIONLIST, entry )
472 {
473 struct notification_recipients *item;
474 BOOL notify = FALSE;
475 DWORD i;
476
477 for( i=0; (i<ptr->cidl) && !notify ; i++ )
478 {
479 LPCITEMIDLIST pidl = ptr->apidl[i].pidl;
480 BOOL subtree = ptr->apidl[i].fRecursive;
481
482 if (wEventId & ptr->wEventMask)
483 {
484 if( !pidl ) /* all ? */
485 notify = TRUE;
486 else if( wEventId & SHCNE_NOITEMEVENTS )
487 notify = TRUE;
488 else if( wEventId & ( SHCNE_ONEITEMEVENTS | SHCNE_TWOITEMEVENTS ) )
489 notify = should_notify( Pidls[0], pidl, subtree );
490 else if( wEventId & SHCNE_TWOITEMEVENTS )
491 notify = should_notify( Pidls[1], pidl, subtree );
492 }
493 }
494
495 if( !notify )
496 continue;
497
498 item = SHAlloc(sizeof(struct notification_recipients));
499 if(!item) {
500 ERR("out of memory\n");
501 continue;
502 }
503
504 item->hwnd = ptr->hwnd;
505 item->msg = ptr->uMsg;
506 item->flags = ptr->dwFlags;
507 list_add_tail(&recipients, &item->entry);
508 }
509 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
510
511 LIST_FOR_EACH_ENTRY_SAFE(cur, next, &recipients, struct notification_recipients, entry)
512 {
513 TRACE("notifying %p, event %s(%x)\n", cur->hwnd, DumpEvent(wEventId), wEventId);
514
515 if (cur->flags & SHCNRF_NewDelivery) {
516 if(!shared_data) {
517 struct new_delivery_notification *notification;
518 UINT size1 = ILGetSize(Pidls[0]), size2 = ILGetSize(Pidls[1]);
519 UINT offset = (size1+sizeof(int)-1)/sizeof(int)*sizeof(int);
520
521 notification = SHAlloc(sizeof(struct new_delivery_notification)+offset+size2);
522 if(!notification) {
523 ERR("out of memory\n");
524 } else {
525 notification->event = wEventId;
526 notification->pidl1_size = size1;
527 notification->pidl2_size = size2;
528 if(size1)
529 memcpy(notification->data, Pidls[0], size1);
530 if(size2)
531 memcpy(notification->data+offset, Pidls[1], size2);
532
533 shared_data = SHAllocShared(notification,
534 sizeof(struct new_delivery_notification)+size1+size2,
535 GetCurrentProcessId());
536 SHFree(notification);
537 }
538 }
539
540 if(shared_data)
541 SendMessageA(cur->hwnd, cur->msg, (WPARAM)shared_data, GetCurrentProcessId());
542 else
543 ERR("out of memory\n");
544 } else {
545 SendMessageA(cur->hwnd, cur->msg, (WPARAM)Pidls, wEventId);
546 }
547
548 list_remove(&cur->entry);
549 SHFree(cur);
550 }
551 SHFreeShared(shared_data, GetCurrentProcessId());
552 SHFree(Pidls[0]);
553 SHFree(Pidls[1]);
554
555 #ifndef __REACTOS__
556 if (wEventId & SHCNE_ASSOCCHANGED)
557 {
558 static const WCHAR args[] = {' ','-','a',0 };
559 TRACE("refreshing file type associations\n");
560 run_winemenubuilder( args );
561 }
562 #endif
563 }
564
565 /*************************************************************************
566 * NTSHChangeNotifyRegister [SHELL32.640]
567 * NOTES
568 * Idlist is an array of structures and Count specifies how many items in the array.
569 * count should always be one when calling SHChangeNotifyRegister, or
570 * SHChangeNotifyDeregister will not work properly.
571 */
572 EXTERN_C ULONG WINAPI NTSHChangeNotifyRegister(
573 HWND hwnd,
574 int fSources,
575 LONG fEvents,
576 UINT msg,
577 int count,
578 SHChangeNotifyEntry *idlist)
579 {
580 return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery,
581 fEvents, msg, count, idlist);
582 }
583
584 /*************************************************************************
585 * SHChangeNotification_Lock [SHELL32.644]
586 */
587 HANDLE WINAPI SHChangeNotification_Lock(
588 HANDLE hChange,
589 DWORD dwProcessId,
590 LPITEMIDLIST **lppidls,
591 LPLONG lpwEventId)
592 {
593 struct new_delivery_notification *ndn;
594 UINT offset;
595
596 TRACE("%p %08x %p %p\n", hChange, dwProcessId, lppidls, lpwEventId);
597
598 ndn = SHLockShared(hChange, dwProcessId);
599 if(!ndn) {
600 WARN("SHLockShared failed\n");
601 return NULL;
602 }
603
604 if(lppidls) {
605 offset = (ndn->pidl1_size+sizeof(int)-1)/sizeof(int)*sizeof(int);
606 ndn->pidls[0] = ndn->pidl1_size ? (LPITEMIDLIST)ndn->data : NULL;
607 ndn->pidls[1] = ndn->pidl2_size ? (LPITEMIDLIST)(ndn->data+offset) : NULL;
608 *lppidls = ndn->pidls;
609 }
610
611 if(lpwEventId)
612 *lpwEventId = ndn->event;
613
614 return ndn;
615 }
616
617 /*************************************************************************
618 * SHChangeNotification_Unlock [SHELL32.645]
619 */
620 BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock)
621 {
622 TRACE("%p\n", hLock);
623 return SHUnlockShared(hLock);
624 }
625
626 /*************************************************************************
627 * NTSHChangeNotifyDeregister [SHELL32.641]
628 */
629 DWORD WINAPI NTSHChangeNotifyDeregister(ULONG x1)
630 {
631 FIXME("(0x%08x):semi stub.\n",x1);
632
633 return SHChangeNotifyDeregister( x1 );
634 }
635
636 #ifdef __REACTOS__
637
638 static
639 void
640 CALLBACK
641 _AddDirectoryProc(ULONG_PTR arg)
642 {
643 LPNOTIFYREGISTER item = (LPNOTIFYREGISTER)arg;
644 _BeginRead(item);
645 }
646
647 BOOL _OpenDirectory(LPNOTIFYREGISTER item)
648 {
649 STRRET strFile;
650 IShellFolder *psfDesktop;
651 HRESULT hr;
652
653 // Makes function idempotent
654 if (item->hDirectory && !(item->hDirectory == INVALID_HANDLE_VALUE))
655 return TRUE;
656
657 hr = SHGetDesktopFolder(&psfDesktop);
658 if (FAILED_UNEXPECTEDLY(hr))
659 return FALSE;
660
661 hr = IShellFolder_GetDisplayNameOf(psfDesktop, item->pidl, SHGDN_FORPARSING, &strFile);
662 IShellFolder_Release(psfDesktop);
663 if (FAILED_UNEXPECTEDLY(hr))
664 return FALSE;
665
666 hr = StrRetToBufW(&strFile, NULL, item->wstrDirectory, _countof(item->wstrDirectory));
667 if (FAILED_UNEXPECTEDLY(hr))
668 return FALSE;
669
670 TRACE("_OpenDirectory %s\n", debugstr_w(item->wstrDirectory));
671
672 item->hDirectory = CreateFileW(item->wstrDirectory, // pointer to the file name
673 GENERIC_READ | FILE_LIST_DIRECTORY, // access (read/write) mode
674 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
675 NULL, // security descriptor
676 OPEN_EXISTING, // how to create
677 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // file attributes
678 NULL); // file with attributes to copy
679
680 if (item->hDirectory == INVALID_HANDLE_VALUE)
681 {
682 ERR("_OpenDirectory failed for %s\n", debugstr_w(item->wstrDirectory));
683 return FALSE;
684 }
685 return TRUE;
686 }
687
688 static void CALLBACK _RequestTermination(ULONG_PTR arg)
689 {
690 LPNOTIFYREGISTER item = (LPNOTIFYREGISTER) arg;
691 TRACE("_RequestTermination %p %p \n", item, item->hDirectory);
692 if (!item->hDirectory || item->hDirectory == INVALID_HANDLE_VALUE) return;
693
694 CancelIo(item->hDirectory);
695 CloseHandle(item->hDirectory);
696 item->hDirectory = NULL;
697 }
698
699 static
700 void
701 CALLBACK
702 _NotificationCompletion(DWORD dwErrorCode, // completion code
703 DWORD dwNumberOfBytesTransfered, // number of bytes transferred
704 LPOVERLAPPED lpOverlapped) // I/O information buffer
705 {
706 /* MSDN: The hEvent member of the OVERLAPPED structure is not used by the
707 system, so you can use it yourself. We do just this, storing a pointer
708 to the working struct in the overlapped structure. */
709 LPNOTIFYREGISTER item = (LPNOTIFYREGISTER) lpOverlapped->hEvent;
710 TRACE("_NotificationCompletion\n");
711
712 #if 0
713 if (dwErrorCode == ERROR_OPERATION_ABORTED)
714 {
715 /* Command was induced by CancelIo in the shutdown procedure. */
716 TRACE("_NotificationCompletion ended.\n");
717 return;
718 }
719 #endif
720
721 #ifdef __REACTOS__
722 /* If the FSD doesn't support directory change notifications, there's no
723 * no need to retry and requeue notification
724 */
725 if (dwErrorCode == ERROR_INVALID_FUNCTION)
726 {
727 WARN("Directory watching not supported\n");
728 goto quit;
729 }
730 #endif
731
732 /* This likely means overflow, so force whole directory refresh. */
733 if (!dwNumberOfBytesTransfered)
734 {
735 ERR("_NotificationCompletion overflow\n");
736
737 ZeroMemory(item->buffer, BUFFER_SIZE);
738 _BeginRead(item);
739
740 SHChangeNotify(SHCNE_UPDATEITEM | SHCNE_INTERRUPT,
741 SHCNF_IDLIST,
742 item->pidl,
743 NULL);
744
745 #ifdef __REACTOS__
746 goto quit;
747 #else
748 return;
749 #endif
750 }
751
752 /*
753 * Get the new read issued as fast as possible (before we do the
754 * processing and message posting). All of the file notification
755 * occur on one thread so the buffers should not collide with one another.
756 * The extra zero mems are because the FNI size isn't written correctly.
757 */
758
759 ZeroMemory(item->backBuffer, BUFFER_SIZE);
760 memcpy(item->backBuffer, item->buffer, dwNumberOfBytesTransfered);
761 ZeroMemory(item->buffer, BUFFER_SIZE);
762
763 _BeginRead(item);
764
765 _ProcessNotification(item);
766
767 #ifdef __REACTOS__
768 quit:
769 InterlockedDecrement(&item->pParent->wQueuedCount);
770 DeleteNode(item->pParent);
771 #endif
772 }
773
774 static VOID _BeginRead(LPNOTIFYREGISTER item )
775 {
776 TRACE("_BeginRead %p \n", item->hDirectory);
777
778 #ifdef __REACTOS__
779 InterlockedIncrement(&item->pParent->wQueuedCount);
780 #endif
781 /* This call needs to be reissued after every APC. */
782 if (!ReadDirectoryChangesW(item->hDirectory, // handle to directory
783 item->buffer, // read results buffer
784 BUFFER_SIZE, // length of buffer
785 FALSE, // monitoring option (recursion)
786 FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions
787 NULL, // bytes returned
788 &item->overlapped, // overlapped buffer
789 _NotificationCompletion)) // completion routine
790 #ifdef __REACTOS__
791 {
792 #endif
793 ERR("ReadDirectoryChangesW failed. (%p, %p, %p, %p, %p, %p) Code: %u \n",
794 item,
795 item->pParent,
796 item->hDirectory,
797 item->buffer,
798 &item->overlapped,
799 _NotificationCompletion,
800 GetLastError());
801 #ifdef __REACTOS__
802 InterlockedDecrement(&item->pParent->wQueuedCount);
803 DeleteNode(item->pParent);
804 }
805 #endif
806 }
807
808 DWORD _MapAction(DWORD dwAction, BOOL isDir)
809 {
810 switch (dwAction)
811 {
812 case FILE_ACTION_ADDED : return isDir ? SHCNE_MKDIR : SHCNE_CREATE;
813 case FILE_ACTION_REMOVED : return isDir ? SHCNE_RMDIR : SHCNE_DELETE;
814 case FILE_ACTION_MODIFIED : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
815 case FILE_ACTION_RENAMED_OLD_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
816 case FILE_ACTION_RENAMED_NEW_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
817 default: return SHCNE_UPDATEITEM;
818 }
819 }
820
821 VOID _ProcessNotification(LPNOTIFYREGISTER item)
822 {
823 BYTE* pBase = item->backBuffer;
824 TRACE("_ProcessNotification\n");
825
826 for (;;)
827 {
828 FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)pBase;
829 LPWSTR wszFilename;
830 INT len = 0;
831 WCHAR wstrFilename[MAX_PATH];
832 WCHAR tmp[MAX_PATH];
833 StringCchCopy(tmp, fni->FileNameLength, fni->FileName);
834
835 PathCombine(wstrFilename, item->wstrDirectory, tmp);
836
837 /* If it could be a short filename, expand it. */
838 wszFilename = PathFindFileNameW(wstrFilename);
839
840 len = lstrlenW(wszFilename);
841 /* The maximum length of an 8.3 filename is twelve, including the dot. */
842 if (len <= 12 && wcschr(wszFilename, L'~'))
843 {
844 /* Convert to the long filename form. Unfortunately, this
845 does not work for deletions, so it's an imperfect fix. */
846 wchar_t wbuf[MAX_PATH];
847 if (GetLongPathName(wstrFilename, wbuf, _countof (wbuf)) > 0)
848 StringCchCopyW(wstrFilename, MAX_PATH, wbuf);
849 }
850
851 /* On deletion of a folder PathIsDirectory will return false even if
852 it *was* a directory, so, again, imperfect. */
853 SHChangeNotify(_MapAction(fni->Action, PathIsDirectory(wstrFilename)) | SHCNE_INTERRUPT,
854 SHCNF_PATHW,
855 wstrFilename,
856 NULL);
857
858 if (!fni->NextEntryOffset)
859 break;
860 pBase += fni->NextEntryOffset;
861 }
862 }
863
864 static void CALLBACK _RequestAllTermination(ULONG_PTR arg)
865 {
866 m_bTerminate = TRUE;
867 }
868
869 static unsigned int WINAPI _RunAsyncThreadProc(LPVOID arg)
870 {
871 m_bTerminate = FALSE;
872 while (!m_bTerminate)
873 {
874 SleepEx(INFINITE, TRUE);
875 }
876 return 0;
877 }
878
879 #endif /* __REACTOS__ */