IShellLink - 如何获取原始目标路径

Posted

技术标签:

【中文标题】IShellLink - 如何获取原始目标路径【英文标题】:IShellLink - how to get the original target path 【发布时间】:2021-08-21 17:44:44 【问题描述】:

我在 Windows PC 中创建了一个shortcut,目标路径为:

C:\Users\b\Desktop\New Text Document.txt

然后我将快捷方式复制到另一台具有不同用户名的PC上,我想检索原始目标路径。

如果你用文本编辑器打开快捷方式文件,可以看到原来的路径被保留了,所以目标肯定是可以的。

尽管存在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();

【问题讨论】:

【参考方案1】:

您的快捷方式是一个.lnk 文件,只是没有.lnk 文件扩展名。根据微软最新的"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,这个我就不知道了。它也存在于文件中,因此在 theory 中应该有一种方法可以查询它,但我不知道它在哪个字段中,而无需花时间完全解码此文件。您应该尝试使用上述文档编写自己的解码器。

【讨论】:

【参考方案2】:

Windows Shell Link 类实现了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
...   

【讨论】:

以上是关于IShellLink - 如何获取原始目标路径的主要内容,如果未能解决你的问题,请参考以下文章

如何获取在 Flutter 中使用图像选择器插件选择的图像的原始路径,而不是复制到缓存?

如何获取原始文件夹uri路径并保存在android的内部存储器中?

如何在 WatchKit 扩展目标中获取核心数据持久存储路径

如何获取符号链接的目标?

delphi 知道路径和进程如何获取窗口句柄?

如何获取网站重定向目标网址(最终用户链接)