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