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
;
395 file
= heap_alloc( (lstrlenW(trash_dir
) + lstrlenW(L
"\\*") + 1) * sizeof(WCHAR
) );
396 lstrcpyW( file
, trash_dir
);
397 lstrcatW( file
, L
"\\*" );
398 handle
= FindFirstFileW( file
, &data
);
399 if (handle
!= INVALID_HANDLE_VALUE
)
403 if (!wcscmp( data
.cFileName
, L
"." )) continue;
404 if (!wcscmp( data
.cFileName
, L
".." )) continue;
405 hr
= add_trash_item( &data
, &ret
, &count
, &size
);
406 } while (hr
== S_OK
&& FindNextFileW( handle
, &data
));
410 if (FAILED(hr
)) while (count
) SHFree( &ret
[--count
] );
413 *pidls
= SHAlloc( count
* sizeof(**pidls
) );
414 memcpy( *pidls
, ret
, count
* sizeof(**pidls
) );
421 static void remove_trashinfo( const WCHAR
*filename
)
426 if (!trash_info_dir
) return;
427 len
= lstrlenW(trash_info_dir
) + lstrlenW(filename
) + 12;
428 info
= heap_alloc( len
* sizeof(WCHAR
) );
429 swprintf( info
, len
, L
"%s\\%s.trashinfo", trash_info_dir
, filename
);
434 static HRESULT
restore_trash_item( LPCITEMIDLIST pidl
)
436 const WIN32_FIND_DATAW
*data
= get_trash_item_data( pidl
);
437 WCHAR
*from
, *filename
= (WCHAR
*)(data
+ 1);
440 if (!wcschr( data
->cFileName
, '\\' ))
442 FIXME( "original name for %s not available\n", debugstr_w(data
->cFileName
) );
445 len
= lstrlenW(trash_dir
) + lstrlenW(filename
) + 2;
446 from
= heap_alloc( len
* sizeof(WCHAR
) );
447 swprintf( from
, len
, L
"%s\\%s", trash_dir
, filename
);
448 if (MoveFileW( from
, data
->cFileName
)) remove_trashinfo( filename
);
449 else WARN( "failed to restore %s to %s\n", debugstr_w(from
), debugstr_w(data
->cFileName
) );
454 static HRESULT
erase_trash_item( LPCITEMIDLIST pidl
)
456 const WIN32_FIND_DATAW
*data
= get_trash_item_data( pidl
);
457 WCHAR
*from
, *filename
= (WCHAR
*)(data
+ 1);
458 ULONG len
= lstrlenW(trash_dir
) + lstrlenW(filename
) + 2;
460 from
= heap_alloc( len
* sizeof(WCHAR
) );
461 swprintf( from
, len
, L
"%s\\%s", trash_dir
, filename
);
462 if (DeleteFileW( from
)) remove_trashinfo( filename
);
468 static HRESULT
FormatDateTime(LPWSTR buffer
, int size
, FILETIME ft
)
474 FileTimeToLocalFileTime(&ft
, &lft
);
475 FileTimeToSystemTime(&lft
, &time
);
477 ret
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, buffer
, size
);
478 if (ret
>0 && ret
<size
)
480 /* Append space + time without seconds */
482 GetTimeFormatW(LOCALE_USER_DEFAULT
, TIME_NOSECONDS
, &time
, NULL
, &buffer
[ret
], size
- ret
);
485 return (ret
!=0 ? E_FAIL
: S_OK
);
488 typedef struct tagRecycleBinMenu
490 IContextMenu2 IContextMenu2_iface
;
495 IShellFolder2
*folder
;
498 static const IContextMenu2Vtbl recycleBinMenuVtbl
;
500 static RecycleBinMenu
*impl_from_IContextMenu2(IContextMenu2
*iface
)
502 return CONTAINING_RECORD(iface
, RecycleBinMenu
, IContextMenu2_iface
);
505 static IContextMenu2
* RecycleBinMenu_Constructor(UINT cidl
, LPCITEMIDLIST
*apidl
, IShellFolder2
*folder
)
507 RecycleBinMenu
*This
= SHAlloc(sizeof(RecycleBinMenu
));
508 TRACE("(%u,%p)\n",cidl
,apidl
);
509 This
->IContextMenu2_iface
.lpVtbl
= &recycleBinMenuVtbl
;
511 This
->apidl
= _ILCopyaPidl(apidl
,cidl
);
512 IShellFolder2_AddRef(folder
);
513 This
->folder
= folder
;
515 return &This
->IContextMenu2_iface
;
518 static HRESULT WINAPI
RecycleBinMenu_QueryInterface(IContextMenu2
*iface
,
522 RecycleBinMenu
*This
= impl_from_IContextMenu2(iface
);
523 TRACE("(%p, %s, %p) - stub\n", This
, debugstr_guid(riid
), ppvObject
);
527 static ULONG WINAPI
RecycleBinMenu_AddRef(IContextMenu2
*iface
)
529 RecycleBinMenu
*This
= impl_from_IContextMenu2(iface
);
530 TRACE("(%p)\n", This
);
531 return InterlockedIncrement(&This
->refCount
);
535 static ULONG WINAPI
RecycleBinMenu_Release(IContextMenu2
*iface
)
537 RecycleBinMenu
*This
= impl_from_IContextMenu2(iface
);
539 TRACE("(%p)\n", This
);
540 result
= InterlockedDecrement(&This
->refCount
);
543 TRACE("Destroying object\n");
544 _ILFreeaPidl(This
->apidl
,This
->cidl
);
545 IShellFolder2_Release(This
->folder
);
551 static HRESULT WINAPI
RecycleBinMenu_QueryContextMenu(IContextMenu2
*iface
,
558 HMENU menures
= LoadMenuW(shell32_hInstance
,MAKEINTRESOURCEW(MENU_RECYCLEBIN
));
559 if(uFlags
& CMF_DEFAULTONLY
)
562 UINT idMax
= Shell_MergeMenus(hmenu
,GetSubMenu(menures
,0),indexMenu
,idCmdFirst
,idCmdLast
,MM_SUBMENUSHAVEIDS
);
563 TRACE("Added %d id(s)\n",idMax
-idCmdFirst
);
564 return MAKE_HRESULT(SEVERITY_SUCCESS
, FACILITY_NULL
, idMax
-idCmdFirst
+1);
568 static void DoErase(RecycleBinMenu
*This
)
571 IShellFolder2_QueryInterface(This
->folder
,&IID_ISFHelper
,(void**)&helper
);
573 ISFHelper_DeleteItems(helper
,This
->cidl
,(LPCITEMIDLIST
*)This
->apidl
);
576 static void DoRestore(RecycleBinMenu
*This
)
581 for(i
=0;i
<This
->cidl
;i
++)
583 const WIN32_FIND_DATAW
*data
= get_trash_item_data( This
->apidl
[i
] );
585 if(PathFileExistsW(data
->cFileName
))
587 PIDLIST_ABSOLUTE dest_pidl
= ILCreateFromPathW(data
->cFileName
);
590 if(_ILIsFolder(ILFindLastID(dest_pidl
)))
591 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_OVERWRITEFOLDER
, message
, ARRAY_SIZE(message
));
593 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_OVERWRITEFILE
, message
, ARRAY_SIZE(message
));
594 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_OVERWRITE_CAPTION
, caption
, ARRAY_SIZE(caption
));
596 if(ShellMessageBoxW(shell32_hInstance
,GetActiveWindow(),message
,
597 caption
,MB_YESNO
|MB_ICONEXCLAMATION
,
598 data
->cFileName
)!=IDYES
)
601 if(SUCCEEDED(restore_trash_item(This
->apidl
[i
])))
603 IPersistFolder2
*persist
;
604 LPITEMIDLIST root_pidl
;
605 PIDLIST_ABSOLUTE dest_pidl
= ILCreateFromPathW(data
->cFileName
);
606 BOOL is_folder
= _ILIsFolder(ILFindLastID(dest_pidl
));
607 IShellFolder2_QueryInterface(This
->folder
,&IID_IPersistFolder2
,
609 IPersistFolder2_GetCurFolder(persist
,&root_pidl
);
610 SHChangeNotify(is_folder
? SHCNE_RMDIR
: SHCNE_DELETE
,
611 SHCNF_IDLIST
,ILCombine(root_pidl
,This
->apidl
[i
]),0);
612 SHChangeNotify(is_folder
? SHCNE_MKDIR
: SHCNE_CREATE
,
613 SHCNF_IDLIST
,dest_pidl
,0);
620 static HRESULT WINAPI
RecycleBinMenu_InvokeCommand(IContextMenu2
*iface
,
621 LPCMINVOKECOMMANDINFO pici
)
623 RecycleBinMenu
*This
= impl_from_IContextMenu2(iface
);
624 LPCSTR verb
= pici
->lpVerb
;
625 if(IS_INTRESOURCE(verb
))
629 case IDM_RECYCLEBIN_ERASE
:
632 case IDM_RECYCLEBIN_RESTORE
:
642 static HRESULT WINAPI
RecycleBinMenu_GetCommandString(IContextMenu2
*iface
,
649 TRACE("(%p, %lu, %u, %p, %s, %u) - stub\n",iface
,idCmd
,uType
,pwReserved
,debugstr_a(pszName
),cchMax
);
653 static HRESULT WINAPI
RecycleBinMenu_HandleMenuMsg(IContextMenu2
*iface
,
654 UINT uMsg
, WPARAM wParam
,
657 TRACE("(%p, %u, 0x%lx, 0x%lx) - stub\n",iface
,uMsg
,wParam
,lParam
);
662 static const IContextMenu2Vtbl recycleBinMenuVtbl
=
664 RecycleBinMenu_QueryInterface
,
665 RecycleBinMenu_AddRef
,
666 RecycleBinMenu_Release
,
667 RecycleBinMenu_QueryContextMenu
,
668 RecycleBinMenu_InvokeCommand
,
669 RecycleBinMenu_GetCommandString
,
670 RecycleBinMenu_HandleMenuMsg
,
677 typedef struct tagRecycleBin
679 IShellFolder2 IShellFolder2_iface
;
680 IPersistFolder2 IPersistFolder2_iface
;
681 ISFHelper ISFHelper_iface
;
687 static const IShellFolder2Vtbl recycleBinVtbl
;
688 static const IPersistFolder2Vtbl recycleBinPersistVtbl
;
689 static const ISFHelperVtbl sfhelperVtbl
;
691 static inline RecycleBin
*impl_from_IShellFolder2(IShellFolder2
*iface
)
693 return CONTAINING_RECORD(iface
, RecycleBin
, IShellFolder2_iface
);
696 static RecycleBin
*impl_from_IPersistFolder2(IPersistFolder2
*iface
)
698 return CONTAINING_RECORD(iface
, RecycleBin
, IPersistFolder2_iface
);
701 static RecycleBin
*impl_from_ISFHelper(ISFHelper
*iface
)
703 return CONTAINING_RECORD(iface
, RecycleBin
, ISFHelper_iface
);
706 static void RecycleBin_Destructor(RecycleBin
*This
);
708 HRESULT WINAPI
RecycleBin_Constructor(IUnknown
*pUnkOuter
, REFIID riid
, LPVOID
*ppOutput
)
713 return CLASS_E_NOAGGREGATION
;
715 obj
= SHAlloc(sizeof(RecycleBin
));
717 return E_OUTOFMEMORY
;
718 ZeroMemory(obj
, sizeof(RecycleBin
));
719 obj
->IShellFolder2_iface
.lpVtbl
= &recycleBinVtbl
;
720 obj
->IPersistFolder2_iface
.lpVtbl
= &recycleBinPersistVtbl
;
721 obj
->ISFHelper_iface
.lpVtbl
= &sfhelperVtbl
;
722 if (FAILED(ret
= IPersistFolder2_QueryInterface(&obj
->IPersistFolder2_iface
, riid
, ppOutput
)))
724 RecycleBin_Destructor(obj
);
727 /* InterlockedIncrement(&objCount);*/
731 static void RecycleBin_Destructor(RecycleBin
*This
)
733 /* InterlockedDecrement(&objCount);*/
738 static HRESULT WINAPI
RecycleBin_QueryInterface(IShellFolder2
*iface
, REFIID riid
, void **ppvObject
)
740 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
741 TRACE("(%p, %s, %p)\n", This
, debugstr_guid(riid
), ppvObject
);
744 if (IsEqualGUID(riid
, &IID_IUnknown
) || IsEqualGUID(riid
, &IID_IShellFolder
)
745 || IsEqualGUID(riid
, &IID_IShellFolder2
))
746 *ppvObject
= &This
->IShellFolder2_iface
;
748 if (IsEqualGUID(riid
, &IID_IPersist
) || IsEqualGUID(riid
, &IID_IPersistFolder
)
749 || IsEqualGUID(riid
, &IID_IPersistFolder2
))
750 *ppvObject
= &This
->IPersistFolder2_iface
;
751 if (IsEqualGUID(riid
, &IID_ISFHelper
))
752 *ppvObject
= &This
->ISFHelper_iface
;
754 if (*ppvObject
!= NULL
)
756 IUnknown_AddRef((IUnknown
*)*ppvObject
);
759 WARN("no interface %s\n", debugstr_guid(riid
));
760 return E_NOINTERFACE
;
763 static ULONG WINAPI
RecycleBin_AddRef(IShellFolder2
*iface
)
765 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
766 TRACE("(%p)\n", This
);
767 return InterlockedIncrement(&This
->refCount
);
770 static ULONG WINAPI
RecycleBin_Release(IShellFolder2
*iface
)
772 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
775 TRACE("(%p)\n", This
);
776 result
= InterlockedDecrement(&This
->refCount
);
779 TRACE("Destroy object\n");
780 RecycleBin_Destructor(This
);
785 static HRESULT WINAPI
RecycleBin_ParseDisplayName(IShellFolder2
*This
, HWND hwnd
, LPBC pbc
,
786 LPOLESTR pszDisplayName
, ULONG
*pchEaten
, LPITEMIDLIST
*ppidl
,
787 ULONG
*pdwAttributes
)
793 static HRESULT WINAPI
RecycleBin_EnumObjects(IShellFolder2
*iface
, HWND hwnd
, SHCONTF grfFlags
, IEnumIDList
**ppenumIDList
)
795 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
796 IEnumIDListImpl
*list
;
798 HRESULT ret
= E_OUTOFMEMORY
;
802 TRACE("(%p, %p, %x, %p)\n", This
, hwnd
, grfFlags
, ppenumIDList
);
804 *ppenumIDList
= NULL
;
805 list
= IEnumIDList_Constructor();
807 return E_OUTOFMEMORY
;
809 if (grfFlags
& SHCONTF_NONFOLDERS
)
811 if (FAILED(ret
= enum_trash_items(&pidls
, &pidls_count
)))
813 for (i
=0; i
<pidls_count
; i
++)
814 if (!AddToEnumList(list
, pidls
[i
]))
818 *ppenumIDList
= &list
->IEnumIDList_iface
;
822 IEnumIDList_Release(&list
->IEnumIDList_iface
);
823 for (; i
<pidls_count
; i
++)
829 static HRESULT WINAPI
RecycleBin_BindToObject(IShellFolder2
*This
, LPCITEMIDLIST pidl
, LPBC pbc
, REFIID riid
, void **ppv
)
831 FIXME("(%p, %p, %p, %s, %p) - stub\n", This
, pidl
, pbc
, debugstr_guid(riid
), ppv
);
835 static HRESULT WINAPI
RecycleBin_BindToStorage(IShellFolder2
*This
, LPCITEMIDLIST pidl
, LPBC pbc
, REFIID riid
, void **ppv
)
837 FIXME("(%p, %p, %p, %s, %p) - stub\n", This
, pidl
, pbc
, debugstr_guid(riid
), ppv
);
841 static HRESULT WINAPI
RecycleBin_CompareIDs(IShellFolder2
*iface
, LPARAM lParam
, LPCITEMIDLIST pidl1
, LPCITEMIDLIST pidl2
)
843 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
847 TRACE("(%p, %p, %p, %p)\n", This
, (void *)lParam
, pidl1
, pidl2
);
848 if (pidl1
->mkid
.cb
!= pidl2
->mkid
.cb
)
849 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, pidl1
->mkid
.cb
- pidl2
->mkid
.cb
);
850 /* Looks too complicated, but in optimized memcpy we might get
851 * a 32bit wide difference and would truncate it to 16 bit, so
852 * erroneously returning equality. */
853 ret
= memcmp(pidl1
->mkid
.abID
, pidl2
->mkid
.abID
, pidl1
->mkid
.cb
);
854 if (ret
< 0) ret
= -1;
855 if (ret
> 0) ret
= 1;
856 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, (unsigned short)ret
);
859 static HRESULT WINAPI
RecycleBin_CreateViewObject(IShellFolder2
*iface
, HWND hwndOwner
, REFIID riid
, void **ppv
)
861 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
863 TRACE("(%p, %p, %s, %p)\n", This
, hwndOwner
, debugstr_guid(riid
), ppv
);
866 if (IsEqualGUID(riid
, &IID_IShellView
))
871 ZeroMemory(&sfv
, sizeof(sfv
));
872 sfv
.cbSize
= sizeof(sfv
);
873 sfv
.pshf
= (IShellFolder
*)&This
->IShellFolder2_iface
;
875 TRACE("Calling SHCreateShellFolderViewEx\n");
876 ret
= SHCreateShellFolderViewEx(&sfv
, &tmp
);
877 TRACE("Result: %08x, output: %p\n", (unsigned int)ret
, tmp
);
882 FIXME("invalid/unsupported interface %s\n", debugstr_guid(riid
));
884 return E_NOINTERFACE
;
887 static HRESULT WINAPI
RecycleBin_GetAttributesOf(IShellFolder2
*This
, UINT cidl
, LPCITEMIDLIST
*apidl
,
890 TRACE("(%p, %d, {%p, ...}, {%x})\n", This
, cidl
, apidl
[0], *rgfInOut
);
891 *rgfInOut
&= SFGAO_CANMOVE
|SFGAO_CANDELETE
|SFGAO_HASPROPSHEET
|SFGAO_FILESYSTEM
;
895 static HRESULT WINAPI
RecycleBin_GetUIObjectOf(IShellFolder2
*iface
, HWND hwndOwner
, UINT cidl
, LPCITEMIDLIST
*apidl
,
896 REFIID riid
, UINT
*rgfReserved
, void **ppv
)
898 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
900 if(IsEqualGUID(riid
, &IID_IContextMenu
) || IsEqualGUID(riid
, &IID_IContextMenu2
))
902 TRACE("(%p, %p, %d, {%p, ...}, %s, %p, %p)\n", This
, hwndOwner
, cidl
, apidl
[0], debugstr_guid(riid
), rgfReserved
, ppv
);
903 *ppv
= RecycleBinMenu_Constructor(cidl
,apidl
,&(This
->IShellFolder2_iface
));
906 FIXME("(%p, %p, %d, {%p, ...}, %s, %p, %p): stub!\n", iface
, hwndOwner
, cidl
, apidl
[0], debugstr_guid(riid
), rgfReserved
, ppv
);
911 static HRESULT WINAPI
RecycleBin_GetDisplayNameOf(IShellFolder2
*This
, LPCITEMIDLIST pidl
, SHGDNF uFlags
, STRRET
*pName
)
913 const WIN32_FIND_DATAW
*data
= get_trash_item_data( pidl
);
915 TRACE("(%p, %p, %x, %p)\n", This
, pidl
, uFlags
, pName
);
916 pName
->uType
= STRRET_WSTR
;
917 return SHStrDupW(PathFindFileNameW(data
->cFileName
), &pName
->u
.pOleStr
);
920 static HRESULT WINAPI
RecycleBin_SetNameOf(IShellFolder2
*This
, HWND hwnd
, LPCITEMIDLIST pidl
, LPCOLESTR pszName
,
921 SHGDNF uFlags
, LPITEMIDLIST
*ppidlOut
)
924 return E_FAIL
; /* not supported */
927 static HRESULT WINAPI
RecycleBin_GetClassID(IPersistFolder2
*This
, CLSID
*pClassID
)
929 TRACE("(%p, %p)\n", This
, pClassID
);
930 if (This
== NULL
|| pClassID
== NULL
)
932 *pClassID
= CLSID_RecycleBin
;
936 static HRESULT WINAPI
RecycleBin_Initialize(IPersistFolder2
*iface
, LPCITEMIDLIST pidl
)
938 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
939 TRACE("(%p, %p)\n", This
, pidl
);
941 This
->pidl
= ILClone(pidl
);
942 if (This
->pidl
== NULL
)
943 return E_OUTOFMEMORY
;
947 static HRESULT WINAPI
RecycleBin_GetCurFolder(IPersistFolder2
*iface
, LPITEMIDLIST
*ppidl
)
949 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
951 *ppidl
= ILClone(This
->pidl
);
955 static HRESULT WINAPI
RecycleBin_GetDefaultSearchGUID(IShellFolder2
*iface
, GUID
*guid
)
957 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
958 TRACE("(%p)->(%p)\n", This
, guid
);
962 static HRESULT WINAPI
RecycleBin_EnumSearches(IShellFolder2
*iface
, IEnumExtraSearch
**ppEnum
)
969 static HRESULT WINAPI
RecycleBin_GetDefaultColumn(IShellFolder2
*iface
, DWORD reserved
, ULONG
*sort
, ULONG
*display
)
971 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
973 TRACE("(%p)->(%#x, %p, %p)\n", This
, reserved
, sort
, display
);
978 static HRESULT WINAPI
RecycleBin_GetDefaultColumnState(IShellFolder2
*iface
, UINT iColumn
, SHCOLSTATEF
*pcsFlags
)
980 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
981 TRACE("(%p, %d, %p)\n", This
, iColumn
, pcsFlags
);
982 if (iColumn
>= COLUMNS_COUNT
)
984 *pcsFlags
= RecycleBinColumns
[iColumn
].pcsFlags
;
988 static HRESULT WINAPI
RecycleBin_GetDetailsEx(IShellFolder2
*iface
, LPCITEMIDLIST pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
)
994 static HRESULT WINAPI
RecycleBin_GetDetailsOf(IShellFolder2
*iface
, LPCITEMIDLIST pidl
, UINT iColumn
, LPSHELLDETAILS pDetails
)
996 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
997 const WIN32_FIND_DATAW
*data
;
998 WCHAR buffer
[MAX_PATH
];
1000 TRACE("(%p, %p, %d, %p)\n", This
, pidl
, iColumn
, pDetails
);
1001 if (iColumn
>= COLUMNS_COUNT
)
1004 if (!pidl
) return SHELL32_GetColumnDetails( RecycleBinColumns
, iColumn
, pDetails
);
1006 if (iColumn
== COLUMN_NAME
)
1007 return RecycleBin_GetDisplayNameOf(iface
, pidl
, SHGDN_NORMAL
, &pDetails
->str
);
1009 data
= get_trash_item_data( pidl
);
1012 case COLUMN_DATEDEL
:
1013 FormatDateTime(buffer
, MAX_PATH
, data
->ftLastAccessTime
);
1015 case COLUMN_DELFROM
:
1016 lstrcpyW(buffer
, data
->cFileName
);
1017 PathRemoveFileSpecW(buffer
);
1020 StrFormatKBSizeW(((LONGLONG
)data
->nFileSizeHigh
<<32)|data
->nFileSizeLow
, buffer
, MAX_PATH
);
1023 FormatDateTime(buffer
, MAX_PATH
, data
->ftLastWriteTime
);
1033 pDetails
->str
.uType
= STRRET_WSTR
;
1034 return SHStrDupW(buffer
, &pDetails
->str
.u
.pOleStr
);
1037 static HRESULT WINAPI
RecycleBin_MapColumnToSCID(IShellFolder2
*iface
, UINT iColumn
, SHCOLUMNID
*pscid
)
1039 RecycleBin
*This
= impl_from_IShellFolder2(iface
);
1040 TRACE("(%p, %d, %p)\n", This
, iColumn
, pscid
);
1041 if (iColumn
>=COLUMNS_COUNT
)
1042 return E_INVALIDARG
;
1044 return shellfolder_map_column_to_scid(RecycleBinColumns
, iColumn
, pscid
);
1047 static const IShellFolder2Vtbl recycleBinVtbl
=
1050 RecycleBin_QueryInterface
,
1055 RecycleBin_ParseDisplayName
,
1056 RecycleBin_EnumObjects
,
1057 RecycleBin_BindToObject
,
1058 RecycleBin_BindToStorage
,
1059 RecycleBin_CompareIDs
,
1060 RecycleBin_CreateViewObject
,
1061 RecycleBin_GetAttributesOf
,
1062 RecycleBin_GetUIObjectOf
,
1063 RecycleBin_GetDisplayNameOf
,
1064 RecycleBin_SetNameOf
,
1067 RecycleBin_GetDefaultSearchGUID
,
1068 RecycleBin_EnumSearches
,
1069 RecycleBin_GetDefaultColumn
,
1070 RecycleBin_GetDefaultColumnState
,
1071 RecycleBin_GetDetailsEx
,
1072 RecycleBin_GetDetailsOf
,
1073 RecycleBin_MapColumnToSCID
1076 static HRESULT WINAPI
RecycleBin_IPersistFolder2_QueryInterface(IPersistFolder2
*iface
, REFIID riid
,
1079 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
1081 return IShellFolder2_QueryInterface(&This
->IShellFolder2_iface
, riid
, ppvObject
);
1084 static ULONG WINAPI
RecycleBin_IPersistFolder2_AddRef(IPersistFolder2
*iface
)
1086 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
1088 return IShellFolder2_AddRef(&This
->IShellFolder2_iface
);
1091 static ULONG WINAPI
RecycleBin_IPersistFolder2_Release(IPersistFolder2
*iface
)
1093 RecycleBin
*This
= impl_from_IPersistFolder2(iface
);
1095 return IShellFolder2_Release(&This
->IShellFolder2_iface
);
1098 static const IPersistFolder2Vtbl recycleBinPersistVtbl
=
1101 RecycleBin_IPersistFolder2_QueryInterface
,
1102 RecycleBin_IPersistFolder2_AddRef
,
1103 RecycleBin_IPersistFolder2_Release
,
1106 RecycleBin_GetClassID
,
1107 /* IPersistFolder */
1108 RecycleBin_Initialize
,
1109 /* IPersistFolder2 */
1110 RecycleBin_GetCurFolder
1113 static HRESULT WINAPI
RecycleBin_ISFHelper_QueryInterface(ISFHelper
*iface
, REFIID riid
,
1116 RecycleBin
*This
= impl_from_ISFHelper(iface
);
1118 return IShellFolder2_QueryInterface(&This
->IShellFolder2_iface
, riid
, ppvObject
);
1121 static ULONG WINAPI
RecycleBin_ISFHelper_AddRef(ISFHelper
*iface
)
1123 RecycleBin
*This
= impl_from_ISFHelper(iface
);
1125 return IShellFolder2_AddRef(&This
->IShellFolder2_iface
);
1128 static ULONG WINAPI
RecycleBin_ISFHelper_Release(ISFHelper
*iface
)
1130 RecycleBin
*This
= impl_from_ISFHelper(iface
);
1132 return IShellFolder2_Release(&This
->IShellFolder2_iface
);
1135 static HRESULT WINAPI
RecycleBin_GetUniqueName(ISFHelper
*iface
,LPWSTR lpName
,
1141 static HRESULT WINAPI
RecycleBin_AddFolder(ISFHelper
* iface
, HWND hwnd
,
1143 LPITEMIDLIST
* ppidlOut
)
1145 /*Adding folders doesn't make sense in the recycle bin*/
1149 static HRESULT
erase_items(HWND parent
,const LPCITEMIDLIST
* apidl
, UINT cidl
, BOOL confirm
)
1153 LPITEMIDLIST recyclebin
;
1157 WCHAR arg
[MAX_PATH
];
1166 const WIN32_FIND_DATAW
*data
= get_trash_item_data( apidl
[0] );
1167 lstrcpynW(arg
,data
->cFileName
,MAX_PATH
);
1168 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_ERASEITEM
, message
, ARRAY_SIZE(message
));
1173 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_ERASEMULTIPLE
, message
, ARRAY_SIZE(message
));
1174 swprintf(arg
, ARRAY_SIZE(arg
), L
"%u", cidl
);
1179 LoadStringW(shell32_hInstance
, IDS_RECYCLEBIN_ERASE_CAPTION
, caption
, ARRAY_SIZE(caption
));
1180 if(ShellMessageBoxW(shell32_hInstance
,parent
,message
,caption
,
1181 MB_YESNO
|MB_ICONEXCLAMATION
,arg
)!=IDYES
)
1185 SHGetFolderLocation(parent
,CSIDL_BITBUCKET
,0,0,&recyclebin
);
1188 if(SUCCEEDED(erase_trash_item(apidl
[i
])))
1189 SHChangeNotify(SHCNE_DELETE
,SHCNF_IDLIST
,
1190 ILCombine(recyclebin
,apidl
[i
]),0);
1196 static HRESULT WINAPI
RecycleBin_DeleteItems(ISFHelper
* iface
, UINT cidl
,
1197 LPCITEMIDLIST
* apidl
)
1199 TRACE("(%p,%u,%p)\n",iface
,cidl
,apidl
);
1200 return erase_items(GetActiveWindow(),apidl
,cidl
,TRUE
);
1203 static HRESULT WINAPI
RecycleBin_CopyItems(ISFHelper
* iface
,
1204 IShellFolder
* pSFFrom
,
1205 UINT cidl
, LPCITEMIDLIST
* apidl
)
1210 static const ISFHelperVtbl sfhelperVtbl
=
1212 RecycleBin_ISFHelper_QueryInterface
,
1213 RecycleBin_ISFHelper_AddRef
,
1214 RecycleBin_ISFHelper_Release
,
1215 RecycleBin_GetUniqueName
,
1216 RecycleBin_AddFolder
,
1217 RecycleBin_DeleteItems
,
1218 RecycleBin_CopyItems
1221 HRESULT WINAPI
SHQueryRecycleBinA(LPCSTR pszRootPath
, LPSHQUERYRBINFO pSHQueryRBInfo
)
1223 WCHAR wszRootPath
[MAX_PATH
];
1224 MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, wszRootPath
, MAX_PATH
);
1225 return SHQueryRecycleBinW(wszRootPath
, pSHQueryRBInfo
);
1228 HRESULT WINAPI
SHQueryRecycleBinW(LPCWSTR pszRootPath
, LPSHQUERYRBINFO pSHQueryRBInfo
)
1230 LPITEMIDLIST
*apidl
;
1235 TRACE("(%s, %p)\n", debugstr_w(pszRootPath
), pSHQueryRBInfo
);
1237 hr
= enum_trash_items(&apidl
, &cidl
);
1240 pSHQueryRBInfo
->i64NumItems
= cidl
;
1241 pSHQueryRBInfo
->i64Size
= 0;
1244 const WIN32_FIND_DATAW
*data
= get_trash_item_data( apidl
[i
] );
1245 pSHQueryRBInfo
->i64Size
+= ((DWORDLONG
)data
->nFileSizeHigh
<< 32) + data
->nFileSizeLow
;
1252 HRESULT WINAPI
SHEmptyRecycleBinA(HWND hwnd
, LPCSTR pszRootPath
, DWORD dwFlags
)
1254 WCHAR wszRootPath
[MAX_PATH
];
1255 MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, wszRootPath
, MAX_PATH
);
1256 return SHEmptyRecycleBinW(hwnd
, wszRootPath
, dwFlags
);
1259 #define SHERB_NOCONFIRMATION 1
1260 #define SHERB_NOPROGRESSUI 2
1261 #define SHERB_NOSOUND 4
1263 HRESULT WINAPI
SHEmptyRecycleBinW(HWND hwnd
, LPCWSTR pszRootPath
, DWORD dwFlags
)
1265 LPITEMIDLIST
*apidl
;
1270 TRACE("(%p, %s, 0x%08x)\n", hwnd
, debugstr_w(pszRootPath
) , dwFlags
);
1272 ret
= enum_trash_items(&apidl
, &cidl
);
1276 ret
= erase_items(hwnd
,(const LPCITEMIDLIST
*)apidl
,cidl
,!(dwFlags
& SHERB_NOCONFIRMATION
));
1283 /*************************************************************************
1284 * SHUpdateRecycleBinIcon [SHELL32.@]
1288 HRESULT WINAPI
SHUpdateRecycleBinIcon(void)