[SHELL32] Report file system changes in SHChangeNotifyRegister. Brought to you by...
[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 && _OpenDirectory( &item->apidl[i] ))
254 QueueUserAPC(_AddDirectoryProc, m_hThread, (ULONG_PTR) &item->apidl[i] );
255 else ERR("_OpenDirectory Failed\n");
256 #endif
257 }
258 item->hwnd = hwnd;
259 item->uMsg = uMsg;
260 item->wEventMask = wEventMask;
261 item->dwFlags = fSources;
262 item->id = InterlockedIncrement( &next_id );
263
264 TRACE("new node: %s\n", NodeName( item ));
265
266 EnterCriticalSection(&SHELL32_ChangenotifyCS);
267
268 list_add_tail( &notifications, &item->entry );
269
270 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
271
272 return item->id;
273 }
274
275 /*************************************************************************
276 * SHChangeNotifyDeregister [SHELL32.4]
277 */
278 BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify)
279 {
280 LPNOTIFICATIONLIST node;
281
282 TRACE("(0x%08x)\n", hNotify);
283
284 EnterCriticalSection(&SHELL32_ChangenotifyCS);
285
286 LIST_FOR_EACH_ENTRY( node, &notifications, NOTIFICATIONLIST, entry )
287 {
288 if (node->id == hNotify)
289 {
290 DeleteNode( node );
291 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
292 return TRUE;
293 }
294 }
295 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
296 return FALSE;
297 }
298
299 /*************************************************************************
300 * SHChangeNotifyUpdateEntryList [SHELL32.5]
301 */
302 BOOL WINAPI SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2,
303 DWORD unknown3, DWORD unknown4)
304 {
305 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
306 unknown1, unknown2, unknown3, unknown4);
307
308 return TRUE;
309 }
310
311 struct new_delivery_notification
312 {
313 LONG event;
314 DWORD pidl1_size;
315 DWORD pidl2_size;
316 LPITEMIDLIST pidls[2];
317 BYTE data[1];
318 };
319
320 static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL sub )
321 {
322 TRACE("%p %p %d\n", changed, watched, sub );
323 if ( !watched )
324 return FALSE;
325 if (ILIsEqual( watched, changed ) )
326 return TRUE;
327 if( sub && ILIsParent( watched, changed, FALSE ) )
328 return TRUE;
329 return FALSE;
330 }
331
332 /*************************************************************************
333 * SHChangeNotify [SHELL32.@]
334 */
335 void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
336 {
337 struct notification_recipients {
338 struct list entry;
339 HWND hwnd;
340 DWORD msg;
341 DWORD flags;
342 } *cur, *next;
343
344 HANDLE shared_data = NULL;
345 LPITEMIDLIST Pidls[2];
346 LPNOTIFICATIONLIST ptr;
347 struct list recipients;
348
349 Pidls[0] = NULL;
350 Pidls[1] = NULL;
351
352 TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2);
353
354 if(uFlags & ~(SHCNF_TYPE|SHCNF_FLUSH))
355 FIXME("ignoring unsupported flags: %x\n", uFlags);
356
357 if( ( wEventId & SHCNE_NOITEMEVENTS ) && ( dwItem1 || dwItem2 ) )
358 {
359 TRACE("dwItem1 and dwItem2 are not zero, but should be\n");
360 dwItem1 = 0;
361 dwItem2 = 0;
362 return;
363 }
364 else if( ( wEventId & SHCNE_ONEITEMEVENTS ) && dwItem2 )
365 {
366 TRACE("dwItem2 is not zero, but should be\n");
367 dwItem2 = 0;
368 return;
369 }
370
371 if( ( ( wEventId & SHCNE_NOITEMEVENTS ) &&
372 ( wEventId & ~SHCNE_NOITEMEVENTS ) ) ||
373 ( ( wEventId & SHCNE_ONEITEMEVENTS ) &&
374 ( wEventId & ~SHCNE_ONEITEMEVENTS ) ) ||
375 ( ( wEventId & SHCNE_TWOITEMEVENTS ) &&
376 ( wEventId & ~SHCNE_TWOITEMEVENTS ) ) )
377 {
378 WARN("mutually incompatible events listed\n");
379 return;
380 }
381
382 /* convert paths in IDLists*/
383 switch (uFlags & SHCNF_TYPE)
384 {
385 case SHCNF_PATHA:
386 if (dwItem1) Pidls[0] = SHSimpleIDListFromPathA(dwItem1); //FIXME
387 if (dwItem2) Pidls[1] = SHSimpleIDListFromPathA(dwItem2); //FIXME
388 break;
389 case SHCNF_PATHW:
390 if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW(dwItem1);
391 if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW(dwItem2);
392 #ifdef __REACTOS__
393 if (wEventId & (SHCNE_MKDIR | SHCNE_RMDIR | SHCNE_UPDATEDIR | SHCNE_RENAMEFOLDER))
394 {
395 /*
396 * The last items in the ID are currently files. So we chop off the last
397 * entry, and create a new one using a find data struct.
398 */
399 if (dwItem1 && Pidls[0]){
400 WIN32_FIND_DATAW wfd;
401 LPITEMIDLIST oldpidl, newpidl;
402 LPWSTR p = PathFindFileNameW((LPCWSTR)dwItem1);
403 ILRemoveLastID(Pidls[0]);
404 lstrcpynW(&wfd.cFileName[0], p, MAX_PATH);
405 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
406 newpidl = _ILCreateFromFindDataW(&wfd);
407 oldpidl = ILClone(Pidls[0]);
408 ILFree(Pidls[0]);
409 Pidls[0] = ILCombine(oldpidl, newpidl);
410 ILFree(newpidl);
411 ILFree(oldpidl);
412 }
413 if (dwItem2 && Pidls[1]){
414 WIN32_FIND_DATAW wfd;
415 LPITEMIDLIST oldpidl, newpidl;
416 LPWSTR p = PathFindFileNameW((LPCWSTR)dwItem2);
417 ILRemoveLastID(Pidls[1]);
418 lstrcpynW(&wfd.cFileName[0], p, MAX_PATH);
419 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
420 newpidl = _ILCreateFromFindDataW(&wfd);
421 oldpidl = ILClone(Pidls[0]);
422 ILFree(Pidls[1]);
423 Pidls[1] = ILCombine(oldpidl, newpidl);
424 ILFree(newpidl);
425 ILFree(oldpidl);
426 }
427 }
428 #endif
429 break;
430 case SHCNF_IDLIST:
431 Pidls[0] = ILClone(dwItem1);
432 Pidls[1] = ILClone(dwItem2);
433 break;
434 case SHCNF_PRINTERA:
435 case SHCNF_PRINTERW:
436 FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n");
437 return;
438 case SHCNF_DWORD:
439 default:
440 FIXME("unknown type %08x\n", uFlags & SHCNF_TYPE);
441 return;
442 }
443
444 list_init(&recipients);
445 EnterCriticalSection(&SHELL32_ChangenotifyCS);
446 LIST_FOR_EACH_ENTRY( ptr, &notifications, NOTIFICATIONLIST, entry )
447 {
448 struct notification_recipients *item;
449 BOOL notify = FALSE;
450 DWORD i;
451
452 for( i=0; (i<ptr->cidl) && !notify ; i++ )
453 {
454 LPCITEMIDLIST pidl = ptr->apidl[i].pidl;
455 BOOL subtree = ptr->apidl[i].fRecursive;
456
457 if (wEventId & ptr->wEventMask)
458 {
459 if( !pidl ) /* all ? */
460 notify = TRUE;
461 else if( wEventId & SHCNE_NOITEMEVENTS )
462 notify = TRUE;
463 else if( wEventId & ( SHCNE_ONEITEMEVENTS | SHCNE_TWOITEMEVENTS ) )
464 notify = should_notify( Pidls[0], pidl, subtree );
465 else if( wEventId & SHCNE_TWOITEMEVENTS )
466 notify = should_notify( Pidls[1], pidl, subtree );
467 }
468 }
469
470 if( !notify )
471 continue;
472
473 item = SHAlloc(sizeof(struct notification_recipients));
474 if(!item) {
475 ERR("out of memory\n");
476 continue;
477 }
478
479 item->hwnd = ptr->hwnd;
480 item->msg = ptr->uMsg;
481 item->flags = ptr->dwFlags;
482 list_add_tail(&recipients, &item->entry);
483 }
484 LeaveCriticalSection(&SHELL32_ChangenotifyCS);
485
486 LIST_FOR_EACH_ENTRY_SAFE(cur, next, &recipients, struct notification_recipients, entry)
487 {
488 TRACE("notifying %p, event %s(%x)\n", cur->hwnd, DumpEvent(wEventId), wEventId);
489
490 if (cur->flags & SHCNRF_NewDelivery) {
491 if(!shared_data) {
492 struct new_delivery_notification *notification;
493 UINT size1 = ILGetSize(Pidls[0]), size2 = ILGetSize(Pidls[1]);
494 UINT offset = (size1+sizeof(int)-1)/sizeof(int)*sizeof(int);
495
496 notification = SHAlloc(sizeof(struct new_delivery_notification)+offset+size2);
497 if(!notification) {
498 ERR("out of memory\n");
499 } else {
500 notification->event = wEventId;
501 notification->pidl1_size = size1;
502 notification->pidl2_size = size2;
503 if(size1)
504 memcpy(notification->data, Pidls[0], size1);
505 if(size2)
506 memcpy(notification->data+offset, Pidls[1], size2);
507
508 shared_data = SHAllocShared(notification,
509 sizeof(struct new_delivery_notification)+size1+size2,
510 GetCurrentProcessId());
511 SHFree(notification);
512 }
513 }
514
515 if(shared_data)
516 SendMessageA(cur->hwnd, cur->msg, (WPARAM)shared_data, GetCurrentProcessId());
517 else
518 ERR("out of memory\n");
519 } else {
520 SendMessageA(cur->hwnd, cur->msg, (WPARAM)Pidls, wEventId);
521 }
522
523 list_remove(&cur->entry);
524 SHFree(cur);
525 }
526 SHFreeShared(shared_data, GetCurrentProcessId());
527 SHFree(Pidls[0]);
528 SHFree(Pidls[1]);
529
530 #ifndef __REACTOS__
531 if (wEventId & SHCNE_ASSOCCHANGED)
532 {
533 static const WCHAR args[] = {' ','-','a',0 };
534 TRACE("refreshing file type associations\n");
535 run_winemenubuilder( args );
536 }
537 #endif
538 }
539
540 /*************************************************************************
541 * NTSHChangeNotifyRegister [SHELL32.640]
542 * NOTES
543 * Idlist is an array of structures and Count specifies how many items in the array.
544 * count should always be one when calling SHChangeNotifyRegister, or
545 * SHChangeNotifyDeregister will not work properly.
546 */
547 EXTERN_C ULONG WINAPI NTSHChangeNotifyRegister(
548 HWND hwnd,
549 int fSources,
550 LONG fEvents,
551 UINT msg,
552 int count,
553 SHChangeNotifyEntry *idlist)
554 {
555 return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery,
556 fEvents, msg, count, idlist);
557 }
558
559 /*************************************************************************
560 * SHChangeNotification_Lock [SHELL32.644]
561 */
562 HANDLE WINAPI SHChangeNotification_Lock(
563 HANDLE hChange,
564 DWORD dwProcessId,
565 LPITEMIDLIST **lppidls,
566 LPLONG lpwEventId)
567 {
568 struct new_delivery_notification *ndn;
569 UINT offset;
570
571 TRACE("%p %08x %p %p\n", hChange, dwProcessId, lppidls, lpwEventId);
572
573 ndn = SHLockShared(hChange, dwProcessId);
574 if(!ndn) {
575 WARN("SHLockShared failed\n");
576 return NULL;
577 }
578
579 if(lppidls) {
580 offset = (ndn->pidl1_size+sizeof(int)-1)/sizeof(int)*sizeof(int);
581 ndn->pidls[0] = ndn->pidl1_size ? (LPITEMIDLIST)ndn->data : NULL;
582 ndn->pidls[1] = ndn->pidl2_size ? (LPITEMIDLIST)(ndn->data+offset) : NULL;
583 *lppidls = ndn->pidls;
584 }
585
586 if(lpwEventId)
587 *lpwEventId = ndn->event;
588
589 return ndn;
590 }
591
592 /*************************************************************************
593 * SHChangeNotification_Unlock [SHELL32.645]
594 */
595 BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock)
596 {
597 TRACE("%p\n", hLock);
598 return SHUnlockShared(hLock);
599 }
600
601 /*************************************************************************
602 * NTSHChangeNotifyDeregister [SHELL32.641]
603 */
604 DWORD WINAPI NTSHChangeNotifyDeregister(ULONG x1)
605 {
606 FIXME("(0x%08x):semi stub.\n",x1);
607
608 return SHChangeNotifyDeregister( x1 );
609 }
610
611 #ifdef __REACTOS__
612
613 static
614 void
615 CALLBACK
616 _AddDirectoryProc(ULONG_PTR arg)
617 {
618 LPNOTIFYREGISTER item = (LPNOTIFYREGISTER)arg;
619 _BeginRead(item);
620 }
621
622 BOOL _OpenDirectory(LPNOTIFYREGISTER item)
623 {
624 STRRET strFile;
625 IShellFolder *psfDesktop;
626 HRESULT hr;
627
628 // Makes function idempotent
629 if (item->hDirectory && !(item->hDirectory == INVALID_HANDLE_VALUE))
630 return TRUE;
631
632 hr = SHGetDesktopFolder(&psfDesktop);
633 if (!SUCCEEDED(hr))
634 return FALSE;
635
636 hr = IShellFolder_GetDisplayNameOf(psfDesktop, item->pidl, SHGDN_FORPARSING, &strFile);
637 if (!SUCCEEDED(hr))
638 return FALSE;
639
640 hr = StrRetToBufW(&strFile, NULL, item->wstrDirectory, _countof(item->wstrDirectory));
641 if (!SUCCEEDED(hr))
642 return FALSE;
643
644 TRACE("_OpenDirectory %s\n", debugstr_w(item->wstrDirectory));
645
646 item->hDirectory = CreateFileW(item->wstrDirectory, // pointer to the file name
647 GENERIC_READ | FILE_LIST_DIRECTORY, // access (read/write) mode
648 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
649 NULL, // security descriptor
650 OPEN_EXISTING, // how to create
651 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // file attributes
652 NULL); // file with attributes to copy
653
654 if (item->hDirectory == INVALID_HANDLE_VALUE)
655 {
656 return FALSE;
657 }
658 return TRUE;
659 }
660
661 static void CALLBACK _RequestTermination(ULONG_PTR arg)
662 {
663 LPNOTIFYREGISTER item = (LPNOTIFYREGISTER) arg;
664 TRACE("_RequestTermination %p \n", item->hDirectory);
665 if (!item->hDirectory || item->hDirectory == INVALID_HANDLE_VALUE) return;
666
667 CancelIo(item->hDirectory);
668 CloseHandle(item->hDirectory);
669 item->hDirectory = NULL;
670 }
671
672 static
673 void
674 CALLBACK
675 _NotificationCompletion(DWORD dwErrorCode, // completion code
676 DWORD dwNumberOfBytesTransfered, // number of bytes transferred
677 LPOVERLAPPED lpOverlapped) // I/O information buffer
678 {
679 /* MSDN: The hEvent member of the OVERLAPPED structure is not used by the
680 system, so you can use it yourself. We do just this, storing a pointer
681 to the working struct in the overlapped structure. */
682 LPNOTIFYREGISTER item = (LPNOTIFYREGISTER) lpOverlapped->hEvent;
683 TRACE("_NotificationCompletion\n");
684
685 if (dwErrorCode == ERROR_OPERATION_ABORTED)
686 {
687 /* Command was induced by CancelIo in the shutdown procedure. */
688 TRACE("_NotificationCompletion ended.\n");
689 return;
690 }
691
692 /* This likely means overflow, so force whole directory refresh. */
693 if (!dwNumberOfBytesTransfered)
694 {
695 ERR("_NotificationCompletion overflow\n");
696
697 ZeroMemory(item->buffer, BUFFER_SIZE);
698 _BeginRead(item);
699
700 SHChangeNotify(SHCNE_UPDATEITEM | SHCNE_INTERRUPT,
701 SHCNF_IDLIST,
702 item->pidl,
703 NULL);
704
705 return;
706 }
707
708 /*
709 * Get the new read issued as fast as possible (before we do the
710 * processing and message posting). All of the file notification
711 * occur on one thread so the buffers should not collide with one another.
712 * The extra zero mems are because the FNI size isn't written correctly.
713 */
714
715 ZeroMemory(item->backBuffer, BUFFER_SIZE);
716 memcpy(item->backBuffer, item->buffer, dwNumberOfBytesTransfered);
717 ZeroMemory(item->buffer, BUFFER_SIZE);
718
719 _BeginRead(item);
720
721 _ProcessNotification(item);
722 }
723
724 static VOID _BeginRead(LPNOTIFYREGISTER item )
725 {
726 TRACE("_BeginRead %p \n", item->hDirectory);
727
728 /* This call needs to be reissued after every APC. */
729 if (!ReadDirectoryChangesW(item->hDirectory, // handle to directory
730 item->buffer, // read results buffer
731 BUFFER_SIZE, // length of buffer
732 FALSE, // monitoring option (recursion)
733 FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions
734 NULL, // bytes returned
735 &item->overlapped, // overlapped buffer
736 _NotificationCompletion)) // completion routine
737 ERR("ReadDirectoryChangesW failed. (%p, %p, %p, %p) Code: %u \n",
738 item->hDirectory,
739 item->buffer,
740 &item->overlapped,
741 _NotificationCompletion,
742 GetLastError());
743 }
744
745 DWORD _MapAction(DWORD dwAction, BOOL isDir)
746 {
747 switch (dwAction)
748 {
749 case FILE_ACTION_ADDED : return isDir ? SHCNE_MKDIR : SHCNE_CREATE;
750 case FILE_ACTION_REMOVED : return isDir ? SHCNE_RMDIR : SHCNE_DELETE;
751 case FILE_ACTION_MODIFIED : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
752 case FILE_ACTION_RENAMED_OLD_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
753 case FILE_ACTION_RENAMED_NEW_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
754 default: return SHCNE_UPDATEITEM;
755 }
756 }
757
758 VOID _ProcessNotification(LPNOTIFYREGISTER item)
759 {
760 BYTE* pBase = item->backBuffer;
761 TRACE("_ProcessNotification\n");
762
763 for (;;)
764 {
765 FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)pBase;
766 LPWSTR wszFilename;
767 INT len = 0;
768 WCHAR wstrFilename[MAX_PATH];
769 WCHAR tmp[MAX_PATH];
770 StringCchCopy(tmp, fni->FileNameLength, fni->FileName);
771
772 PathCombine(wstrFilename, item->wstrDirectory, tmp);
773
774 /* If it could be a short filename, expand it. */
775 wszFilename = PathFindFileNameW(wstrFilename);
776
777 len = lstrlenW(wszFilename);
778 /* The maximum length of an 8.3 filename is twelve, including the dot. */
779 if (len <= 12 && wcschr(wszFilename, L'~'))
780 {
781 /* Convert to the long filename form. Unfortunately, this
782 does not work for deletions, so it's an imperfect fix. */
783 wchar_t wbuf[MAX_PATH];
784 if (GetLongPathName(wstrFilename, wbuf, _countof (wbuf)) > 0)
785 StringCchCopyW(wstrFilename, MAX_PATH, wbuf);
786 }
787
788 /* On deletion of a folder PathIsDirectory will return false even if
789 it *was* a directory, so, again, imperfect. */
790 SHChangeNotify(_MapAction(fni->Action, PathIsDirectory(wstrFilename)) | SHCNE_INTERRUPT,
791 SHCNF_PATHW,
792 wstrFilename,
793 NULL);
794
795 if (!fni->NextEntryOffset)
796 break;
797 pBase += fni->NextEntryOffset;
798 }
799 }
800
801 static void CALLBACK _RequestAllTermination(ULONG_PTR arg)
802 {
803 m_bTerminate = TRUE;
804 }
805
806 static unsigned int WINAPI _RunAsyncThreadProc(LPVOID arg)
807 {
808 m_bTerminate = FALSE;
809 while (!m_bTerminate)
810 {
811 SleepEx(INFINITE, TRUE);
812 }
813 return 0;
814 }
815
816 #endif /* __REACTOS__ */