7846497184afb88d9504efcc37f54cadfa059521
[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 #endif
739
740 /* This likely means overflow, so force whole directory refresh. */
741 if (!dwNumberOfBytesTransfered)
742 {
743 ERR("_NotificationCompletion overflow\n");
744
745 ZeroMemory(item->buffer, BUFFER_SIZE);
746 _BeginRead(item);
747
748 SHChangeNotify(SHCNE_UPDATEITEM | SHCNE_INTERRUPT,
749 SHCNF_IDLIST,
750 item->pidl,
751 NULL);
752
753 #ifdef __REACTOS__
754 goto quit;
755 #else
756 return;
757 #endif
758 }
759
760 /*
761 * Get the new read issued as fast as possible (before we do the
762 * processing and message posting). All of the file notification
763 * occur on one thread so the buffers should not collide with one another.
764 * The extra zero mems are because the FNI size isn't written correctly.
765 */
766
767 ZeroMemory(item->backBuffer, BUFFER_SIZE);
768 memcpy(item->backBuffer, item->buffer, dwNumberOfBytesTransfered);
769 ZeroMemory(item->buffer, BUFFER_SIZE);
770
771 _BeginRead(item);
772
773 _ProcessNotification(item);
774
775 #ifdef __REACTOS__
776 quit:
777 InterlockedDecrement(&item->pParent->wQueuedCount);
778 DeleteNode(item->pParent);
779 #endif
780 }
781
782 static VOID _BeginRead(LPNOTIFYREGISTER item )
783 {
784 TRACE("_BeginRead %p \n", item->hDirectory);
785
786 #ifdef __REACTOS__
787 InterlockedIncrement(&item->pParent->wQueuedCount);
788 #endif
789 /* This call needs to be reissued after every APC. */
790 if (!ReadDirectoryChangesW(item->hDirectory, // handle to directory
791 item->buffer, // read results buffer
792 BUFFER_SIZE, // length of buffer
793 FALSE, // monitoring option (recursion)
794 FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions
795 NULL, // bytes returned
796 &item->overlapped, // overlapped buffer
797 _NotificationCompletion)) // completion routine
798 #ifdef __REACTOS__
799 {
800 #endif
801 ERR("ReadDirectoryChangesW failed. (%p, %p, %p, %p) Code: %u \n",
802 item->hDirectory,
803 item->buffer,
804 &item->overlapped,
805 _NotificationCompletion,
806 GetLastError());
807 #ifdef __REACTOS__
808 InterlockedDecrement(&item->pParent->wQueuedCount);
809 DeleteNode(item->pParent);
810 }
811 #endif
812 }
813
814 DWORD _MapAction(DWORD dwAction, BOOL isDir)
815 {
816 switch (dwAction)
817 {
818 case FILE_ACTION_ADDED : return isDir ? SHCNE_MKDIR : SHCNE_CREATE;
819 case FILE_ACTION_REMOVED : return isDir ? SHCNE_RMDIR : SHCNE_DELETE;
820 case FILE_ACTION_MODIFIED : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
821 case FILE_ACTION_RENAMED_OLD_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
822 case FILE_ACTION_RENAMED_NEW_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
823 default: return SHCNE_UPDATEITEM;
824 }
825 }
826
827 VOID _ProcessNotification(LPNOTIFYREGISTER item)
828 {
829 BYTE* pBase = item->backBuffer;
830 TRACE("_ProcessNotification\n");
831
832 for (;;)
833 {
834 FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)pBase;
835 LPWSTR wszFilename;
836 INT len = 0;
837 WCHAR wstrFilename[MAX_PATH];
838 WCHAR tmp[MAX_PATH];
839 StringCchCopy(tmp, fni->FileNameLength, fni->FileName);
840
841 PathCombine(wstrFilename, item->wstrDirectory, tmp);
842
843 /* If it could be a short filename, expand it. */
844 wszFilename = PathFindFileNameW(wstrFilename);
845
846 len = lstrlenW(wszFilename);
847 /* The maximum length of an 8.3 filename is twelve, including the dot. */
848 if (len <= 12 && wcschr(wszFilename, L'~'))
849 {
850 /* Convert to the long filename form. Unfortunately, this
851 does not work for deletions, so it's an imperfect fix. */
852 wchar_t wbuf[MAX_PATH];
853 if (GetLongPathName(wstrFilename, wbuf, _countof (wbuf)) > 0)
854 StringCchCopyW(wstrFilename, MAX_PATH, wbuf);
855 }
856
857 /* On deletion of a folder PathIsDirectory will return false even if
858 it *was* a directory, so, again, imperfect. */
859 SHChangeNotify(_MapAction(fni->Action, PathIsDirectory(wstrFilename)) | SHCNE_INTERRUPT,
860 SHCNF_PATHW,
861 wstrFilename,
862 NULL);
863
864 if (!fni->NextEntryOffset)
865 break;
866 pBase += fni->NextEntryOffset;
867 }
868 }
869
870 static void CALLBACK _RequestAllTermination(ULONG_PTR arg)
871 {
872 m_bTerminate = TRUE;
873 }
874
875 static unsigned int WINAPI _RunAsyncThreadProc(LPVOID arg)
876 {
877 m_bTerminate = FALSE;
878 while (!m_bTerminate)
879 {
880 SleepEx(INFINITE, TRUE);
881 }
882 return 0;
883 }
884
885 #endif /* __REACTOS__ */