如何在 shell 环境(winapi)中获取扩展名的文件名?
How to get filename with extension in shell envirionment (winapi)?
我有一个程序可以获取有关 windows 回收站中文件的信息。我看到的存储桶访问问题的唯一非拐杖解决方案是使用 shell 环境。
我有一个使用 winapi(shell 环境)从回收站中删除单个文件(或文件夹)的代码,如下所示:
#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;
}
在上面的代码中,最上面有常量定义(SCID_RemovedFrom
、SCID_RemovedName
),用于通过函数[=14]获取被删除文件所在的路径和名称=].但是,生成的名称并不总是包含扩展名,只有当文件以扩展名显示在文件资源管理器中的方式在系统中注册时才会包含。在其他情况下,获取的名称没有扩展名。
我找不到常量,其中 GetDetailsEx
将 return 全名(带扩展名),而不管用户的设置如何。到目前为止,我只找到了一个拐杖:使用 pRecycleBin->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &sret);
来获取扩展名,但是有一个问题:我无法确定如何解释名称 some.docx
和扩展名 $RXZH1I6.docx
:是some.docx
全名(如果显示扩展名)或 some.docx.docx
是全名(如果隐藏扩展名)。原来这拐杖更危险
如何获得保证有扩展名的任何文件(“NONFOLDER”)的名称?
下面是一些示例代码(使用 ATL 实现智能指针),显示回收站(或您喜欢的任何其他文件夹)中所有项目的所有属性。
您可以选择您想要的属性。我已经转储了一个带有已删除 txt 文件的示例输出。
该项目的显示名称实际上取决于加工设置,但您可以看到还有许多其他有趣的属性,例如:
System.Recycle.DeletedFrom
(相当于 SCID_RemovedFrom)
System.Recycle.DateDeleted
(相当于 SCID_DateDeleted)
System.ItemNameDisplay
(注意虚拟物品,你不会总是
有一个 System.FileName)
System.ItemType
(注意虚拟物品,
你不会总是有 System.FileExtension)
示例输出:
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
示例代码:
#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;
}
我有一个程序可以获取有关 windows 回收站中文件的信息。我看到的存储桶访问问题的唯一非拐杖解决方案是使用 shell 环境。
我有一个使用 winapi(shell 环境)从回收站中删除单个文件(或文件夹)的代码,如下所示:
#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;
}
在上面的代码中,最上面有常量定义(SCID_RemovedFrom
、SCID_RemovedName
),用于通过函数[=14]获取被删除文件所在的路径和名称=].但是,生成的名称并不总是包含扩展名,只有当文件以扩展名显示在文件资源管理器中的方式在系统中注册时才会包含。在其他情况下,获取的名称没有扩展名。
我找不到常量,其中 GetDetailsEx
将 return 全名(带扩展名),而不管用户的设置如何。到目前为止,我只找到了一个拐杖:使用 pRecycleBin->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &sret);
来获取扩展名,但是有一个问题:我无法确定如何解释名称 some.docx
和扩展名 $RXZH1I6.docx
:是some.docx
全名(如果显示扩展名)或 some.docx.docx
是全名(如果隐藏扩展名)。原来这拐杖更危险
如何获得保证有扩展名的任何文件(“NONFOLDER”)的名称?
下面是一些示例代码(使用 ATL 实现智能指针),显示回收站(或您喜欢的任何其他文件夹)中所有项目的所有属性。
您可以选择您想要的属性。我已经转储了一个带有已删除 txt 文件的示例输出。 该项目的显示名称实际上取决于加工设置,但您可以看到还有许多其他有趣的属性,例如:
System.Recycle.DeletedFrom
(相当于 SCID_RemovedFrom)System.Recycle.DateDeleted
(相当于 SCID_DateDeleted)System.ItemNameDisplay
(注意虚拟物品,你不会总是 有一个 System.FileName)System.ItemType
(注意虚拟物品, 你不会总是有 System.FileExtension)
示例输出:
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
示例代码:
#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;
}