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
37 #include "shellfolder.h"
39 #include "knownfolders.h"
40 #include "wine/debug.h"
42 #include "shell32_main.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(recyclebin
);
48 static const shvheader RecycleBinColumns
[] =
50 {&FMTID_Storage
, PID_STG_NAME
, IDS_SHV_COLUMN1
, SHCOLSTATE_TYPE_STR
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 30},
51 {&FMTID_Displaced
, PID_DISPLACED_FROM
, IDS_SHV_COLUMN_DELFROM
, SHCOLSTATE_TYPE_STR
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 30},
52 {&FMTID_Displaced
, PID_DISPLACED_DATE
, IDS_SHV_COLUMN_DELDATE
, SHCOLSTATE_TYPE_DATE
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 20},
53 {&FMTID_Storage
, PID_STG_SIZE
, IDS_SHV_COLUMN2
, SHCOLSTATE_TYPE_INT
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_RIGHT
, 20},
54 {&FMTID_Storage
, PID_STG_STORAGETYPE
,IDS_SHV_COLUMN3
, SHCOLSTATE_TYPE_INT
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 20},
55 {&FMTID_Storage
, PID_STG_WRITETIME
, IDS_SHV_COLUMN4
, SHCOLSTATE_TYPE_DATE
|SHCOLSTATE_ONBYDEFAULT
, LVCFMT_LEFT
, 20},
56 /* {&FMTID_Storage, PID_STG_CREATETIME, "creation time", SHCOLSTATE_TYPE_DATE, LVCFMT_LEFT, 20}, */
57 /* {&FMTID_Storage, PID_STG_ATTRIBUTES, "attribs", SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 20}, */
61 #define COLUMN_DELFROM 1
62 #define COLUMN_DATEDEL 2
65 #define COLUMN_MTIME 5
67 #define COLUMNS_COUNT 6
69 static const WIN32_FIND_DATAW
*get_trash_item_data( LPCITEMIDLIST id
)
71 if (id
->mkid
.cb
< 2 + 1 + sizeof(WIN32_FIND_DATAW
) + 2) return NULL
;
72 if (id
->mkid
.abID
[0] != 0) return NULL
;
73 return (const WIN32_FIND_DATAW
*)(id
->mkid
.abID
+ 1);
76 static INIT_ONCE trash_dir_once
;
77 static WCHAR
*trash_dir
;
78 static WCHAR
*trash_info_dir
;
79 static ULONG random_seed
;
81 extern void CDECL
wine_get_host_version( const char **sysname
, const char **release
);
83 static BOOL
is_macos(void)
87 wine_get_host_version( &sysname
, NULL
);
88 return !strcmp( sysname
, "Darwin" );
91 static BOOL WINAPI
init_trash_dirs( INIT_ONCE
*once
, void *param
, void **context
)
93 const WCHAR
*home
= _wgetenv( L
"WINEHOMEDIR" );
94 WCHAR
*info
= NULL
, *files
= NULL
;
97 if (!home
) return TRUE
;
100 files
= malloc( (wcslen(home
) + ARRAY_SIZE(L
"\\.Trash")) * sizeof(WCHAR
) );
101 lstrcpyW( files
, home
);
102 lstrcatW( files
, L
"\\.Trash" );
103 files
[1] = '\\'; /* change \??\ to \\?\ */
107 const WCHAR
*data_home
= _wgetenv( L
"XDG_DATA_HOME" );
108 const WCHAR
*fmt
= L
"%s/.local/share/Trash";
111 if (data_home
&& data_home
[0] == '/')
114 fmt
= L
"\\??\\unix%s/Trash";
116 len
= lstrlenW(home
) + lstrlenW(fmt
) + 7;
117 files
= malloc( len
* sizeof(WCHAR
) );
118 swprintf( files
, len
, fmt
, home
);
119 files
[1] = '\\'; /* change \??\ to \\?\ */
120 for (p
= files
; *p
; p
++) if (*p
== '/') *p
= '\\';
121 CreateDirectoryW( files
, NULL
);
122 info
= malloc( len
* sizeof(WCHAR
) );
123 lstrcpyW( info
, files
);
124 lstrcatW( files
, L
"\\files" );
125 lstrcatW( info
, L
"\\info" );
126 if (!CreateDirectoryW( info
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
) goto done
;
127 trash_info_dir
= info
;
130 if (!CreateDirectoryW( files
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
) goto done
;
132 random_seed
= GetTickCount();
141 BOOL
is_trash_available(void)
143 InitOnceExecuteOnce( &trash_dir_once
, init_trash_dirs
, NULL
, NULL
);
144 return trash_dir
!= NULL
;
147 static void encode( const char *src
, char *dst
)
149 static const char escape_chars
[] = "^&`{}|[]'<>\\#%\"+";
150 static const char hexchars
[] = "0123456789ABCDEF";
154 if (*src
<= 0x20 || *src
>= 0x7f || strchr( escape_chars
, *src
))
157 *dst
++ = hexchars
[(unsigned char)*src
/ 16];
158 *dst
++ = hexchars
[(unsigned char)*src
% 16];
165 static char *decode( char *buf
)
167 const char *src
= buf
;
168 char *dst
= buf
; /* decode in place */
176 if (src
[1] >= '0' && src
[1] <= '9') v
= src
[1] - '0';
177 else if (src
[1] >= 'A' && src
[1] <= 'F') v
= src
[1] - 'A' + 10;
178 else if (src
[1] >= 'a' && src
[1] <= 'f') v
= src
[1] - 'a' + 10;
181 if (src
[2] >= '0' && src
[2] <= '9') v
+= src
[2] - '0';
182 else if (src
[2] >= 'A' && src
[2] <= 'F') v
+= src
[2] - 'A' + 10;
183 else if (src
[2] >= 'a' && src
[2] <= 'f') v
+= src
[2] - 'a' + 10;
187 if (src
[1] && src
[2]) src
+= 3;
190 else *dst
++ = *src
++;
196 static BOOL
write_trashinfo_file( HANDLE handle
, const WCHAR
*orig_path
)
201 if (!(path
= wine_get_unix_file_name( orig_path
))) return FALSE
;
202 GetLocalTime( &now
);
204 buffer
= malloc( strlen(path
) * 3 + 128 );
205 strcpy( buffer
, "[Trash Info]\nPath=" );
206 encode( path
, buffer
+ strlen(buffer
) );
207 sprintf( buffer
+ strlen(buffer
), "\nDeletionDate=%04u-%02u-%02uT%02u:%02u:%02u\n",
208 now
.wYear
, now
.wMonth
, now
.wDay
, now
.wHour
, now
.wMinute
, now
.wSecond
);
209 WriteFile( handle
, buffer
, strlen(buffer
), NULL
, NULL
);
215 static void read_trashinfo_file( HANDLE handle
, WIN32_FIND_DATAW
*data
)
219 char *buffer
, *next
, *p
;
222 size
= GetFileSize( handle
, NULL
);
223 if (!(buffer
= malloc( size
+ 1 ))) return;
224 if (!ReadFile( handle
, buffer
, size
, &size
, NULL
)) size
= 0;
230 if ((next
= strchr( next
, '\n' ))) *next
++ = 0;
231 while (*p
== ' ' || *p
== '\t') p
++;
232 if (!strncmp( p
, "[Trash Info]", 12 ))
237 if (header
&& !strncmp( p
, "Path=", 5 ))
240 if ((path
= wine_get_dos_file_name( decode( p
))))
242 lstrcpynW( data
->cFileName
, path
, MAX_PATH
);
247 /* show only the file name */
248 char *file
= strrchr( p
, '/' );
251 MultiByteToWideChar( CP_UNIXCP
, 0, file
, -1, data
->cFileName
, MAX_PATH
);
255 if (header
&& !strncmp( p
, "DeletionDate=", 13 ))
257 int year
, mon
, day
, hour
, min
, sec
;
258 if (sscanf( p
+ 13, "%d-%d-%dT%d:%d:%d", &year
, &mon
, &day
, &hour
, &min
, &sec
) == 6)
260 SYSTEMTIME time
= { year
, mon
, 0, day
, hour
, min
, sec
};
262 if (SystemTimeToFileTime( &time
, &ft
))
263 LocalFileTimeToFileTime( &ft
, &data
->ftLastAccessTime
);
271 BOOL
trash_file( const WCHAR
*path
)
273 WCHAR
*dest
= NULL
, *file
= PathFindFileNameW( path
);
277 InitOnceExecuteOnce( &trash_dir_once
, init_trash_dirs
, NULL
, NULL
);
278 if (!trash_dir
) return FALSE
;
280 len
= lstrlenW(trash_dir
) + lstrlenW(file
) + 11;
281 dest
= malloc( len
* sizeof(WCHAR
) );
286 ULONG infolen
= lstrlenW(trash_info_dir
) + lstrlenW(file
) + 21;
287 WCHAR
*info
= malloc( infolen
* sizeof(WCHAR
) );
289 swprintf( info
, infolen
, L
"%s\\%s.trashinfo", trash_info_dir
, file
);
290 for (i
= 0; i
< 1000; i
++)
292 handle
= CreateFileW( info
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, 0 );
293 if (handle
!= INVALID_HANDLE_VALUE
) break;
294 swprintf( info
, infolen
, L
"%s\\%s-%08x.trashinfo", trash_info_dir
, file
, RtlRandom( &random_seed
));
296 if (handle
!= INVALID_HANDLE_VALUE
)
298 if ((ret
= write_trashinfo_file( handle
, path
)))
300 ULONG namelen
= lstrlenW(info
) - lstrlenW(trash_info_dir
) - 10 /* .trashinfo */;
301 swprintf( dest
, len
, L
"%s%.*s", trash_dir
, namelen
, info
+ lstrlenW(trash_info_dir
) );
302 ret
= MoveFileW( path
, dest
);
304 CloseHandle( handle
);
305 if (!ret
) DeleteFileW( info
);
310 swprintf( dest
, len
, L
"%s\\%s", trash_dir
, file
);
311 for (i
= 0; i
< 1000; i
++)
313 ret
= MoveFileW( path
, dest
);
314 if (ret
|| GetLastError() != ERROR_ALREADY_EXISTS
) break;
315 swprintf( dest
, len
, L
"%s\\%s-%08x", trash_dir
, file
, RtlRandom( &random_seed
));
318 if (ret
) TRACE( "%s -> %s\n", debugstr_w(path
), debugstr_w(dest
) );
323 static BOOL
get_trash_item_info( const WCHAR
*filename
, WIN32_FIND_DATAW
*data
)
327 return !!wcscmp( filename
, L
".DS_Store" );
332 ULONG len
= lstrlenW(trash_info_dir
) + lstrlenW(filename
) + 12;
333 WCHAR
*info
= malloc( len
* sizeof(WCHAR
) );
335 swprintf( info
, len
, L
"%s\\%s.trashinfo", trash_info_dir
, filename
);
336 handle
= CreateFileW( info
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0 );
338 if (handle
== INVALID_HANDLE_VALUE
) return FALSE
;
339 read_trashinfo_file( handle
, data
);
340 CloseHandle( handle
);
345 static HRESULT
add_trash_item( WIN32_FIND_DATAW
*orig_data
, LPITEMIDLIST
**pidls
,
346 ULONG
*count
, ULONG
*size
)
349 WIN32_FIND_DATAW
*data
;
350 const WCHAR
*filename
= orig_data
->cFileName
;
351 ULONG len
= offsetof( ITEMIDLIST
, mkid
.abID
[1 + sizeof(*data
) + (lstrlenW(filename
)+1) * sizeof(WCHAR
)]);
353 if (!(pidl
= SHAlloc( len
+ 2 ))) return E_OUTOFMEMORY
;
355 pidl
->mkid
.abID
[0] = 0;
356 data
= (WIN32_FIND_DATAW
*)(pidl
->mkid
.abID
+ 1);
357 memcpy( data
, orig_data
, sizeof(*data
) );
358 lstrcpyW( (WCHAR
*)(data
+ 1), filename
);
359 *(USHORT
*)((char *)pidl
+ len
) = 0;
361 if (get_trash_item_info( filename
, data
))
367 *size
= max( *size
* 2, 32 );
368 new = realloc( *pidls
, *size
* sizeof(**pidls
) );
372 return E_OUTOFMEMORY
;
376 (*pidls
)[(*count
)++] = pidl
;
378 else SHFree( pidl
); /* ignore it */
382 static HRESULT
enum_trash_items( LPITEMIDLIST
**pidls
, int *ret_count
)
386 WIN32_FIND_DATAW data
;
387 LPITEMIDLIST
*ret
= NULL
;
388 ULONG count
= 0, size
= 0;
391 InitOnceExecuteOnce( &trash_dir_once
, init_trash_dirs
, NULL
, NULL
);
392 if (!trash_dir
) return E_FAIL
;
395 file
= malloc( (wcslen(trash_dir
) + ARRAY_SIZE(L
"\\*")) * 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
= malloc( 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
= malloc( 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
= malloc( 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, %Iu, %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%Ix, 0x%Ix) - 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, %lx, %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, ...}, {%lx})\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, %lx, %p)\n", This
, pidl
, uFlags
, pName
);
916 pName
->uType
= STRRET_WSTR
;
917 return SHStrDupW(PathFindFileNameW(data
->cFileName
), &pName
->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)->(%#lx, %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
.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 const ISFHelperVtbl sfhelperVtbl
=
1205 RecycleBin_ISFHelper_QueryInterface
,
1206 RecycleBin_ISFHelper_AddRef
,
1207 RecycleBin_ISFHelper_Release
,
1208 RecycleBin_GetUniqueName
,
1209 RecycleBin_AddFolder
,
1210 RecycleBin_DeleteItems
,
1213 HRESULT WINAPI
SHQueryRecycleBinA(LPCSTR pszRootPath
, LPSHQUERYRBINFO pSHQueryRBInfo
)
1215 WCHAR wszRootPath
[MAX_PATH
];
1216 MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, wszRootPath
, MAX_PATH
);
1217 return SHQueryRecycleBinW(wszRootPath
, pSHQueryRBInfo
);
1220 HRESULT WINAPI
SHQueryRecycleBinW(LPCWSTR pszRootPath
, LPSHQUERYRBINFO pSHQueryRBInfo
)
1222 LPITEMIDLIST
*apidl
;
1227 TRACE("(%s, %p)\n", debugstr_w(pszRootPath
), pSHQueryRBInfo
);
1229 hr
= enum_trash_items(&apidl
, &cidl
);
1232 pSHQueryRBInfo
->i64NumItems
= cidl
;
1233 pSHQueryRBInfo
->i64Size
= 0;
1236 const WIN32_FIND_DATAW
*data
= get_trash_item_data( apidl
[i
] );
1237 pSHQueryRBInfo
->i64Size
+= ((DWORDLONG
)data
->nFileSizeHigh
<< 32) + data
->nFileSizeLow
;
1244 HRESULT WINAPI
SHEmptyRecycleBinA(HWND hwnd
, LPCSTR pszRootPath
, DWORD dwFlags
)
1246 WCHAR wszRootPath
[MAX_PATH
];
1247 MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, wszRootPath
, MAX_PATH
);
1248 return SHEmptyRecycleBinW(hwnd
, wszRootPath
, dwFlags
);
1251 #define SHERB_NOCONFIRMATION 1
1252 #define SHERB_NOPROGRESSUI 2
1253 #define SHERB_NOSOUND 4
1255 HRESULT WINAPI
SHEmptyRecycleBinW(HWND hwnd
, LPCWSTR pszRootPath
, DWORD dwFlags
)
1257 LPITEMIDLIST
*apidl
;
1262 TRACE("(%p, %s, 0x%08lx)\n", hwnd
, debugstr_w(pszRootPath
) , dwFlags
);
1264 ret
= enum_trash_items(&apidl
, &cidl
);
1268 ret
= erase_items(hwnd
,(const LPCITEMIDLIST
*)apidl
,cidl
,!(dwFlags
& SHERB_NOCONFIRMATION
));
1275 /*************************************************************************
1276 * SHUpdateRecycleBinIcon [SHELL32.@]
1280 HRESULT WINAPI
SHUpdateRecycleBinIcon(void)