【问题标题】:IShellLinkW::GetPath( SLGP_RAWPATH ) does not return the 'raw' target of a lnk fileIShellLinkW::GetPath( SLGP_RAWPATH ) 不返回 lnk 文件的“原始”目标
【发布时间】:2019-10-13 09:26:21
【问题描述】:

我注意到,如果快捷方式的目标指向特殊文件夹(如“我的电脑”或控制面板小程序),IShellLinkW::GetPath 方法会返回一个空字符串。

示例代码

' CLSID_ShellLink from ShlGuid.h
Dim cShellLink As New CShellLink() 

' https://msdn.microsoft.com/en-us/library/windows/desktop/ms687223%28v=vs.85%29.aspx
Dim persistFile As IPersistFile = DirectCast(cShellLink, IPersistFile) 
persistFile.Load(lnkFilePath, 0)

Dim target As New StringBuilder(260)

' https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-ishelllinkw
Dim lnkW As IShellLinkW = DirectCast(cShellLink, IShellLinkW)
lnkW.GetPath(target, target.Capacity, Nothing, IShellLinkGetPathFlags.RawPath)

(抱歉,这里分享的 P/Invokes 太多,但您可以在 this 存储库中找到它们。)

上面的代码可以正确检索任何指向文件或目录的 .lnk 文件的目标。它无法检索指向特殊项目/虚拟文件夹的目标。

我的问题是:

这是设计使然吗?如果没有,我错过了什么或做错了什么? (也许我的定义是错误的?)。为了检索特殊目标,我必须做什么?请注意,我想检索目标以将其显示在 PropertyGrid 中,并创建一个具有特殊目标克隆的新目标。

请注意,我开发此代码所使用的编程语言无关紧要,因为我首先要求的是方向,而且我接受任何用 C# 编写的解决方案。

【问题讨论】:

  • 路径仅适用于文件系统对象。您必须熟悉 PIDL,IShellLinkW::GetIDList() 会返回它们。不推荐。
  • SHGetPathFromIDListSHGetFileInfo(注意它接受LPCWSTR,但也接受pidl),SHGetSpecialFolderLocationIShellLinkW 接口还提供了一个可能派上用场的.Resolve() 方法。在同一个shell对象上使用,然后得到GetIDList()返回的out地址
  • 感谢 cmets,但如果我调用 IShellLinkW.GetIDList,然后获得(有效,非零)PIDL 的 SHGetPathFromIDListW,我总是得到一个空字符串,并且函数返回 false 但最后一个 win32 错误代码为零(SetLastError 设置为 true)。为什么?。我尝试了各种签名,还有来自 pinvoke.net 网站的签名。
  • 另外,我不明白使用 SHGetFileInfo 有什么意义,因为我似乎只能获取文件名 (SHGFI_DISPLAYNAME) 和文件类型描述 (SHGFI_TYPENAME)。顺便说一句,这个函数接受我用 IShellLinkW.GetIDList 获得的 PIDL,但正如我提到的,我似乎无法用这个函数检索“原始”lnk 目标字符串......只是文件名或文件类型的描述。
  • 在十六进制编辑器中分析这些 lnk 文件,它们具有目标的 CLSID(例如 ::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C} 指向我的电脑),并且如果我在创建调用 SetPath 的快捷方式时使用该 CLSID(没有弄乱 SetIDList 和任何琐碎的事情),它会按预期工作。这个发现似乎使问题更容易解决,因为我想我可以从 PIDL 获得 CLSID。 PIDL 是一个我根本没有读过的概念,所以如果我说的是愚蠢的话,请原谅。但我仍然会通过可用的 win32 函数进行调查以获取 CLSID 值...

标签: c# .net vb.net lnk


【解决方案1】:

如主帖的 cmets 框中所述,为了检索指向特殊的非文件系统路径(例如“我的 PC”)的 shell 链接的原始目标,首先我们需要检索PIDL 通过调用IShellLinkW::GetIDList()

接下来,我们将(绝对)PIDL 传递给带有标志SIGDN_DESKTOPABSOLUTEPARSINGSHGetNameFromIDList,最后这将返回一个CLSID 字符串,我们可以使用IShellLinkW::SetPath() 为.lnk 文件的目标指定该字符串。它按预期工作。

请注意,我不确定是否存在无法按预期工作的特殊情况(lnk 文件的罕见目标)。我没有发现那个错误案例。

如果需要,可以使用标志SIGDN_NORMALDISPLAY 获得正常显示名称,或者也可以通过调用SHGetFileInfo 函数来获得,如主帖的cmets 框中所述。


感谢向我建议 PIDL 检索及其用法的用户,因为我不确定要调查什么才能解决此问题。

我还将感谢 Vanara 开源库,它帮助我在其源代码中找到每个 SH* 函数的正确 P/Invokes,特别是 the signature for SHGetNameFromIDList

【讨论】:

  • pidl 获取 DisplayName 的许多方法,其中一些由 ShellItem 接口(例如GetDisplayName())公开,用于绑定项目 - 相对或绝对。您使用的内容与上下文和路径类型有关。关于编组器的一个注意事项:您经常在示例代码中看到Marshal.AllocHGlobal 的使用。您应该按照说明使用Marshal.AllocCoTaskMem(原生CoTaskMemAlloc)释放。
  • 如果你看到有人使用SHGetMalloc,来初始化一个IMalloc接口,那是旧代码:该方法被CoTaskMemAlloc取代。
  • 感谢您的评论和提示!。也许下一个是题外话,但我想说的是,对我来说 WindowsAPICodePack 似乎是一个更好(一致的)源代码,可以作为示例(并复制)与 IShellItem/IShellFolder/ 相关的任何接口IShellView 等。无论如何,Vanara 有大量的 P/Invokes 集合,这在某些情况下非常有用。
猜你喜欢
  • 1970-01-01
  • 2019-02-07
  • 2010-09-28
  • 2011-10-29
  • 2013-06-06
  • 1970-01-01
  • 2023-03-23
  • 2020-05-22
  • 1970-01-01
相关资源
最近更新 更多