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



댓글

이 블로그의 인기 게시물

콘탁 Kontakt, KOMPLETE 저렴한 구입 방법.

샤오신 패드 12.7 2025 사용기, 충전기에 대해.

대성 산업 탁상 시계 DS-173 메뉴얼