win32 shell 파일 복원 로직.
윈도우 shellapi.h 에서 지운 파일을 복원하는 API가 없습니다.
구현방법은 다음과 같습니다.
윈도우 휴지통의 모든 파일을 검색해 최근 삭제한 파일, 폴더를 복원합니다.
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <propkey.h>
#include <propvarutil.h>
#include <iostream>
#include <string>
#include <wrl/client.h>
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "propsys.lib")
static HRESULT GetFileTimeProp(IShellItem2* item2, const PROPERTYKEY& key, FILETIME& outFt)
{
outFt = {};
PROPVARIANT pv; PropVariantInit(&pv);
HRESULT hr = item2->GetProperty(key, &pv);
if (FAILED(hr)) { PropVariantClear(&pv); return hr; }
if (pv.vt == VT_FILETIME) { outFt = pv.filetime; PropVariantClear(&pv); return S_OK; }
if (pv.vt == VT_DATE)
{
SYSTEMTIME st{};
if (VariantTimeToSystemTime(pv.date, &st) && SystemTimeToFileTime(&st, &outFt))
{
PropVariantClear(&pv); return S_OK;
}
}
PropVariantClear(&pv);
return E_FAIL;
}
static HRESULT InvokeUndelete(IShellItem* item, HWND hwndOwner)
{
ComPtr<IContextMenu> cm;
HRESULT hr = item->BindToHandler(nullptr, BHID_SFUIObject, IID_PPV_ARGS(&cm));
if (FAILED(hr)) return hr;
HMENU h = CreatePopupMenu();
if (!h) return HRESULT_FROM_WIN32(GetLastError());
cm->QueryContextMenu(h, 0, 1, 0x7FFF, CMF_NORMAL);
CMINVOKECOMMANDINFOEX info{};
info.cbSize = sizeof(info);
info.fMask = CMIC_MASK_UNICODE;
info.hwnd = hwndOwner;
info.lpVerb = "undelete";
info.lpVerbW = L"undelete";
info.nShow = SW_SHOWNORMAL;
hr = cm->InvokeCommand(reinterpret_cast<LPCMINVOKECOMMANDINFO>(&info));
DestroyMenu(h);
return hr;
}
HRESULT RestoreMostRecentFromRecycleBin(HWND hwndOwner /*=nullptr*/)
{
// STA 권장
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) return hr;
auto uninit = uninit = (hr == S_OK || hr == S_FALSE);
HRESULT result = E_FAIL;
ComPtr<IShellItem> rb;
hr = SHGetKnownFolderItem(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS(&rb));
if (FAILED(hr)) { result = hr; if (uninit) CoUninitialize(); return result; }
ComPtr<IEnumShellItems> e;
hr = rb->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&e));
if (FAILED(hr)) { result = hr; if (uninit) CoUninitialize(); return result; }
PROPERTYKEY keyDateDeleted{};
hr = PSGetPropertyKeyFromName(L"System.Recycle.DateDeleted", &keyDateDeleted);
if (FAILED(hr)) { result = hr; if (uninit) CoUninitialize(); return result; }
ComPtr<IShellItem> best;
FILETIME bestFt{};
bool haveBest = false;
while (true)
{
ComPtr<IShellItem> item;
ULONG fetched = 0;
HRESULT hrNext = e->Next(1, &item, &fetched);
if (hrNext != S_OK) break;
ComPtr<IShellItem2> item2;
if (FAILED(item.As(&item2)) || !item2) continue;
FILETIME ft{};
if (FAILED(GetFileTimeProp(item2.Get(), keyDateDeleted, ft))) continue;
if (!haveBest || CompareFileTime(&ft, &bestFt) > 0)
{
best = item;
bestFt = ft;
haveBest = true;
}
}
if (!best) { result = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); goto done; }
result = InvokeUndelete(best.Get(), hwndOwner);
done:
if (uninit) CoUninitialize();
return result;
}
- 참고.
https://devblogs.microsoft.com/oldnewthing/20110901-00/?p=9753
https://stackoverflow.com/questions/78010977/how-to-programatically-restore-a-recycle-bin-item
https://microsoft.public.platformsdk.shell.narkive.com/eWwGSZ1d/undoing-a-delete-after-shfileoperation-with-the-fof-allowundo-flag
댓글
댓글 쓰기