Как «безопасно» удалить папку в корзину

Я ищу способ поместить папку (с подпапками) в корзину с этими условиями:

  1. Это должно быть сделано молча — без всяких Пользовательский интерфейс Windows.

  2. Папка никогда не должен быть навсегда удаленным. Если это не может быть помещено в корзину, я ожидаю, что API потерпит неудачу.

  3. Получить процедуру обратного вызова для процесса, как CopyFileEx делает.

Пока я смог придумать это:

SHFILEOPSTRUCT sfo = {0};
sfo.wFunc = FO_DELETE;
sfo.pFrom = L"K:\\test del from USB\0";     //Folder on a USB stick
sfo.fFlags = FOF_ALLOWUNDO |
FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR |
FOF_WANTNUKEWARNING;

int res = SHFileOperation(&sfo);
BOOL bFullSuccess = res == 0 && !sfo.fAnyOperationsAborted;

Который ужасно терпит неудачу на папке, расположенной на флеш-накопителе USB, то есть это постоянно удаляется несмотря на FOF_ALLOWUNDO флаг.

Так ли я не делаю что-то правильно, или SHFileOperation API очень неправильно!

Любая идея, как сделать то, что я изложил выше?

РЕДАКТИРОВАТЬ: Я реализовал IRecycleBinManager::WillRecycle метод, как было предложено @Denis Anisimov, но, очевидно, это еще не все. Вот моя версия C ++. Первое определение интерфейса для метода, который мне нужен:

#if defined(__cplusplus) && !defined(CINTERFACE)

MIDL_INTERFACE("5869092D-8AF9-4A6C-AE84-1F03BE2246CC")
IRecycleBinManager : public IUnknown
{
public:

//function WillRecycle(const pszPath: LPCWSTR): HRESULT; stdcall;
virtual HRESULT STDMETHODCALLTYPE WillRecycle(
/* [string][in] */ __RPC__in LPCWSTR pszFile) = 0;
};

#endif

а потом сам звонок:

HRESULT hr;

CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);

// {4A04656D-52AA-49DE-8A09-CB178760E748}
const CLSID CLSID_RecycleBinManager = {0x4A04656D, 0x52AA, 0x49DE, {0x8A, 0x09, 0xCB, 0x17, 0x87, 0x60, 0xE7, 0x48}};

// {5869092D-8AF9-4A6C-AE84-1F03BE2246CC}
const IID IID_IRecycleBinManager = {0x5869092D, 0x8AF9, 0x4A6C, {0xAE, 0x84, 0x1F, 0x03, 0xBE, 0x22, 0x46, 0xCC}};

IRecycleBinManager* pIRBM = NULL;

hr = CoCreateInstance(CLSID_RecycleBinManager, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
IID_IRecycleBinManager, (void**) &pIRBM);
//  hr = SHCoCreateInstance(NULL, &CLSID_RecycleBinManager, NULL, IID_IRecycleBinManager, (void **)&pIRBM);
if (SUCCEEDED(hr))
{

hr = pIRBM->WillRecycle(L"C:\\test del");   //Crashes

pIRBM->Release();
}

К сожалению, я получаю эту ошибку на линии, где я должен позвонить WillRecycle метод:

Ошибка проверки времени выполнения # 0 — значение ESP не было сохранено должным образом
через вызов функции. Обычно это результат вызова
функция объявлена ​​с одним соглашением о вызовах с указателем на функцию
объявлено с другим соглашением о вызовах.

введите описание изображения здесь

9

Решение

Каждый диск имеет свою собственную корзину. А когда вы удаляете файл с диска С: он должен быть перемещен в корзину на диске С :. Когда вы удаляете файл с USB-накопителя, его следует переместить в корзину на USB-накопителе. Но когда на USB-накопителе нет корзины, файл удаляется без возможности восстановления. Это поведение Windows по умолчанию.

Флаг FOF_ALLOWUNDO — только РЕКОМЕНДАЦИЯ. MSDN говорит о флаге FOF_ALLOWUNDO:

Сохранять отмененную информацию, если возможно.

Поэтому нет никаких ошибок, когда Windows окончательно удаляет файлы, даже если вы используете флаг FOF_ALLOWUNDO.

Единственный способ, которым я вижу, — это проверить наличие корзины на диске с помощью функции SHQueryRecycleBin (как указал Алекс Фарбер в комментарии) перед операцией удаления. Но даже если Recycle Bin представляет, это не полная гарантия, что файл будет удален в корзину. Корзина имеет максимальный размер и может быть уже заполнена.

ОБНОВИТЬ

Вы можете использовать взломать. Вы можете эмулировать удаление файла в корзину с вашим собственным кодом, который создаст все необходимые системные записи в папке C: \ $ Recycle.Bin \ UserSID. Я протестировал этот метод на Windows 7, и он работает правильно. Это позволяет игнорировать ограничение максимального размера корзины. Также это позволяет перемещать файлы с USB в корзину на любом диске.

ОБНОВЛЕНИЕ 2

Для Vista + вы можете использовать недокументированный интерфейс IRecycleBinManager (русское описание можно найти на веб-странице http://rcrrad.com/2010/10/14/bitbucket-interfaces/):

const
IID_IEnumRecycleItems: TGUID = '{6E325F88-D12F-49E5-895B-8EC98630C021}';
IID_IRecycle: TGUID = '{0125E62F-8349-443A-854B-A55FB84CFA35}';
IID_IRecycleBin: TGUID = '{F964AD97-96F4-48AB-B444-E8588BC7C7B3}';
IID_IRecycleBinManager: TGUID = '{5869092D-8AF9-4A6C-AE84-1F03BE2246CC}';
CLSID_RecycleBinManager: TGUID = '{4A04656D-52AA-49DE-8A09-CB178760E748}';

type
{ Тип Корзины }
tagRECYCLEBIN_TYPE = (RBTYPE_VOLUME, RBTYPE_KNOWNFOLDER);
TRecycleBinType = tagRECYCLEBIN_TYPE;

{ Данные об удаленном элементе }
PDeletedItem = ^TDeletedItem;
tagDELETEDITEM = packed record
dwFileSizeLow: DWORD;
dwFileSizeHigh: DWORD;
ftDeletionTime: TFileTime;
szOriginalPath: array[0..Pred(MAX_PATH)] of WideChar;
szDisplacedPath: array[0..Pred(MAX_PATH)] of WideChar;
end;
TDeletedItem = tagDELETEDITEM;

{ Перечислитель элементов Корзины }
IEnumRecycleItems = interface(IUnknown)
['{6E325F88-D12F-49E5-895B-8EC98630C021}']
{ celt может быть равен только единице }
function Next(celt: ULONG; out rgelt: TDeletedItem;
var pceltFetched: ULONG): HRESULT; stdcall;
{ Not Implemented }
function Skip(celt: ULONG): HRESULT; stdcall;
function Reset: HRESULT; stdcall;
{ Not Implemented }
function Clone(out ppenum: IEnumRecycleItems): HRESULT; stdcall;
end;

{ "Интерфейс-переходник" между IRecycleBin и IRecycleBinManager }
IRecycle = interface(IUnknown)
['{0125E62F-8349-443A-854B-A55FB84CFA35}']
function Compact(): HRESULT; stdcall;
function GetFileData(const pszPath: LPCWSTR;
out lpData: TDeletedItem): HRESULT; stdcall;
function GetItemCount(out lpCount: TLargeInteger): HRESULT; stdcall;
function GetUsedSpace(out lpUsedSpace: TLargeInteger): HRESULT; stdcall;
function IsEmpty(): HRESULT; stdcall;
function PurgeAll(pfo: IFileOperation): HRESULT; stdcall;
function PurgeItems(const lpstrItems: LPCWSTR;
pfo: IFileOperation): HRESULT; stdcall;
function SuspendUpdating(fSuspend: BOOL): HRESULT; stdcall;
function RecycleItem(const lpstrItem: LPCWSTR; const dwAttrs: DWORD;
const iFileSize: TLargeInteger; out psi: IShellItem): HRESULT; stdcall;
function RestoreItems(const lpstrItems: LPCWSTR;
pfo: IFileOperation): HRESULT; stdcall;
function IsRecycled(const pszPath: LPCWSTR;
lpRecycled: PBOOL): HRESULT; stdcall;
function EnumItems(dwFlags: DWORD;
out EnumRecycleItems: IEnumRecycleItems): HRESULT; stdcall;
function WillRecycle(const pszPath: LPCWSTR): HRESULT; stdcall;
end;

{ Представляет определенную Корзину на конкретном диске }
IRecycleBin = interface(IUnknown)
['{F964AD97-96F4-48AB-B444-E8588BC7C7B3}']
function Compact(): HRESULT; stdcall;
function GetFileData(const pszPath: LPCWSTR;
out lpData: TDeletedItem): HRESULT; stdcall;
function GetItemCount(out lpCount: TLargeInteger): HRESULT; stdcall;
function GetUsedSpace(out lpUsedSpace: TLargeInteger): HRESULT; stdcall;
function IsEmpty(): HRESULT; stdcall;
function PurgeAll(pfo: IFileOperation): HRESULT; stdcall;
function PurgeItems(const lpstrItems: LPCWSTR;
pfo: IFileOperation): HRESULT; stdcall;
function SuspendUpdating(fSuspend: BOOL): HRESULT; stdcall;
function RecycleItem(const lpstrItem: LPCWSTR; const dwAttrs: DWORD;
const iFileSize: TLargeInteger; out psi: IShellItem): HRESULT; stdcall;
function RestoreItems(const lpstrItems: LPCWSTR;
pfo: IFileOperation): HRESULT; stdcall;
function IsRecycled(const pszPath: LPCWSTR;
lpRecycled: PBOOL): HRESULT; stdcall;
function EnumItems(dwFlags: DWORD;
out EnumRecycleItems: IEnumRecycleItems): HRESULT; stdcall;
function WillRecycle(const pszPath: LPCWSTR): HRESULT; stdcall;
function Initialize(const rbType: TRecycleBinType;
const pszID: LPCWSTR): HRESULT; stdcall;
function GetTypeID(out rbType: TRecycleBinType;
var pszID: LPWSTR): HRESULT; stdcall;
function GetIDList(out ppidl: PItemIDList): HRESULT; stdcall;
function GetLocation(pszPathBuffer: LPWSTR;
cchMax: DWORD): HRESULT; stdcall;
function GetMaxCapacityRange(out lpMin: TLargeInteger;
out lpMax: TLargeInteger): HRESULT; stdcall;
function GetMaxCapacity(out lpCapacity: TLargeInteger): HRESULT; stdcall;
function SetMaxCapacity(const lpCapacity: TLargeInteger): HRESULT; stdcall;
function GetPurgeOnDelete(out fNukeOnDelete: BOOL): HRESULT; stdcall;
function SetPurgeOnDelete(const fNukeOnDelete: BOOL): HRESULT; stdcall;
end;

{ Менеджер всех Корзин данной ОС }
IRecycleBinManager = interface(IUnknown)
['{5869092D-8AF9-4A6C-AE84-1F03BE2246CC}']
function Compact(): HRESULT; stdcall;
function GetFileData(const pszPath: LPCWSTR;
out lpData: TDeletedItem): HRESULT; stdcall;
function GetItemCount(out lpCount: TLargeInteger): HRESULT; stdcall;
function GetUsedSpace(out lpUsedSpace: TLargeInteger): HRESULT; stdcall;
function IsEmpty(): HRESULT; stdcall;
function PurgeAll(pfo: IFileOperation): HRESULT; stdcall;
function PurgeItems(const lpstrItems: LPCWSTR;
pfo: IFileOperation): HRESULT; stdcall;
function SuspendUpdating(fSuspend: BOOL): HRESULT; stdcall;
{ Not Implemented }
function RecycleItem(const lpstrItem: LPCWSTR; const dwAttrs: DWORD;
const iFileSize: TLargeInteger; out psi: IShellItem): HRESULT; stdcall;
function RestoreItems(const lpstrItems: LPCWSTR;
pfo: IFileOperation): HRESULT; stdcall;
function IsRecycled(const pszPath: LPCWSTR;
lpRecycled: PBOOL): HRESULT; stdcall;
function EnumItems(dwFlags: DWORD;
out EnumRecycleItems: IEnumRecycleItems): HRESULT; stdcall;
function WillRecycle(const pszPath: LPCWSTR): HRESULT; stdcall;
function DelayCompaction(const fDelay: BOOL): HRESULT; stdcall;
function GetRecycleBinCount(out iCount: Integer): HRESULT; stdcall;
function GetRecycleBinAt(const index: Integer; const iid: TGUID;
out ppv): HRESULT; stdcall;
function GetRecycleBin(const pszPath: LPCWSTR; const iid: TGUID;
out ppv): HRESULT; stdcall;
function Refresh(): HRESULT; stdcall;
end;

Вы можете проверить возможность удаления файла в корзину с помощью следующего кода:

function CanFileBeDeletedToRecycleBin(const AFileName: UnicodeString): Boolean;
var
RecycleBinManager: IRecycleBinManager;
begin
OleCheck(CoCreateInstance(CLSID_RecycleBinManager, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IRecycleBinManager, RecycleBinManager));
try
Result := RecycleBinManager.WillRecycle(PWideChar(AFileName)) = S_OK;
finally
RecycleBinManager := nil;
end;
end;

ОБНОВЛЕНИЕ 3

Также вы можете попробовать следующий код для удаления объекта в корзину:

function GetObjectSize(const AFileName: UnicodeString): Int64;
var
FindHandle: THandle;
FindData: TWin32FindDataW;
S: Int64;
begin
Result := 0;
FindHandle := FindFirstFileW(PWideChar(AFileName), FindData);
if FindHandle = INVALID_HANDLE_VALUE then
RaiseLastOSError;
try
repeat
if (FindData.cFileName <> UnicodeString('.')) and (FindData.cFileName <> '..') then
begin
Int64Rec(S).Lo := FindData.nFileSizeLow;
Int64Rec(S).Hi := FindData.nFileSizeHigh;
Result := Result + S;
if FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0 then
Result := Result + GetObjectSize(AFileName + '\*.*');
end;
until not FindNextFileW(FindHandle, FindData);
finally
FindClose(FindHandle);
end;
end;

procedure DeleteToRecycleBin(const AFileName: UnicodeString);
var
Attr: DWORD;
Size: Int64;
RecycleBinManager: IRecycleBinManager;
RecycleBin: IRecycleBin;
ShellItem: IShellItem;
begin
OleCheck(CoCreateInstance(CLSID_RecycleBinManager, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IRecycleBinManager, RecycleBinManager));
try
OleCheck(RecycleBinManager.GetRecycleBin(PWideChar(AFileName), IRecycleBin, RecycleBin));
try
Attr := GetFileAttributes(PWideChar(AFileName));
Size := GetObjectSize(AFileName);
OleCheck(RecycleBin.RecycleItem(PWideChar(AFileName), Attr, Size, ShellItem));
ShellItem := nil;
finally
RecycleBin := nil;
end;
finally
RecycleBinManager := nil;
end;
end;
10

Другие решения

Мне удалось найти решение для всех 3 моих первоначальных пунктов / запросов.

В двух словах, нужно использовать IFileOperation интерфейс и реализовать IFileOperationProgressSink в этом.

Вот полный пример кода и объяснение этому.

РЕДАКТИРОВАТЬ: Хорошо, это еще не все. Метод, который я разместил выше, не охватывает все основы 🙁

1

По вопросам рекламы [email protected]