I have a program that gets information about files in the windows recycle bin. The only non-crutch solution to the bucket access problem I see is using the shell environment.
I have a code for removing single file (or folder) from recycle bin with winapi (shell envirionment) like the next:
#include <iostream>
#include <shobjidl_core.h>
#include <Shlobj.h>
#include <shlwapi.h>
#include <ntquery.h>
const SHCOLUMNID SCID_RemovedFrom = { PSGUID_DISPLACED, PID_DISPLACED_FROM };
const SHCOLUMNID SCID_RemovedName = { PSGUID_STORAGE, PID_STG_NAME };
const SHCOLUMNID SCID_DateDeleted = { PSGUID_DISPLACED, PID_DISPLACED_DATE };
const SHCOLUMNID SCID_DateCreated = { PSGUID_STORAGE, PID_STG_CREATETIME };
const SHCOLUMNID SCID_DateModifed = { PSGUID_STORAGE, PID_STG_WRITETIME };
const SHCOLUMNID SCID_DateOpened = { PSGUID_STORAGE, PID_STG_ACCESSTIME };
int main() {
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
LPSHELLFOLDER pDesktop = NULL;
SHGetDesktopFolder(&pDesktop);
LPITEMIDLIST pidlRecycleBin = NULL;
SHGetSpecialFolderLocation(NULL, CSIDL_BITBUCKET, &pidlRecycleBin);
IShellFolder2 *pRecycleBin;
pDesktop->BindToObject(pidlRecycleBin, NULL, IID_IShellFolder2, (LPVOID*)&pRecycleBin);
pDesktop->Release();
CoTaskMemFree(pidlRecycleBin);
IEnumIDList* penumFiles;
pRecycleBin->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penumFiles);
STRRET sret;
IFileOperation *pfo;
CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
pfo->SetOperationFlags(FOF_NO_UI);
LPITEMIDLIST pidl = NULL;
BSTR bstr = NULL;
VARIANT vt;
SYSTEMTIME syst;
while (penumFiles->Next(1, &pidl, NULL) != S_FALSE) {
pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sret); // not a FULL name!!!
StrRetToBSTR(&sret, pidl, &bstr);
std::wcout << bstr << L' ';
SysFreeString(bstr);
pRecycleBin->GetDetailsEx(pidl, &SCID_DateDeleted, &vt);
VariantTimeToSystemTime(vt.date, &syst);
std::wcout << "\t" << syst.wHour << ":" << syst.wMinute << " " << syst.wDay << "." << syst.wMonth << "." << syst.wYear << std::endl;
/*MARK ITEMS TO DELETE*/
SHCreateItemWithParent(NULL, pRecycleBin, pidl, IID_IShellItem, (void**)&shi);
pfo->DeleteItem(shi, NULL);
shi->Release();
CoTaskMemFree(pidl);
}
penumFiles->Release();
pRecycleBin->Release();
/*DELETE MARKED ITEMS*/
pfo->PerformOperations();
pfo->Release();
//Update recycle bin icon [undocumented function]
(void (*)())GetProcAddress(GetModuleHandle(L"shell32.dll"), "SHUpdateRecycleBinIcon")();
CoUninitialize();
return 0;
}
In the code above, at the top, there are constant definitions (SCID_RemovedFrom
, SCID_RemovedName
) for getting the path where the deleted file was located and the name via the function GetDetailsEx
. However, the resulting name does not always contain the extension, but only when the file is registered in the system in such a way that the extension is displayed in File Explorer. In other cases, the name is obtained without an extension.
I couldn't find a constant where GetDetailsEx
will return the full name (with the extension) regardless of the user's settings. So far, I've only found a crutch: use pRecycleBin->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &sret);
to get extensions, BUT there a problem: I can't determine how to interpret the name some.docx
and extension $RXZH1I6.docx
: is some.docx
a full name (if extensions are shown) or some.docx.docx
is a full name (if extensions are hidden). It turns out that this crutch is even dangerous.
How can I get the name of any file ("NONFOLDER") guaranteed to have an extension?
Here is some sample code (using ATL for smart pointers) that displays all properties for all items in the recycle bin (or any other folder you'd like).
You can pick the properties you want. I've dumped a sample output with a deleted txt file. The item's display name actually depends on the maching settings, but you can see there are lots of other interesting properties, for example:
System.Recycle.DeletedFrom
(eq to SCID_RemovedFrom)System.Recycle.DateDeleted
(eq to SCID_DateDeleted)System.ItemNameDisplay
(note for virtual items, you won't always
have a System.FileName)System.ItemType
(note for virtual items,
you won't always have a System.FileExtension)Sample output:
D:\temp\New Text Document.txt // this is the normal display name (depends on settings)
System.ItemFolderNameDisplay = Recycle Bin
System.ItemTypeText = Text Document
System.ItemNameDisplay = New Text Document.txt
System.Size = 0
System.FileAttributes = 33
System.DateModified = 2019/12/04:17:52:05.132
System.DateCreated = 2019/12/04:17:52:06.000
System.DateAccessed = 2019/12/04:17:52:06.000
System.ItemNameDisplayWithoutExtension = $RWLK87M
System.ContentType = text/plain
System.Document.DateCreated = 2019/12/04:17:52:05.132
System.Document.DateSaved = 2019/12/04:17:52:05.132
System.Recycle.DeletedFrom = D:\temp
System.Recycle.DateDeleted = 2021/02/16:08:18:46.000
System.FileOwner = KILLROY\WasHere
System.NetworkLocation =
System.ComputerName = KILLROY
System.ItemPathDisplayNarrow = $RWLK87M (D:\$RECYCLE.BIN\Recycle Bin)
System.PerceivedType = 1
System.ItemType = .txt
System.ParsingName = D:\$RECYCLE.BIN\S-1-5-21-2804694453-2728412037-1988799983-1001\$RWLK87M.txt
System.SFGAOFlags = 1077936503
System.ParsingPath = D:\$RECYCLE.BIN\S-1-5-21-2804694453-2728412037-1988799983-1001\$RWLK87M.txt
System.FileExtension = .txt
System.ItemDate = 2019/12/04:17:52:05.132
System.KindText = Document
System.FileAttributesDisplay = Read-only
System.IsShared = 0
System.SharedWith =
System.SharingStatus = 2
System.ShareScope =
System.Security.EncryptionOwnersDisplay =
System.ItemName = $RWLK87M.txt
System.Shell.SFGAOFlagsStrings = filesys; stream
System.Link.TargetSFGAOFlagsStrings =
System.OfflineAvailability =
System.ZoneIdentifier = 0
System.LastWriterPackageFamilyName =
System.AppZoneIdentifier =
System.Kind = document
System.Security.EncryptionOwners =
System.ItemFolderPathDisplayNarrow = Recycle Bin (D:\$RECYCLE.BIN)
System.FileName = New Text Document.txt
System.Security.AllowedEnterpriseDataProtectionIdentities =
System.ThumbnailCacheId = 11524143073100486861
System.VolumeId = {184CD9C9-570E-483F-9AE1-68D57270A239}
System.Link.TargetParsingPath =
System.Link.TargetSFGAOFlags =
System.ItemFolderPathDisplay = D:\$RECYCLE.BIN\Recycle Bin
System.ItemPathDisplay = D:\$RECYCLE.BIN\Recycle Bin\$RWLK87M.txt
{9E5E05AC-1936-4A75-94F7-4704B8B01923} 0 = New Text Document.txt
System.AppUserModel.ID =
System.AppUserModel.ParentID =
System.Link.TargetExtension =
System.OfflineStatus =
System.IsFolder = 0
System.NotUserContent = 0
System.StorageProviderAggregatedCustomStates =
System.SyncTransferStatusFlags =
System.DateImported = 2019/12/04:17:52:05.132
System.ExpandoProperties =
System.FilePlaceholderStatus = 6
Sample code:
#include <iostream>
#include <shobjidl_core.h>
#include <shlobj.h>
#include <propvarutil.h>
#include <atlbase.h>
#include <atlcom.h>
#pragma comment(lib, "propsys")
int main() {
HRESULT hr = CoInitialize(nullptr);
{
// get recycle bin folder
CComPtr<IShellItem> bin;
hr = SHCreateItemInKnownFolder(FOLDERID_RecycleBinFolder, 0, nullptr, IID_PPV_ARGS(&bin));
if (SUCCEEDED(hr))
{
// enumerate items
CComPtr<IEnumShellItems> items;
hr = bin->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&items));
if (SUCCEEDED(hr))
{
do
{
// get item
CComPtr<IShellItem> item;
hr = items->Next(1, &item, nullptr);
if (hr != 0)
break;
// get the display name (depends on settings)
CComHeapPtr<wchar_t> path;
item->GetDisplayName(SIGDN_NORMALDISPLAY, &path);
std::wcout << path.m_pData << std::endl;
// get item's property store
CComPtr<IPropertyStore> store;
hr = CComQIPtr<IShellItem2>(item)->GetPropertyStore(GPS_DEFAULT, IID_PPV_ARGS(&store));
if (SUCCEEDED(hr))
{
DWORD count = 0;
store->GetCount(&count);
for (DWORD i = 0; i < count; i++)
{
// print this property (name, value)
PROPERTYKEY pk;
hr = store->GetAt(i, &pk);
if (SUCCEEDED(hr))
{
// get property canonical name
CComHeapPtr<wchar_t> name;
PSGetNameFromPropertyKey(pk, &name);
// get property value
PROPVARIANT pv;
PropVariantInit(&pv);
hr = store->GetValue(pk, &pv);
if (SUCCEEDED(hr))
{
CComHeapPtr<wchar_t> value;
hr = PropVariantToStringAlloc(pv, &value); // propvarutil.h
if (SUCCEEDED(hr))
{
if (!name) // unknown name
{
CComHeapPtr<wchar_t> fmtid;
StringFromCLSID(pk.fmtid, &fmtid);
std::wcout << L" " << fmtid.m_pData << L" " << pk.pid << L" = " << value.m_pData << std::endl;
}
else
{
std::wcout << L" " << name.m_pData << L" = " << value.m_pData << std::endl;
}
}
else
{
// can't convert to string, print something useful
}
}
PropVariantClear(&pv);
}
}
}
} while (true);
}
}
}
CoUninitialize();
return 0;
}