Get deleted item from recyclebin

Flaviu_ 971 Reputation points
2024-06-11T13:21:13.9233333+00:00

Task: I need to get deleted item having SHITEMID data.

SHITEMID is defined as:

typedef struct _ITEMIDLIST
{
	SHITEMID mkid;
}ITEMIDLIST;

defined in C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um\shtypes.h

I have the following code to get mkid:

First, get display name from LPITEMIDLIST:

std::wstring CMyClass::GetDisplayName(IShellFolder *pShellFolder, const LPITEMIDLIST pidl)
{
	if (!pShellFolder)
		return {};
	STRRET str{};
	if (SUCCEEDED(pShellFolder->GetDisplayNameOf(reinterpret_cast<PCUITEMID_CHILD>(pidl), SHGDN_NORMAL, &str)))
	{
		wchar_t szDisplayName[MAX_PATH];
		switch (str.uType)
		{
		case STRRET_WSTR:
			wcscpy_s(szDisplayName, MAX_PATH, str.pOleStr);
			CoTaskMemFree(str.pOleStr);
			break;
		case STRRET_OFFSET:
			wcscpy_s(szDisplayName, MAX_PATH, reinterpret_cast<wchar_t *>(reinterpret_cast<BYTE*>(pidl) + str.uOffset));
			break;
		case STRRET_CSTR:
			MultiByteToWideChar(CP_ACP, 0, str.cStr, -1, szDisplayName, MAX_PATH);
			break;
		default:
			break;
		}
		return std::wstring(szDisplayName);
	}
	return {};
}

and the code that search in RecycleBin the item:

bool CMyClass::FindFileInRecycleBin(const LPITEMIDLIST& pidl)
{
	IShellFolder *pDesktopFolder = nullptr;
	HRESULT hr = SHGetDesktopFolder(&pDesktopFolder);
	if (FAILED(hr))
	{
		return false;
	}
	PIDLIST_ABSOLUTE pidlRecycleBin = nullptr;
	hr = SHGetSpecialFolderLocation(nullptr, CSIDL_BITBUCKET, &pidlRecycleBin);
	if (FAILED(hr))
	{
		pDesktopFolder->Release();
		return false;
	}
	IShellFolder *pRecycleBinFolder = nullptr;
	hr = pDesktopFolder->BindToObject(pidlRecycleBin, nullptr, IID_IShellFolder,
		reinterpret_cast<void **>(&pRecycleBinFolder));
	if (FAILED(hr))
	{
		CoTaskMemFree(pidlRecycleBin);
		pDesktopFolder->Release();
		return false;
	}
	IEnumIDList *pEnumIDList = nullptr;
	hr = pRecycleBinFolder->EnumObjects(nullptr, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIDList);
	if (FAILED(hr))
	{
		pRecycleBinFolder->Release();
		CoTaskMemFree(pidlRecycleBin);
		pDesktopFolder->Release();
		return false;
	}
	std::wstring displayNameFile{};
	PITEMID_CHILD pidlItem{ nullptr };
	while (S_OK == pEnumIDList->Next(1, &pidlItem, nullptr))
	{
		const std::wstring displayName = GetDisplayName(pRecycleBinFolder, pidlItem);
		if (displayNameFile.empty())
			displayNameFile = GetDisplayName(pRecycleBinFolder, pidl);
		if (displayName == displayNameFile)
		{
			CoTaskMemFree(pidlItem);
			pEnumIDList->Release();
			pRecycleBinFolder->Release();
			CoTaskMemFree(pidlRecycleBin);
			pDesktopFolder->Release();
			return true;
		}
		CoTaskMemFree(pidlItem);
	}
	pEnumIDList->Release();
	pRecycleBinFolder->Release();
	CoTaskMemFree(pidlRecycleBin);
	pDesktopFolder->Release();
	return false;
}

And I am trying to get mkid, like this:

deletedItem->mkid; // see definiton at the beginning
CMyClass mc;
ITEMIDLIST item{ deletedItem->mkid };
mc.FindFileInRecycleBin(item);

The issue is I got an exception, at line:

if (SUCCEEDED(pShellFolder->GetDisplayNameOf(reinterpret_cast<PCUITEMID_CHILD>(pidl), SHGDN_NORMAL, &str)))

inside CMyClass::GetDisplayName method. Where? At second call here:

		const std::wstring displayName = GetDisplayName(pRecycleBinFolder, pidlItem);	// <--- works fine
		if (displayNameFile.empty())
			displayNameFile = GetDisplayName(pRecycleBinFolder, pidl);	// <---- here raise exception

2 questions:

  1. Why I got exception at second call of GetDisplayName?
  2. How can I solve this task? Is there other way?

Regards,

C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,628 questions
{count} votes

Accepted answer
  1. RLWA32 43,046 Reputation points
    2024-06-12T11:37:46.58+00:00

    As I demonstrated above the recycle bin can contain more than one item with the same display name.

    A better method is to use IShellFolder::CompareIds. I saved the absolute pidls of the recycle bin items enumerated in my previous posts to files. Then I enumerated the items in the recycle bin and did the comparison using the IShellFolder interface retrieved for the Recycle bin as well as for the Desktop.

    Example code -

        CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlBin;
        CComPtr<IShellFolder> pDesktop, psf;
        CHeapPtr<BYTE> pBuffer;
    
        // Read the binary file that contains the pidl for item in recycle bin.
        HANDLE hFile = CreateFileW(L"PIDL-$RERVIFA.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
        if (hFile != INVALID_HANDLE_VALUE)
        {
            DWORD dwRead{};
            auto cBytes = GetFileSize(hFile, nullptr);
            pBuffer.Allocate(cBytes);
            ReadFile(hFile, pBuffer, cBytes, &dwRead, NULL);
            ATLASSERT(cBytes == dwRead);
            CloseHandle(hFile);
        }
    
        auto hr = SHGetDesktopFolder(&pDesktop);
        hr = SHGetKnownFolderIDList(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, NULL, &pidlBin);
        if (SUCCEEDED(hr))
        {
            hr = pDesktop->BindToObject(pidlBin, nullptr, IID_PPV_ARGS(&psf));
            if (SUCCEEDED(hr))
            {
                CComPtr<IEnumIDList> pEnumIdlist;
                hr = psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIdlist);
                if (SUCCEEDED(hr))
                {
                    ULONG cFetched{};
    
                    for (CComHeapPtr<ITEMID_CHILD> pidlChild; pEnumIdlist->Next(1, &pidlChild, &cFetched) == S_OK; pidlChild.Free())
                    {
                        STRRET strnormal{}, strparsing{};
                        hr = psf->GetDisplayNameOf(pidlChild, SHGDN_NORMAL, &strnormal);
                        hr = psf->GetDisplayNameOf(pidlChild, SHGDN_FORPARSING, &strparsing);
                        if (SUCCEEDED(hr))
                        {
                            CComHeapPtr<WCHAR> pwszName, pwszParsing;
                            hr = StrRetToStrW(&strnormal, nullptr, &pwszName);
                            hr = StrRetToStrW(&strparsing, nullptr, &pwszParsing);
                            if (SUCCEEDED(hr))
                            {
                                wprintf_s(L"IShellFolder Enumeration name was %s\n", (LPWSTR)pwszName);
                                wprintf_s(L"Parsing name: %s\n", (LPWSTR)pwszParsing);
                                // Find last pidl the represents child item in the recycle bin
                                auto pidlSaved = ILFindLastID(reinterpret_cast<PCUIDLIST_RELATIVE>(static_cast<BYTE*>(pBuffer)));
                                // Compare using IShellFolder for recycle bin
                                hr = psf->CompareIDs(SHCIDS_CANONICALONLY, static_cast<PCUITEMID_CHILD>((ITEMID_CHILD*)pidlChild), pidlSaved);
                                bool IsMatch = static_cast<short>(HRESULT_CODE(hr)) == 0;
                                if(IsMatch)
                                    wprintf(L"******************** Recycle bin IShellFolder Matched Item!!! *******************\n");
                                // Construct absolute pidl for enumerated item from recycle bin
                                auto pidlAbsolute = ILCombine(pidlBin, pidlChild);
                                // Compare using IShellFolder for desktop
                                hr = pDesktop->CompareIDs(SHCIDS_CANONICALONLY, pidlAbsolute, reinterpret_cast<PCUIDLIST_RELATIVE>(static_cast<BYTE*>(pBuffer)));
                                IsMatch = static_cast<short>(HRESULT_CODE(hr)) == 0;
                                if (IsMatch)
                                    wprintf(L"******************** Desktop IShellFolder Matched Item!!! *******************\n");
                                CoTaskMemFree(pidlAbsolute);
                            }
                        }
                    }
                }
            }
        }
    

    Results of pidl matching -

    Results


2 additional answers

Sort by: Most helpful
  1. RLWA32 43,046 Reputation points
    2024-06-11T13:56:05.6866667+00:00

    Its not clear to me how you start with a pidl for an item in the recycle bin.

    Anyway, this code will enumerate the contents of the recycle bin -

    #include <stdio.h>
    #include <tchar.h>
    
    
    #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // some CString constructors will be explicit
    
    #include <atlbase.h>
    #include <atlstr.h>
    
    // TODO: reference additional headers your program requires here
    #include <ShlObj.h>
    #include <ShlGuid.h>
    #include <propkey.h>
    #include <propsys.h>
    
    #pragma comment(lib,"propsys.lib")
    
    int main()
    {
        auto hr = CoInitialize(nullptr);
        if (SUCCEEDED(hr))
        {
            CComPtr<IShellItem> pbin;
            CComPtr<IEnumShellItems> psiEnum;
            PROPERTYKEY PKey_DisplacedFrom = { FMTID_Displaced, PID_DISPLACED_FROM };
            PROPERTYKEY PKey_DisplacedDate = { FMTID_Displaced, PID_DISPLACED_DATE };
    
            hr = SHGetKnownFolderItem(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, NULL, IID_PPV_ARGS(&pbin));
            if (SUCCEEDED(hr))
            {
                hr = pbin->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&psiEnum));
                if (SUCCEEDED(hr))
                {
                    for (CComPtr<IShellItem> psi; psiEnum->Next(1, &psi, NULL) == S_OK; psi.Release())
                    {
                        CComHeapPtr<WCHAR> pszName;
                        CComQIPtr<IShellItem2> psi2 = psi;
                        CComHeapPtr<WCHAR> pszItemName, pszItemNameDisplay, pszDateDeleted, pszDeletedFrom;
                        CComPtr<IContextMenu> pcm;
    
                        hr = psi2->GetDisplayName(SIGDN_NORMALDISPLAY, &pszName);
    
                        hr = psi2->GetString(PKEY_ItemName, &pszItemName);
    
                        hr = psi2->GetString(PKEY_ItemNameDisplay, &pszItemNameDisplay);
    
                        hr = psi2->GetString(PKey_DisplacedDate, &pszDateDeleted);
    
                        hr = psi2->GetString(PKey_DisplacedFrom, &pszDeletedFrom);
    
                        wprintf_s(L"Display Name is %s\n", (LPWSTR)pszName);
                        wprintf_s(L"\tItemName is %s\n\tItemNameDisplay is %s\n\tDate Deleted is %s\n\tOriginal Location is %s\n\n", (LPWSTR)pszItemName, (LPWSTR)pszItemNameDisplay, (LPWSTR)pszDateDeleted, (LPWSTR)pszDeletedFrom);
    
                    }
                }
            }
        }
    
        CoUninitialize();
    
        return 0;
    
    }
    

  2. Flaviu_ 971 Reputation points
    2024-06-28T13:13:52.8333333+00:00

    Hi. I am pretty close of a successful code, but not quite.

    So, in the original code (open source code), I have the following data that point out items from RecycleBin:

    std::vector<PCIDLIST_ABSOLUTE> DeletedItems;

    From the vector items I need to find the recycled item. And I have tried/adapted this code:

    bool CSomeClass::FindDeletedItem(const PCIDLIST_ABSOLUTE& pidl)
    {
    	if (!SUCCEEDED(CoInitialize(nullptr)))
    		return false;
    	bool found{ false };
    	CComPtr<IShellFolder> pDesktop, psf;
    	CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlBin;
    	do
    	{
    		auto hr = SHGetDesktopFolder(&pDesktop);
    		hr = SHGetKnownFolderIDList(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT, NULL, &pidlBin);
    		if (!SUCCEEDED(hr))
    			break;
    		hr = pDesktop->BindToObject(pidlBin, nullptr, IID_PPV_ARGS(&psf));
    		if (!SUCCEEDED(hr))
    			break;
    		CComPtr<IEnumIDList> pEnumIdlist;
    		hr = psf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIdlist);
    		if (!SUCCEEDED(hr))
    			break;
    		ULONG cFetched{};
    		for (CComHeapPtr<ITEMID_CHILD> pidlChild; S_OK == pEnumIdlist->Next(1, &pidlChild, &cFetched); pidlChild.Free())
    		{
    			const auto pidlAbsolute = ILCombine(pidlBin, pidlChild);
    			// Compare using IShellFolder for desktop
    			hr = pDesktop->CompareIDs(SHCIDS_CANONICALONLY, pidlAbsolute, pidl);
    			if (0 == static_cast<short>(HRESULT_CODE(hr)))
    				found = true;
    			CoTaskMemFree(pidlAbsolute);
    			if (found)
    				break;
    		}
    	} while (false);
    	CoUninitialize();
    	return found;
    }
    

    And I have tried the code like this:

    	for (const auto& item : deletedItemList)
    	{
    		const auto s = std::format("+++++++++++++++++{}\n", FileOperations::FindDeletedItem(item));
    		OutputDebugStringA(s.c_str());
    	}
    

    Even if I know the deleted item is inside RecycleBin, seems my code doesn't find the deleted item, output is:

    +++++++++++++++++false

    What I did wrong in my FindDeletedItem code? The problem is that I cannot try my FindDeletedItem code in a simple test application to realize what I did wrong, can you help here?