IShellLink——如何获取原始目标路径
IShellLink - how to get the original target path
我在 Windows PC 中创建了一个 shortcut,目标路径为:
C:\Users\b\Desktop\New Text Document.txt
然后我把快捷方式复制到另一台不同用户名的电脑上,想找回原来的目标路径
如果用文本编辑器打开快捷方式文件,可以看到保留了原路径,所以目标是绝对可以的。
尽管存在 SLGP_RAWPATH
,但以下代码不起作用。它输出:
C:\Users\a\Desktop\New Text Document.txt
正在将用户文件夹名称更改为与 运行 程序关联的名称。
我了解到不是环境变量的问题,因为在文件中看不到环境变量名。但是我找不到任何关于这种自动重定位行为的文档。
IShellLinkW*lnk;
if (CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID*)&lnk) == 0){
IPersistFile* file;
if (lnk->QueryInterface(IID_IPersistFile, (void**)&file) == 0){
if (file->Load(L"shortcut", 0) == 0){
wchar_t path[MAX_PATH];
if (lnk->GetPath(path, _countof(path), 0, SLGP_RAWPATH) == 0){
_putws(path);
}
IShellLinkDataList* sdl;
if (lnk->QueryInterface(IID_IShellLinkDataList, (void**)&sdl) == 0){
EXP_SZ_LINK* lnkData;
if (sdl->CopyDataBlock(EXP_SZ_LINK_SIG, (void**)&lnkData) == 0){
_putws(lnkData->swzTarget);
LocalFree(lnkData);
}
sdl->Release();
}
}
file->Release();
}
lnk->Release();
}
您的快捷方式是一个 .lnk
文件,只是没有 .lnk
文件扩展名。根据 Microsoft 的最新 "Shell Link (.LNK) Binary File Format" documentation,您的快捷方式似乎配置为 relative 文件目标。相对名称只是 New Text Document.txt
。我没有深入研究这个文件,但我猜它是相对于系统的桌面文件夹的,所以它会占用当前 PC 的实际桌面文件夹。这可以解释为什么在更改 PC 时查询目标会将相对根从 C:\Users\b\Desktop
更改为 C:\Users\a\Desktop
。
至于能否查询到原来的目标C:\Users\b\Desktop\New Text Document.txt
,我就不知道了。它也存在于文件中,所以在理论中应该有查询它的方法,但我不知道它在哪个字段中,没有花时间完全解码这个文件。您应该尝试使用上述文档编写自己的解码器。
Windows Shell Link class 实现了一个 property store,因此您可以使用这样的代码访问它(使用 ATL 智能指针):
int main()
{
// note: error checking omitted!
CoInitialize(NULL);
{
CComPtr<IShellLink> link;
link.CoCreateInstance(CLSID_ShellLink);
CComPtr<IPersistFile> file;
link->QueryInterface(&file);
file->Load(L"shortcut", STGM_READ);
// get the property store
CComPtr<IPropertyStore> ps;
link->QueryInterface(&ps);
// dump all properties
DWORD count = 0;
ps->GetCount(&count);
for (DWORD i = 0; i < count; i++)
{
PROPERTYKEY pk;
ps->GetAt(i, &pk);
// get property's canonical name from pk
CComHeapPtr<wchar_t> name;
PSGetNameFromPropertyKey(pk, &name);
PROPVARIANT pv;
PropVariantInit(&pv);
ps->GetValue(pk, &pv);
// convert PropVariants to a string to be able to display
CComHeapPtr<wchar_t> valueAsString;
PropVariantToStringAlloc(pv, &valueAsString); // propvarutil.h
wprintf(L"%s: %s\n", name, valueAsString);
PropVariantClear(&pv);
}
}
CoUninitialize();
return 0;
}
它会输出这个:
System.ItemNameDisplay: New Text Document.txt
System.DateCreated: 2021/06/03:14:45:30.000
System.Size: 0
System.ItemTypeText: Text Document
System.DateModified: 2021/06/03:14:45:29.777
System.ParsingPath: C:\Users\b\Desktop\New Text Document.txt
System.VolumeId: {E506CEB2-0000-0000-0000-300300000000}
System.ItemFolderPathDisplay: C:\Users\b\Desktop
所以,你要找的是System.ParsingPath,你可以这样直接得到:
...
ps->GetValue(PKEY_ParsingPath, &pv); // propkey.h
...
我在 Windows PC 中创建了一个 shortcut,目标路径为:
C:\Users\b\Desktop\New Text Document.txt
然后我把快捷方式复制到另一台不同用户名的电脑上,想找回原来的目标路径
如果用文本编辑器打开快捷方式文件,可以看到保留了原路径,所以目标是绝对可以的。
尽管存在 SLGP_RAWPATH
,但以下代码不起作用。它输出:
C:\Users\a\Desktop\New Text Document.txt
正在将用户文件夹名称更改为与 运行 程序关联的名称。
我了解到不是环境变量的问题,因为在文件中看不到环境变量名。但是我找不到任何关于这种自动重定位行为的文档。
IShellLinkW*lnk;
if (CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID*)&lnk) == 0){
IPersistFile* file;
if (lnk->QueryInterface(IID_IPersistFile, (void**)&file) == 0){
if (file->Load(L"shortcut", 0) == 0){
wchar_t path[MAX_PATH];
if (lnk->GetPath(path, _countof(path), 0, SLGP_RAWPATH) == 0){
_putws(path);
}
IShellLinkDataList* sdl;
if (lnk->QueryInterface(IID_IShellLinkDataList, (void**)&sdl) == 0){
EXP_SZ_LINK* lnkData;
if (sdl->CopyDataBlock(EXP_SZ_LINK_SIG, (void**)&lnkData) == 0){
_putws(lnkData->swzTarget);
LocalFree(lnkData);
}
sdl->Release();
}
}
file->Release();
}
lnk->Release();
}
您的快捷方式是一个 .lnk
文件,只是没有 .lnk
文件扩展名。根据 Microsoft 的最新 "Shell Link (.LNK) Binary File Format" documentation,您的快捷方式似乎配置为 relative 文件目标。相对名称只是 New Text Document.txt
。我没有深入研究这个文件,但我猜它是相对于系统的桌面文件夹的,所以它会占用当前 PC 的实际桌面文件夹。这可以解释为什么在更改 PC 时查询目标会将相对根从 C:\Users\b\Desktop
更改为 C:\Users\a\Desktop
。
至于能否查询到原来的目标C:\Users\b\Desktop\New Text Document.txt
,我就不知道了。它也存在于文件中,所以在理论中应该有查询它的方法,但我不知道它在哪个字段中,没有花时间完全解码这个文件。您应该尝试使用上述文档编写自己的解码器。
Windows Shell Link class 实现了一个 property store,因此您可以使用这样的代码访问它(使用 ATL 智能指针):
int main()
{
// note: error checking omitted!
CoInitialize(NULL);
{
CComPtr<IShellLink> link;
link.CoCreateInstance(CLSID_ShellLink);
CComPtr<IPersistFile> file;
link->QueryInterface(&file);
file->Load(L"shortcut", STGM_READ);
// get the property store
CComPtr<IPropertyStore> ps;
link->QueryInterface(&ps);
// dump all properties
DWORD count = 0;
ps->GetCount(&count);
for (DWORD i = 0; i < count; i++)
{
PROPERTYKEY pk;
ps->GetAt(i, &pk);
// get property's canonical name from pk
CComHeapPtr<wchar_t> name;
PSGetNameFromPropertyKey(pk, &name);
PROPVARIANT pv;
PropVariantInit(&pv);
ps->GetValue(pk, &pv);
// convert PropVariants to a string to be able to display
CComHeapPtr<wchar_t> valueAsString;
PropVariantToStringAlloc(pv, &valueAsString); // propvarutil.h
wprintf(L"%s: %s\n", name, valueAsString);
PropVariantClear(&pv);
}
}
CoUninitialize();
return 0;
}
它会输出这个:
System.ItemNameDisplay: New Text Document.txt
System.DateCreated: 2021/06/03:14:45:30.000
System.Size: 0
System.ItemTypeText: Text Document
System.DateModified: 2021/06/03:14:45:29.777
System.ParsingPath: C:\Users\b\Desktop\New Text Document.txt
System.VolumeId: {E506CEB2-0000-0000-0000-300300000000}
System.ItemFolderPathDisplay: C:\Users\b\Desktop
所以,你要找的是System.ParsingPath,你可以这样直接得到:
...
ps->GetValue(PKEY_ParsingPath, &pv); // propkey.h
...