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