[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 && _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 IShellFolder_Release(psfDesktop);
638 if (!SUCCEEDED(hr))
639 return FALSE;
640
641 hr = StrRetToBufW(&strFile, NULL, item->wstrDirectory, _countof(item->wstrDirectory));
642 if (!SUCCEEDED(hr))
643 return FALSE;
644
645 TRACE("_OpenDirectory %s\n", debugstr_w(item->wstrDirectory));
646
647 item->hDirectory = CreateFileW(item->wstrDirectory, // pointer to the file name
648 GENERIC_READ | FILE_LIST_DIRECTORY, // access (read/write) mode
649 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
650 NULL, // security descriptor
651 OPEN_EXISTING, // how to create
652 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // file attributes
653 NULL); // file with attributes to copy
654
655 if (item->hDirectory == INVALID_HANDLE_VALUE)
656 {
657 return FALSE;
658 }
659 return TRUE;
660 }
661
662 static void CALLBACK _RequestTermination(ULONG_PTR arg)
663 {
664 LPNOTIFYREGISTER item = (LPNOTIFYREGISTER) arg;
665 TRACE("_RequestTermination %p \n", item->hDirectory);
666 if (!item->hDirectory || item->hDirectory == INVALID_HANDLE_VALUE) return;
667
668 CancelIo(item->hDirectory);
669 CloseHandle(item->hDirectory);
670 item->hDirectory = NULL;
671 }
672
673 static
674 void
675 CALLBACK
676 _NotificationCompletion(DWORD dwErrorCode, // completion code
677 DWORD dwNumberOfBytesTransfered, // number of bytes transferred
678 LPOVERLAPPED lpOverlapped) // I/O information buffer
679 {
680 /* MSDN: The hEvent member of the OVERLAPPED structure is not used by the
681 system, so you can use it yourself. We do just this, storing a pointer
682 to the working struct in the overlapped structure. */
683 LPNOTIFYREGISTER item = (LPNOTIFYREGISTER) lpOverlapped->hEvent;
684 TRACE("_NotificationCompletion\n");
685
686 if (dwErrorCode == ERROR_OPERATION_ABORTED)
687 {
688 /* Command was induced by CancelIo in the shutdown procedure. */
689 TRACE("_NotificationCompletion ended.\n");
690 return;
691 }
692
693 /* This likely means overflow, so force whole directory refresh. */
694 if (!dwNumberOfBytesTransfered)
695 {
696 ERR("_NotificationCompletion overflow\n");
697
698 ZeroMemory(item->buffer, BUFFER_SIZE);
699 _BeginRead(item);
700
701 SHChangeNotify(SHCNE_UPDATEITEM | SHCNE_INTERRUPT,
702 SHCNF_IDLIST,
703 item->pidl,
704 NULL);
705
706 return;
707 }
708
709 /*
710 * Get the new read issued as fast as possible (before we do the
711 * processing and message posting). All of the file notification
712 * occur on one thread so the buffers should not collide with one another.
713 * The extra zero mems are because the FNI size isn't written correctly.
714 */
715
716 ZeroMemory(item->backBuffer, BUFFER_SIZE);
717 memcpy(item->backBuffer, item->buffer, dwNumberOfBytesTransfered);
718 ZeroMemory(item->buffer, BUFFER_SIZE);
719
720 _BeginRead(item);
721
722 _ProcessNotification(item);
723 }
724
725 static VOID _BeginRead(LPNOTIFYREGISTER item )
726 {
727 TRACE("_BeginRead %p \n", item->hDirectory);
728
729 /* This call needs to be reissued after every APC. */
730 if (!ReadDirectoryChangesW(item->hDirectory, // handle to directory
731 item->buffer, // read results buffer
732 BUFFER_SIZE, // length of buffer
733 FALSE, // monitoring option (recursion)
734 FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME, // filter conditions
735 NULL, // bytes returned
736 &item->overlapped, // overlapped buffer
737 _NotificationCompletion)) // completion routine
738 ERR("ReadDirectoryChangesW failed. (%p, %p, %p, %p) Code: %u \n",
739 item->hDirectory,
740 item->buffer,
741 &item->overlapped,
742 _NotificationCompletion,
743 GetLastError());
744 }
745
746 DWORD _MapAction(DWORD dwAction, BOOL isDir)
747 {
748 switch (dwAction)
749 {
750 case FILE_ACTION_ADDED : return isDir ? SHCNE_MKDIR : SHCNE_CREATE;
751 case FILE_ACTION_REMOVED : return isDir ? SHCNE_RMDIR : SHCNE_DELETE;
752 case FILE_ACTION_MODIFIED : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
753 case FILE_ACTION_RENAMED_OLD_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
754 case FILE_ACTION_RENAMED_NEW_NAME : return isDir ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM;
755 default: return SHCNE_UPDATEITEM;
756 }
757 }
758
759 VOID _ProcessNotification(LPNOTIFYREGISTER item)
760 {
761 BYTE* pBase = item->backBuffer;
762 TRACE("_ProcessNotification\n");
763
764 for (;;)
765 {
766 FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)pBase;
767 LPWSTR wszFilename;
768 INT len = 0;
769 WCHAR wstrFilename[MAX_PATH];
770 WCHAR tmp[MAX_PATH];
771 StringCchCopy(tmp, fni->FileNameLength, fni->FileName);
772
773 PathCombine(wstrFilename, item->wstrDirectory, tmp);
774
775 /* If it could be a short filename, expand it. */
776 wszFilename = PathFindFileNameW(wstrFilename);
777
778 len = lstrlenW(wszFilename);
779 /* The maximum length of an 8.3 filename is twelve, including the dot. */
780 if (len <= 12 && wcschr(wszFilename, L'~'))
781 {
782 /* Convert to the long filename form. Unfortunately, this
783 does not work for deletions, so it's an imperfect fix. */
784 wchar_t wbuf[MAX_PATH];
785 if (GetLongPathName(wstrFilename, wbuf, _countof (wbuf)) > 0)
786 StringCchCopyW(wstrFilename, MAX_PATH, wbuf);
787 }
788
789 /* On deletion of a folder PathIsDirectory will return false even if
790 it *was* a directory, so, again, imperfect. */
791 SHChangeNotify(_MapAction(fni->Action, PathIsDirectory(wstrFilename)) | SHCNE_INTERRUPT,
792 SHCNF_PATHW,
793 wstrFilename,
794 NULL);
795
796 if (!fni->NextEntryOffset)
797 break;
798 pBase += fni->NextEntryOffset;
799 }
800 }
801
802 static void CALLBACK _RequestAllTermination(ULONG_PTR arg)
803 {
804 m_bTerminate = TRUE;
805 }
806
807 static unsigned int WINAPI _RunAsyncThreadProc(LPVOID arg)
808 {
809 m_bTerminate = FALSE;
810 while (!m_bTerminate)
811 {
812 SleepEx(INFINITE, TRUE);
813 }
814 return 0;
815 }
816
817 #endif /* __REACTOS__ */