2 * Trash virtual folder support
4 * Copyright (C) 2006 Mikolaj Zalewski
5 * Copyright 2011 Jay Yang
6 * Copyright 2021 Alexandre Julliard
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #define NONAMELESSUNION
38 #include "shellfolder.h"
40 #include "knownfolders.h"
41 #include "wine/debug.h"
43 #include "shell32_main.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(recyclebin
);
49 static const shvheader RecycleBinColumns
[] =
51 {&FMTID_Storage
, PID_STG_NAME
, IDS_SHV_COLUMN1
, SHCOLSTATE_TYPE_STR
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 30},
52 {&FMTID_Displaced
, PID_DISPLACED_FROM
, IDS_SHV_COLUMN_DELFROM
, SHCOLSTATE_TYPE_STR
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 30},
53 {&FMTID_Displaced
, PID_DISPLACED_DATE
, IDS_SHV_COLUMN_DELDATE
, SHCOLSTATE_TYPE_DATE
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 20},
54 {&FMTID_Storage
, PID_STG_SIZE
, IDS_SHV_COLUMN2
, SHCOLSTATE_TYPE_INT
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 20},
55 {&FMTID_Storage
, PID_STG_STORAGETYPE
,IDS_SHV_COLUMN3
, SHCOLSTATE_TYPE_INT
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 20},
56 {&FMTID_Storage
, PID_STG_WRITETIME
, IDS_SHV_COLUMN4
, SHCOLSTATE_TYPE_DATE
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 20},
57 /* {&FMTID_Storage, PID_STG_CREATETIME, "creation time", SHCOLSTATE_TYPE_DATE, LVCFMT_LEFT, 20}, */
58 /* {&FMTID_Storage, PID_STG_ATTRIBUTES, "attribs", SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 20}, */
62 #define COLUMN_DELFROM 1
63 #define COLUMN_DATEDEL 2
66 #define COLUMN_MTIME 5
68 #define COLUMNS_COUNT 6
70 static const WIN32_FIND_DATAW
*get_trash_item_data( LPCITEMIDLIST id
)
72 if (id
->mkid
.cb
< 2 + 1 + sizeof(WIN32_FIND_DATAW
) + 2) return NULL
;
73 if (id
->mkid
.abID
[0] != 0) return NULL
;
74 return (const WIN32_FIND_DATAW
*)(id
->mkid
.abID
+ 1);
77 static INIT_ONCE trash_dir_once
;
78 static WCHAR
*trash_dir
;
79 static WCHAR
*trash_info_dir
;
80 static ULONG random_seed
;
82 extern void CDECL
wine_get_host_version( const char **sysname
, const char **release
);
84 static BOOL
is_macos(void)
88 wine_get_host_version( &sysname
, NULL
);
89 return !strcmp( sysname
, "Darwin" );
92 static BOOL WINAPI
init_trash_dirs( INIT_ONCE
*once
, void *param
, void **context
)
94 const WCHAR
*home
= _wgetenv( L
"WINEHOMEDIR" );
95 WCHAR
*info
= NULL
, *files
= NULL
;
98 if (!home
) return TRUE
;
101 files
= heap_alloc( (lstrlenW(home
) + lstrlenW(L
"\\.Trash") + 1) * sizeof(WCHAR
) );
102 lstrcpyW( files
, home
);
103 lstrcatW( files
, L
"\\.Trash" );
104 files
[1] = '\\'; /* change \??\ to \\?\ */
108 const WCHAR
*data_home
= _wgetenv( L
"XDG_DATA_HOME" );
109 const WCHAR
*fmt
= L
"%s/.local/share/Trash";
112 if (data_home
&& data_home
[0] == '/')
115 fmt
= L
"\\??\\unix%s/Trash";
117 len
= lstrlenW(home
) + lstrlenW(fmt
) + 7;
118 files
= heap_alloc( len
* sizeof(WCHAR
) );
119 swprintf( files
, len
, fmt
, home
);
120 files
[1] = '\\'; /* change \??\ to \\?\ */
121 for (p
= files
; *p
; p
++) if (*p
== '/') *p
= '\\';
122 CreateDirectoryW( files
, NULL
);
123 info
= heap_alloc( len
* sizeof(WCHAR
) );
124 lstrcpyW( info
, files
);
125 lstrcatW( files
, L
"\\files" );
126 lstrcatW( info
, L
"\\info" );
127 if (!CreateDirectoryW( info
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
) goto done
;
128 trash_info_dir
= info
;
131 if (!CreateDirectoryW( files
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
) goto done
;
133 random_seed
= GetTickCount();
142 BOOL
is_trash_available(void)
144 InitOnceExecuteOnce( &trash_dir_once
, init_trash_dirs
, NULL
, NULL
);
145 return trash_dir
!= NULL
;
148 static void encode( const char *src
, char *dst
)
150 static const char escape_chars
[] = "^&`{}|[]'<>\\#%\"+";
151 static const char hexchars
[] = "0123456789ABCDEF";
155 if (*src
<= 0x20 || *src
>= 0x7f || strchr( escape_chars
, *src
))
158 *dst
++ = hexchars
[(unsigned char)*src
/ 16];
159 *dst
++ = hexchars
[(unsigned char)*src
% 16];
166 static char *decode( char *buf
)
168 const char *src
= buf
;
169 char *dst
= buf
; /* decode in place */
177 if (src
[1] >= '0' && src
[1] <= '9') v
= src
[1] - '0';
178 else if (src
[1] >= 'A' && src
[1] <= 'F') v
= src
[1] - 'A' + 10;
179 else if (src
[1] >= 'a' && src
[1] <= 'f') v
= src
[1] - 'a' + 10;
182 if (src
[2] >= '0' && src
[2] <= '9') v
+= src
[2] - '0';
183 else if (src
[2] >= 'A' && src
[2] <= 'F') v
+= src
[2] - 'A' + 10;
184 else if (src
[2] >= 'a' && src
[2] <= 'f') v
+= src
[2] - 'a' + 10;
188 if (src
[1] && src
[2]) src
+= 3;
191 else *dst
++ = *src
++;
197 static BOOL
write_trashinfo_file( HANDLE handle
, const WCHAR
*orig_path
)
202 if (!(path
= wine_get_unix_file_name( orig_path
))) return FALSE
;
203 GetLocalTime( &now
);
205 buffer
= heap_alloc( strlen(path
) * 3 + 128 );
206 strcpy( buffer
, "[Trash Info]\nPath=" );
207 encode( path
, buffer
+ strlen(buffer
) );
208 sprintf( buffer
+ strlen(buffer
), "\nDeletionDate=%04u-%02u-%02uT%02u:%02u:%02u\n",
209 now
.wYear
, now
.wMonth
, now
.wDay
, now
.wHour
, now
.wMinute
, now
.wSecond
);
210 WriteFile( handle
, buffer
, strlen(buffer
), NULL
, NULL
);
216 static void read_trashinfo_file( HANDLE handle
, WIN32_FIND_DATAW
*data
)
220 char *buffer
, *next
, *p
;
223 size
= GetFileSize( handle
, NULL
);
224 if (!(buffer
= heap_alloc( size
+ 1 ))) return;
225 if (!ReadFile( handle
, buffer
, size
, &size
, NULL
)) size
= 0;
231 if ((next
= strchr( next
, '\n' ))) *next
++ = 0;
232 while (*p
== ' ' || *p
== '\t') p
++;
233 if (!strncmp( p
, "[Trash Info]", 12 ))
238 if (header
&& !strncmp( p
, "Path=", 5 ))
241 if ((path
= wine_get_dos_file_name( decode( p
))))
243 lstrcpynW( data
->cFileName
, path
, MAX_PATH
);
248 /* show only the file name */
249 char *file
= strrchr( p
, '/' );
252 MultiByteToWideChar( CP_UNIXCP
, 0, file
, -1, data
->cFileName
, MAX_PATH
);
256 if (header
&& !strncmp( p
, "DeletionDate=", 13 ))
258 int year
, mon
, day
, hour
, min
, sec
;
259 if (sscanf( p
+ 13, "%d-%d-%dT%d:%d:%d", &year
, &mon
, &day
, &hour
, &min
, &sec
) == 6)
261 SYSTEMTIME time
= { year
, mon
, 0, day
, hour
, min
, sec
};
263 if (SystemTimeToFileTime( &time
, &ft
))
264 LocalFileTimeToFileTime( &ft
, &data
->ftLastAccessTime
);
272 BOOL
trash_file( const WCHAR
*path
)
274 WCHAR
*dest
= NULL
, *file
= PathFindFileNameW( path
);
278 InitOnceExecuteOnce( &trash_dir_once
, init_trash_dirs
, NULL
, NULL
);
279 if (!trash_dir
) return FALSE
;
281 len
= lstrlenW(trash_dir
) + lstrlenW(file
) + 11;
282 dest
= heap_alloc( len
* sizeof(WCHAR
) );
287 ULONG infolen
= lstrlenW(trash_info_dir
) + lstrlenW(file
) + 21;
288 WCHAR
*info
= heap_alloc( infolen
* sizeof(WCHAR
) );
290 swprintf( info
, infolen
, L
"%s\\%s.trashinfo", trash_info_dir
, file
);
291 for (i
= 0; i
< 1000; i
++)
293 handle
= CreateFileW( info
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, 0 );
294 if (handle
!= INVALID_HANDLE_VALUE
) break;
295 swprintf( info
, infolen
, L
"%s\\%s-%08x.trashinfo", trash_info_dir
, file
, RtlRandom( &random_seed
));
297 if (handle
!= INVALID_HANDLE_VALUE
)
299 if ((ret
= write_trashinfo_file( handle
, path
)))
301 ULONG namelen
= lstrlenW(info
) - lstrlenW(trash_info_dir
) - 10 /* .trashinfo */;
302 swprintf( dest
, len
, L
"%s%.*s", trash_dir
, namelen
, info
+ lstrlenW(trash_info_dir
) );
303 ret
= MoveFileW( path
, dest
);
305 CloseHandle( handle
);
306 if (!ret
) DeleteFileW( info
);
311 swprintf( dest
, len
, L
"%s\\%s", trash_dir
, file
);
312 for (i
= 0; i
< 1000; i
++)
314 ret
= MoveFileW( path
, dest
);
315 if (ret
|| GetLastError() != ERROR_ALREADY_EXISTS
) break;
316 swprintf( dest
, len
, L
"%s\\%s-%08x", trash_dir
, file
, RtlRandom( &random_seed
));
319 if (ret
) TRACE( "%s -> %s\n", debugstr_w(path
), debugstr_w(dest
) );
324 static BOOL
get_trash_item_info( const WCHAR
*filename
, WIN32_FIND_DATAW
*data
)
328 return !!wcscmp( filename
, L
".DS_Store" );
333 ULONG len
= lstrlenW(trash_info_dir
) + lstrlenW(filename
) + 12;
334 WCHAR
*info
= heap_alloc( len
* sizeof(WCHAR
) );
336 swprintf( info
, len
, L
"%s\\%s.trashinfo", trash_info_dir
, filename
);
337 handle
= CreateFileW( info
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0 );
339 if (handle
== INVALID_HANDLE_VALUE
) return FALSE
;
340 read_trashinfo_file( handle
, data
);
341 CloseHandle( handle
);
346 static HRESULT
add_trash_item( WIN32_FIND_DATAW
*orig_data
, LPITEMIDLIST
**pidls
,
347 ULONG
*count
, ULONG
*size
)
350 WIN32_FIND_DATAW
*data
;
351 const WCHAR
*filename
= orig_data
->cFileName
;
352 ULONG len
= offsetof( ITEMIDLIST
, mkid
.abID
[1 + sizeof(*data
) + (lstrlenW(filename
)+1) * sizeof(WCHAR
)]);
354 if (!(pidl
= SHAlloc( len
+ 2 ))) return E_OUTOFMEMORY
;
356 pidl
->mkid
.abID
[0] = 0;
357 data
= (WIN32_FIND_DATAW
*)(pidl
->mkid
.abID
+ 1);
358 memcpy( data
, orig_data
, sizeof(*data
) );
359 lstrcpyW( (WCHAR
*)(data
+ 1), filename
);
360 *(USHORT
*)((char *)pidl
+ len
) = 0;
362 if (get_trash_item_info( filename
, data
))
368 *size
= max( *size
* 2, 32 );
369 new = heap_realloc( *pidls
, *size
* sizeof(**pidls
) );
373 return E_OUTOFMEMORY
;
377 (*pidls
)[(*count
)++] = pidl
;
379 else SHFree( pidl
); /* ignore it */
383 static HRESULT
enum_trash_items( LPITEMIDLIST
**pidls
, int *ret_count
)
387 WIN32_FIND_DATAW data
;
388 LPITEMIDLIST
*ret
= NULL
;
389 ULONG count
= 0, size
= 0;
392 InitOnceExecuteOnce( &trash_dir_once
, init_trash_dirs
, NULL
, NULL
);
393 if (!trash_dir
) return E_FAIL
;
396 file
= heap_alloc( (lstrlenW(trash_dir
) + lstrlenW(L
"\\*") + 1) * sizeof(WCHAR
) );
397 lstrcpyW( file
, trash_dir
);
398 lstrcatW( file
, L
"\\*" );
399 handle
= FindFirstFileW( file
, &data
);
400 if (handle
!= INVALID_HANDLE_VALUE
)
404 if (!wcscmp( data
.cFileName
, L
"." )) continue;
405 if (!wcscmp( data
.cFileName
, L
".." )) continue;
406 hr
= add_trash_item( &data
, &ret
, &count
, &size
);
407 } while (hr
== S_OK
&& FindNextFileW( handle
, &data
));
411 if (FAILED(hr
)) while (count
) SHFree( &ret
[--count
] );
414 *pidls
= SHAlloc( count
* sizeof(**pidls
) );
415 memcpy( *pidls
, ret
, count
* sizeof(**pidls
) );
422 static void remove_trashinfo( const WCHAR
*filename
)
427 if (!trash_info_dir
) return;
428 len
= lstrlenW(trash_info_dir
) + lstrlenW(filename
) + 12;
429 info
= heap_alloc( len
* sizeof(WCHAR
) );
430 swprintf( info
, len
, L
"%s\\%s.trashinfo", trash_info_dir
, filename
);
435 static HRESULT
restore_trash_item( LPCITEMIDLIST pidl
)
437 const WIN32_FIND_DATAW
*data
= get_trash_item_data( pidl
);
438 WCHAR
*from
, *filename
= (WCHAR
*)(data
+ 1);
441 if (!wcschr( data
->cFileName
, '\\' ))
443 FIXME( "original name for %s not available\n", debugstr_w(data
->cFileName
) );
446 len
= lstrlenW(trash_dir
) + lstrlenW(filename
) + 2;
447 from
= heap_alloc( len
* sizeof(WCHAR
) );
448 swprintf( from
, len
, L
"%s\\%s", trash_dir
, filename
);
449 if (MoveFileW( from
, data
->cFileName
)) remove_trashinfo( filename
);
450 else WARN( "failed to restore %s to %s\n", debugstr_w(from
), debugstr_w(data
->cFileName
) );
455 static HRESULT
erase_trash_item( LPCITEMIDLIST pidl
)
457 const WIN32_FIND_DATAW
*data
= get_trash_item_data( pidl
);
458 WCHAR
*from
, *filename
= (WCHAR
*)(data
+ 1);
459 ULONG len
= lstrlenW(trash_dir
) + lstrlenW(filename
) + 2;
461 from
= heap_alloc( len
* sizeof(WCHAR
) );
462 swprintf( from
, len
, L
"%s\\%s", trash_dir
, filename
);
463 if (DeleteFileW( from
)) remove_trashinfo( filename
);
469 static HRESULT
FormatDateTime(LPWSTR buffer
, int size
, FILETIME ft
)
475 FileTimeToLocalFileTime(&ft
, &lft
);
476 FileTimeToSystemTime(&lft
, &time
);
478 ret
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, buffer
, size
);
479 if (ret
>0 && ret
<size
)
481 /* Append space + time without seconds */
483 GetTimeFormatW(LOCALE_USER_DEFAULT
, TIME_NOSECONDS
, &time
, NULL
, &buffer
[ret
], size
- ret
);
486 return (ret
!=0 ? E_FAIL
: S_OK
);
489 typedef struct tagRecycleBinMenu
491 IContextMenu2 IContextMenu2_iface
;
496 IShellFolder2
*folder
;
499 static const IContextMenu2Vtbl recycleBinMenuVtbl
;
501 static RecycleBinMenu
*impl_from_IContextMenu2(IContextMenu2
*iface
)
503 return CONTAINING_RECORD(iface
, RecycleBinMenu
, IContextMenu2_iface
);
506 static IContextMenu2
* RecycleBinMenu_Constructor(UINT cidl
, LPCITEMIDLIST
*apidl
, IShellFolder2
*folder
)
508 RecycleBinMenu
*This
= SHAlloc(sizeof(RecycleBinMenu
));
509 TRACE("(%u,%p)\n",cidl
,apidl
);
510 This
->IContextMenu2_iface
.lpVtbl
= &recycleBinMenuVtbl
;
512 This
->apidl
= _ILCopyaPidl(apidl
,cidl
);
513 IShellFolder2_AddRef(folder
);
514 This
->folder
= folder
;
516 return &This
->IContextMenu2_iface
;
519 static HRESULT WINAPI
RecycleBinMenu_QueryInterface(IContextMenu2
*iface
,
523 RecycleBinMenu
*This
= impl_from_IContextMenu2(iface
);
524 TRACE("(%p, %s, %p) - stub\n", This
, debugstr_guid(riid
), ppvObject
);
528 static ULONG WINAPI
RecycleBinMenu_AddRef(IContextMenu2
*iface
)
530 RecycleBinMenu
*This
= impl_from_IContextMenu2(iface
);
531 TRACE("(%p)\n", This
);
532 return InterlockedIncrement(&This
->refCount
);
536 static ULONG WINAPI
RecycleBinMenu_Release(IContextMenu2
*iface
)
538 RecycleBinMenu
*This
= impl_from_IContextMenu2(iface
);
540 TRACE("(%p)\n", This
);
541 result
= InterlockedDecrement(&This
->refCount
);
544 TRACE("Destroying object\n");
545 _ILFreeaPidl(This
->apidl
,This
->cidl
);
546 IShellFolder2_Release(This
->folder
);
552 static HRESULT WINAPI
RecycleBinMenu_QueryContextMenu(IContextMenu2
*iface
,
559 HMENU menures
= LoadMenuW(shell32_hInstance
,MAKEINTRESOURCEW(MENU_RECYCLEBIN
));
560 if(uFlags
& CMF_DEFAULTONLY
)
563 UINT idMax
= Shell_MergeMenus(hmenu
,GetSubMenu(menures
,0),indexMenu
,idCmdFirst
,idCmdLast
,MM_SUBMENUSHAVEIDS
);
564 TRACE("Added %d id(s)\n",idMax
-idCmdFirst
);
565 return MAKE_HRESULT(SEVERITY_SUCCESS
, FACILITY_NULL
, idMax
-idCmdFirst
+1);
569 static void DoErase(RecycleBinMenu
*This
)
572 IShellFolder2_QueryInterface(This
->folder
,&IID_ISFHelper
,(void**)&helper
);
574 ISFHelper_DeleteItems(helper
,This
->cidl
,(LPCITEMIDLIST
*)This
->apidl
);
577 static void DoRestore(RecycleBinMenu
*This
)
582 for(i
=0;i
<This
->cidl
;i
++)
584 const WIN32_FIND_DATAW
*data
= get_trash_item_data( This
->apidl
[i
] );
586 if(PathFileExistsW(data
->cFileName
))
588 PIDLIST_ABSOLUTE dest_pidl
= ILCreateFromPathW(data
->cFileName
);
591 if(_ILIsFolder(ILFindLastID(dest_pidl
)))
592 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_OVERWRITEFOLDER
, message
, ARRAY_SIZE(message
));
594 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_OVERWRITEFILE
, message
, ARRAY_SIZE(message
));
595 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_OVERWRITE_CAPTION
, caption
, ARRAY_SIZE(caption
));
597 if(ShellMessageBoxW(shell32_hInstance
,GetActiveWindow(),message
,
598 caption
,MB_YESNO
|MB_ICONEXCLAMATION
,
599 data
->cFileName
)!=IDYES
)
602 if(SUCCEEDED(restore_trash_item(This
->apidl
[i
])))
604 IPersistFolder2
*persist
;
605 LPITEMIDLIST root_pidl
;
606 PIDLIST_ABSOLUTE dest_pidl
= ILCreateFromPathW(data
->cFileName
);
607 BOOL is_folder
= _ILIsFolder(ILFindLastID(dest_pidl
));
608 IShellFolder2_QueryInterface(This
->folder
,&IID_IPersistFolder2
,
610 IPersistFolder2_GetCurFolder(persist
,&root_pidl
);
611 SHChangeNotify(is_folder
? SHCNE_RMDIR
: SHCNE_DELETE
,
612 SHCNF_IDLIST
,ILCombine(root_pidl
,This
->apidl
[i
]),0);
613 SHChangeNotify(is_folder
? SHCNE_MKDIR
: SHCNE_CREATE
,
614 SHCNF_IDLIST
,dest_pidl
,0);
621 static HRESULT WINAPI
RecycleBinMenu_InvokeCommand(IContextMenu2
*iface
,
622 LPCMINVOKECOMMANDINFO pici
)
624 RecycleBinMenu
*This
= impl_from_IContextMenu2(iface
);
625 LPCSTR verb
= pici
->lpVerb
;
626 if(IS_INTRESOURCE(verb
))
630 case IDM_RECYCLEBIN_ERASE
:
633 case IDM_RECYCLEBIN_RESTORE
:
643 static HRESULT WINAPI
RecycleBinMenu_GetCommandString(IContextMenu2
*iface
,
650 TRACE("(%p, %Iu, %u, %p, %s, %u) - stub\n",iface
,idCmd
,uType
,pwReserved
,debugstr_a(pszName
),cchMax
);
654 static HRESULT WINAPI
RecycleBinMenu_HandleMenuMsg(IContextMenu2
*iface
,
655 UINT uMsg
, WPARAM wParam
,
658 TRACE("(%p, %u, 0x%Ix, 0x%Ix) - stub\n",iface
,uMsg
,wParam
,lParam
);
663 static const IContextMenu2Vtbl recycleBinMenuVtbl
=
665 RecycleBinMenu_QueryInterface
,
666 RecycleBinMenu_AddRef
,
667 RecycleBinMenu_Release
,
668 RecycleBinMenu_QueryContextMenu
,
669 RecycleBinMenu_InvokeCommand
,
670 RecycleBinMenu_GetCommandString
,
671 RecycleBinMenu_HandleMenuMsg
,
678 typedef struct tagRecycleBin
680 IShellFolder2 IShellFolder2_iface
;
681 IPersistFolder2 IPersistFolder2_iface
;
682 ISFHelper ISFHelper_iface
;
688 static const IShellFolder2Vtbl recycleBinVtbl
;
689 static const IPersistFolder2Vtbl recycleBinPersistVtbl
;
690 static const ISFHelperVtbl sfhelperVtbl
;
692 static inline RecycleBin
*impl_from_IShellFolder2(IShellFolder2
*iface
)
694 return CONTAINING_RECORD(iface
, RecycleBin
, IShellFolder2_iface
);
697 static RecycleBin
*impl_from_IPersistFolder2(IPersistFolder2
*iface
)
699 return CONTAINING_RECORD(iface
, RecycleBin
, IPersistFolder2_iface
);
702 static RecycleBin
*impl_from_ISFHelper(ISFHelper
*iface
)
704 return CONTAINING_RECORD(iface
, RecycleBin
, ISFHelper_iface
);
707 static void RecycleBin_Destructor(RecycleBin
*This
);
709 HRESULT WINAPI
RecycleBin_Constructor(IUnknown
*pUnkOuter
, REFIID riid
, LPVOID
*ppOutput
)
714 return CLASS_E_NOAGGREGATION
;
716 obj
= SHAlloc(sizeof(RecycleBin
));
718 return E_OUTOFMEMORY
;
719 ZeroMemory(obj
, sizeof(RecycleBin
));
720 obj
->IShellFolder2_iface
.lpVtbl
= &recycleBinVtbl
;
721 obj
->IPersistFolder2_iface
.lpVtbl
= &recycleBinPersistVtbl
;
722 obj
->ISFHelper_iface
.lpVtbl
= &sfhelperVtbl
;
723 if (FAILED(ret
= IPersistFolder2_QueryInterface(&obj
->IPersistFolder2_iface
, riid
, ppOutput
)))
725 RecycleBin_Destructor(obj
);
728 /* InterlockedIncrement(&objCount);*/
732 static void RecycleBin_Destructor(RecycleBin
*This
)
734 /* InterlockedDecrement(&objCount);*/
739 static HRESULT WINAPI
RecycleBin_QueryInterface(IShellFolder2
*iface
, REFIID riid
, void **ppvObject
)
741 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
742 TRACE("(%p, %s, %p)\n", This
, debugstr_guid(riid
), ppvObject
);
745 if (IsEqualGUID(riid
, &IID_IUnknown
) || IsEqualGUID(riid
, &IID_IShellFolder
)
746 || IsEqualGUID(riid
, &IID_IShellFolder2
))
747 *ppvObject
= &This
->IShellFolder2_iface
;
749 if (IsEqualGUID(riid
, &IID_IPersist
) || IsEqualGUID(riid
, &IID_IPersistFolder
)
750 || IsEqualGUID(riid
, &IID_IPersistFolder2
))
751 *ppvObject
= &This
->IPersistFolder2_iface
;
752 if (IsEqualGUID(riid
, &IID_ISFHelper
))
753 *ppvObject
= &This
->ISFHelper_iface
;
755 if (*ppvObject
!= NULL
)
757 IUnknown_AddRef((IUnknown
*)*ppvObject
);
760 WARN("no interface %s\n", debugstr_guid(riid
));
761 return E_NOINTERFACE
;
764 static ULONG WINAPI
RecycleBin_AddRef(IShellFolder2
*iface
)
766 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
767 TRACE("(%p)\n", This
);
768 return InterlockedIncrement(&This
->refCount
);
771 static ULONG WINAPI
RecycleBin_Release(IShellFolder2
*iface
)
773 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
776 TRACE("(%p)\n", This
);
777 result
= InterlockedDecrement(&This
->refCount
);
780 TRACE("Destroy object\n");
781 RecycleBin_Destructor(This
);
786 static HRESULT WINAPI
RecycleBin_ParseDisplayName(IShellFolder2
*This
, HWND hwnd
, LPBC pbc
,
787 LPOLESTR pszDisplayName
, ULONG
*pchEaten
, LPITEMIDLIST
*ppidl
,
788 ULONG
*pdwAttributes
)
794 static HRESULT WINAPI
RecycleBin_EnumObjects(IShellFolder2
*iface
, HWND hwnd
, SHCONTF grfFlags
, IEnumIDList
**ppenumIDList
)
796 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
797 IEnumIDListImpl
*list
;
799 HRESULT ret
= E_OUTOFMEMORY
;
803 TRACE("(%p, %p, %lx, %p)\n", This
, hwnd
, grfFlags
, ppenumIDList
);
805 *ppenumIDList
= NULL
;
806 list
= IEnumIDList_Constructor();
808 return E_OUTOFMEMORY
;
810 if (grfFlags
& SHCONTF_NONFOLDERS
)
812 if (FAILED(ret
= enum_trash_items(&pidls
, &pidls_count
)))
814 for (i
=0; i
<pidls_count
; i
++)
815 if (!AddToEnumList(list
, pidls
[i
]))
819 *ppenumIDList
= &list
->IEnumIDList_iface
;
823 IEnumIDList_Release(&list
->IEnumIDList_iface
);
824 for (; i
<pidls_count
; i
++)
830 static HRESULT WINAPI
RecycleBin_BindToObject(IShellFolder2
*This
, LPCITEMIDLIST pidl
, LPBC pbc
, REFIID riid
, void **ppv
)
832 FIXME("(%p, %p, %p, %s, %p) - stub\n", This
, pidl
, pbc
, debugstr_guid(riid
), ppv
);
836 static HRESULT WINAPI
RecycleBin_BindToStorage(IShellFolder2
*This
, LPCITEMIDLIST pidl
, LPBC pbc
, REFIID riid
, void **ppv
)
838 FIXME("(%p, %p, %p, %s, %p) - stub\n", This
, pidl
, pbc
, debugstr_guid(riid
), ppv
);
842 static HRESULT WINAPI
RecycleBin_CompareIDs(IShellFolder2
*iface
, LPARAM lParam
, LPCITEMIDLIST pidl1
, LPCITEMIDLIST pidl2
)
844 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
848 TRACE("(%p, %p, %p, %p)\n", This
, (void *)lParam
, pidl1
, pidl2
);
849 if (pidl1
->mkid
.cb
!= pidl2
->mkid
.cb
)
850 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, pidl1
->mkid
.cb
- pidl2
->mkid
.cb
);
851 /* Looks too complicated, but in optimized memcpy we might get
852 * a 32bit wide difference and would truncate it to 16 bit, so
853 * erroneously returning equality. */
854 ret
= memcmp(pidl1
->mkid
.abID
, pidl2
->mkid
.abID
, pidl1
->mkid
.cb
);
855 if (ret
< 0) ret
= -1;
856 if (ret
> 0) ret
= 1;
857 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, (unsigned short)ret
);
860 static HRESULT WINAPI
RecycleBin_CreateViewObject(IShellFolder2
*iface
, HWND hwndOwner
, REFIID riid
, void **ppv
)
862 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
864 TRACE("(%p, %p, %s, %p)\n", This
, hwndOwner
, debugstr_guid(riid
), ppv
);
867 if (IsEqualGUID(riid
, &IID_IShellView
))
872 ZeroMemory(&sfv
, sizeof(sfv
));
873 sfv
.cbSize
= sizeof(sfv
);
874 sfv
.pshf
= (IShellFolder
*)&This
->IShellFolder2_iface
;
876 TRACE("Calling SHCreateShellFolderViewEx\n");
877 ret
= SHCreateShellFolderViewEx(&sfv
, &tmp
);
878 TRACE("Result: %08x, output: %p\n", (unsigned int)ret
, tmp
);
883 FIXME("invalid/unsupported interface %s\n", debugstr_guid(riid
));
885 return E_NOINTERFACE
;
888 static HRESULT WINAPI
RecycleBin_GetAttributesOf(IShellFolder2
*This
, UINT cidl
, LPCITEMIDLIST
*apidl
,
891 TRACE("(%p, %d, {%p, ...}, {%lx})\n", This
, cidl
, apidl
[0], *rgfInOut
);
892 *rgfInOut
&= SFGAO_CANMOVE
|SFGAO_CANDELETE
|SFGAO_HASPROPSHEET
|SFGAO_FILESYSTEM
;
896 static HRESULT WINAPI
RecycleBin_GetUIObjectOf(IShellFolder2
*iface
, HWND hwndOwner
, UINT cidl
, LPCITEMIDLIST
*apidl
,
897 REFIID riid
, UINT
*rgfReserved
, void **ppv
)
899 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
901 if(IsEqualGUID(riid
, &IID_IContextMenu
) || IsEqualGUID(riid
, &IID_IContextMenu2
))
903 TRACE("(%p, %p, %d, {%p, ...}, %s, %p, %p)\n", This
, hwndOwner
, cidl
, apidl
[0], debugstr_guid(riid
), rgfReserved
, ppv
);
904 *ppv
= RecycleBinMenu_Constructor(cidl
,apidl
,&(This
->IShellFolder2_iface
));
907 FIXME("(%p, %p, %d, {%p, ...}, %s, %p, %p): stub!\n", iface
, hwndOwner
, cidl
, apidl
[0], debugstr_guid(riid
), rgfReserved
, ppv
);
912 static HRESULT WINAPI
RecycleBin_GetDisplayNameOf(IShellFolder2
*This
, LPCITEMIDLIST pidl
, SHGDNF uFlags
, STRRET
*pName
)
914 const WIN32_FIND_DATAW
*data
= get_trash_item_data( pidl
);
916 TRACE("(%p, %p, %lx, %p)\n", This
, pidl
, uFlags
, pName
);
917 pName
->uType
= STRRET_WSTR
;
918 return SHStrDupW(PathFindFileNameW(data
->cFileName
), &pName
->u
.pOleStr
);
921 static HRESULT WINAPI
RecycleBin_SetNameOf(IShellFolder2
*This
, HWND hwnd
, LPCITEMIDLIST pidl
, LPCOLESTR pszName
,
922 SHGDNF uFlags
, LPITEMIDLIST
*ppidlOut
)
925 return E_FAIL
; /* not supported */
928 static HRESULT WINAPI
RecycleBin_GetClassID(IPersistFolder2
*This
, CLSID
*pClassID
)
930 TRACE("(%p, %p)\n", This
, pClassID
);
931 if (This
== NULL
|| pClassID
== NULL
)
933 *pClassID
= CLSID_RecycleBin
;
937 static HRESULT WINAPI
RecycleBin_Initialize(IPersistFolder2
*iface
, LPCITEMIDLIST pidl
)
939 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
940 TRACE("(%p, %p)\n", This
, pidl
);
942 This
->pidl
= ILClone(pidl
);
943 if (This
->pidl
== NULL
)
944 return E_OUTOFMEMORY
;
948 static HRESULT WINAPI
RecycleBin_GetCurFolder(IPersistFolder2
*iface
, LPITEMIDLIST
*ppidl
)
950 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
952 *ppidl
= ILClone(This
->pidl
);
956 static HRESULT WINAPI
RecycleBin_GetDefaultSearchGUID(IShellFolder2
*iface
, GUID
*guid
)
958 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
959 TRACE("(%p)->(%p)\n", This
, guid
);
963 static HRESULT WINAPI
RecycleBin_EnumSearches(IShellFolder2
*iface
, IEnumExtraSearch
**ppEnum
)
970 static HRESULT WINAPI
RecycleBin_GetDefaultColumn(IShellFolder2
*iface
, DWORD reserved
, ULONG
*sort
, ULONG
*display
)
972 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
974 TRACE("(%p)->(%#lx, %p, %p)\n", This
, reserved
, sort
, display
);
979 static HRESULT WINAPI
RecycleBin_GetDefaultColumnState(IShellFolder2
*iface
, UINT iColumn
, SHCOLSTATEF
*pcsFlags
)
981 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
982 TRACE("(%p, %d, %p)\n", This
, iColumn
, pcsFlags
);
983 if (iColumn
>= COLUMNS_COUNT
)
985 *pcsFlags
= RecycleBinColumns
[iColumn
].pcsFlags
;
989 static HRESULT WINAPI
RecycleBin_GetDetailsEx(IShellFolder2
*iface
, LPCITEMIDLIST pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
)
995 static HRESULT WINAPI
RecycleBin_GetDetailsOf(IShellFolder2
*iface
, LPCITEMIDLIST pidl
, UINT iColumn
, LPSHELLDETAILS pDetails
)
997 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
998 const WIN32_FIND_DATAW
*data
;
999 WCHAR buffer
[MAX_PATH
];
1001 TRACE("(%p, %p, %d, %p)\n", This
, pidl
, iColumn
, pDetails
);
1002 if (iColumn
>= COLUMNS_COUNT
)
1005 if (!pidl
) return SHELL32_GetColumnDetails( RecycleBinColumns
, iColumn
, pDetails
);
1007 if (iColumn
== COLUMN_NAME
)
1008 return RecycleBin_GetDisplayNameOf(iface
, pidl
, SHGDN_NORMAL
, &pDetails
->str
);
1010 data
= get_trash_item_data( pidl
);
1013 case COLUMN_DATEDEL
:
1014 FormatDateTime(buffer
, MAX_PATH
, data
->ftLastAccessTime
);
1016 case COLUMN_DELFROM
:
1017 lstrcpyW(buffer
, data
->cFileName
);
1018 PathRemoveFileSpecW(buffer
);
1021 StrFormatKBSizeW(((LONGLONG
)data
->nFileSizeHigh
<<32)|data
->nFileSizeLow
, buffer
, MAX_PATH
);
1024 FormatDateTime(buffer
, MAX_PATH
, data
->ftLastWriteTime
);
1034 pDetails
->str
.uType
= STRRET_WSTR
;
1035 return SHStrDupW(buffer
, &pDetails
->str
.u
.pOleStr
);
1038 static HRESULT WINAPI
RecycleBin_MapColumnToSCID(IShellFolder2
*iface
, UINT iColumn
, SHCOLUMNID
*pscid
)
1040 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
1041 TRACE("(%p, %d, %p)\n", This
, iColumn
, pscid
);
1042 if (iColumn
>=COLUMNS_COUNT
)
1043 return E_INVALIDARG
;
1045 return shellfolder_map_column_to_scid(RecycleBinColumns
, iColumn
, pscid
);
1048 static const IShellFolder2Vtbl recycleBinVtbl
=
1051 RecycleBin_QueryInterface
,
1056 RecycleBin_ParseDisplayName
,
1057 RecycleBin_EnumObjects
,
1058 RecycleBin_BindToObject
,
1059 RecycleBin_BindToStorage
,
1060 RecycleBin_CompareIDs
,
1061 RecycleBin_CreateViewObject
,
1062 RecycleBin_GetAttributesOf
,
1063 RecycleBin_GetUIObjectOf
,
1064 RecycleBin_GetDisplayNameOf
,
1065 RecycleBin_SetNameOf
,
1068 RecycleBin_GetDefaultSearchGUID
,
1069 RecycleBin_EnumSearches
,
1070 RecycleBin_GetDefaultColumn
,
1071 RecycleBin_GetDefaultColumnState
,
1072 RecycleBin_GetDetailsEx
,
1073 RecycleBin_GetDetailsOf
,
1074 RecycleBin_MapColumnToSCID
1077 static HRESULT WINAPI
RecycleBin_IPersistFolder2_QueryInterface(IPersistFolder2
*iface
, REFIID riid
,
1080 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
1082 return IShellFolder2_QueryInterface(&This
->IShellFolder2_iface
, riid
, ppvObject
);
1085 static ULONG WINAPI
RecycleBin_IPersistFolder2_AddRef(IPersistFolder2
*iface
)
1087 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
1089 return IShellFolder2_AddRef(&This
->IShellFolder2_iface
);
1092 static ULONG WINAPI
RecycleBin_IPersistFolder2_Release(IPersistFolder2
*iface
)
1094 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
1096 return IShellFolder2_Release(&This
->IShellFolder2_iface
);
1099 static const IPersistFolder2Vtbl recycleBinPersistVtbl
=
1102 RecycleBin_IPersistFolder2_QueryInterface
,
1103 RecycleBin_IPersistFolder2_AddRef
,
1104 RecycleBin_IPersistFolder2_Release
,
1107 RecycleBin_GetClassID
,
1108 /* IPersistFolder */
1109 RecycleBin_Initialize
,
1110 /* IPersistFolder2 */
1111 RecycleBin_GetCurFolder
1114 static HRESULT WINAPI
RecycleBin_ISFHelper_QueryInterface(ISFHelper
*iface
, REFIID riid
,
1117 RecycleBin
*This
= impl_from_ISFHelper(iface
);
1119 return IShellFolder2_QueryInterface(&This
->IShellFolder2_iface
, riid
, ppvObject
);
1122 static ULONG WINAPI
RecycleBin_ISFHelper_AddRef(ISFHelper
*iface
)
1124 RecycleBin
*This
= impl_from_ISFHelper(iface
);
1126 return IShellFolder2_AddRef(&This
->IShellFolder2_iface
);
1129 static ULONG WINAPI
RecycleBin_ISFHelper_Release(ISFHelper
*iface
)
1131 RecycleBin
*This
= impl_from_ISFHelper(iface
);
1133 return IShellFolder2_Release(&This
->IShellFolder2_iface
);
1136 static HRESULT WINAPI
RecycleBin_GetUniqueName(ISFHelper
*iface
,LPWSTR lpName
,
1142 static HRESULT WINAPI
RecycleBin_AddFolder(ISFHelper
* iface
, HWND hwnd
,
1144 LPITEMIDLIST
* ppidlOut
)
1146 /*Adding folders doesn't make sense in the recycle bin*/
1150 static HRESULT
erase_items(HWND parent
,const LPCITEMIDLIST
* apidl
, UINT cidl
, BOOL confirm
)
1154 LPITEMIDLIST recyclebin
;
1158 WCHAR arg
[MAX_PATH
];
1167 const WIN32_FIND_DATAW
*data
= get_trash_item_data( apidl
[0] );
1168 lstrcpynW(arg
,data
->cFileName
,MAX_PATH
);
1169 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_ERASEITEM
, message
, ARRAY_SIZE(message
));
1174 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_ERASEMULTIPLE
, message
, ARRAY_SIZE(message
));
1175 swprintf(arg
, ARRAY_SIZE(arg
), L
"%u", cidl
);
1180 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_ERASE_CAPTION
, caption
, ARRAY_SIZE(caption
));
1181 if(ShellMessageBoxW(shell32_hInstance
,parent
,message
,caption
,
1182 MB_YESNO
|MB_ICONEXCLAMATION
,arg
)!=IDYES
)
1186 SHGetFolderLocation(parent
,CSIDL_BITBUCKET
,0,0,&recyclebin
);
1189 if(SUCCEEDED(erase_trash_item(apidl
[i
])))
1190 SHChangeNotify(SHCNE_DELETE
,SHCNF_IDLIST
,
1191 ILCombine(recyclebin
,apidl
[i
]),0);
1197 static HRESULT WINAPI
RecycleBin_DeleteItems(ISFHelper
* iface
, UINT cidl
,
1198 LPCITEMIDLIST
* apidl
)
1200 TRACE("(%p,%u,%p)\n",iface
,cidl
,apidl
);
1201 return erase_items(GetActiveWindow(),apidl
,cidl
,TRUE
);
1204 static HRESULT WINAPI
RecycleBin_CopyItems(ISFHelper
* iface
,
1205 IShellFolder
* pSFFrom
,
1206 UINT cidl
, LPCITEMIDLIST
* apidl
)
1211 static const ISFHelperVtbl sfhelperVtbl
=
1213 RecycleBin_ISFHelper_QueryInterface
,
1214 RecycleBin_ISFHelper_AddRef
,
1215 RecycleBin_ISFHelper_Release
,
1216 RecycleBin_GetUniqueName
,
1217 RecycleBin_AddFolder
,
1218 RecycleBin_DeleteItems
,
1219 RecycleBin_CopyItems
1222 HRESULT WINAPI
SHQueryRecycleBinA(LPCSTR pszRootPath
, LPSHQUERYRBINFO pSHQueryRBInfo
)
1224 WCHAR wszRootPath
[MAX_PATH
];
1225 MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, wszRootPath
, MAX_PATH
);
1226 return SHQueryRecycleBinW(wszRootPath
, pSHQueryRBInfo
);
1229 HRESULT WINAPI
SHQueryRecycleBinW(LPCWSTR pszRootPath
, LPSHQUERYRBINFO pSHQueryRBInfo
)
1231 LPITEMIDLIST
*apidl
;
1236 TRACE("(%s, %p)\n", debugstr_w(pszRootPath
), pSHQueryRBInfo
);
1238 hr
= enum_trash_items(&apidl
, &cidl
);
1241 pSHQueryRBInfo
->i64NumItems
= cidl
;
1242 pSHQueryRBInfo
->i64Size
= 0;
1245 const WIN32_FIND_DATAW
*data
= get_trash_item_data( apidl
[i
] );
1246 pSHQueryRBInfo
->i64Size
+= ((DWORDLONG
)data
->nFileSizeHigh
<< 32) + data
->nFileSizeLow
;
1253 HRESULT WINAPI
SHEmptyRecycleBinA(HWND hwnd
, LPCSTR pszRootPath
, DWORD dwFlags
)
1255 WCHAR wszRootPath
[MAX_PATH
];
1256 MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, wszRootPath
, MAX_PATH
);
1257 return SHEmptyRecycleBinW(hwnd
, wszRootPath
, dwFlags
);
1260 #define SHERB_NOCONFIRMATION 1
1261 #define SHERB_NOPROGRESSUI 2
1262 #define SHERB_NOSOUND 4
1264 HRESULT WINAPI
SHEmptyRecycleBinW(HWND hwnd
, LPCWSTR pszRootPath
, DWORD dwFlags
)
1266 LPITEMIDLIST
*apidl
;
1271 TRACE("(%p, %s, 0x%08lx)\n", hwnd
, debugstr_w(pszRootPath
) , dwFlags
);
1273 ret
= enum_trash_items(&apidl
, &cidl
);
1277 ret
= erase_items(hwnd
,(const LPCITEMIDLIST
*)apidl
,cidl
,!(dwFlags
& SHERB_NOCONFIRMATION
));
1284 /*************************************************************************
1285 * SHUpdateRecycleBinIcon [SHELL32.@]
1289 HRESULT WINAPI
SHUpdateRecycleBinIcon(void)