使用 IShellLink 接口解决已更改驱动器号的快捷方式 [英] Using IShellLink Interface to resolve a shortcut which has changed their drive letter

查看:23
本文介绍了使用 IShellLink 接口解决已更改驱动器号的快捷方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据此处的 MSDN 信息:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb774952%28v=vs.85%29.aspx

这里:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb774944%28v=vs.85%29.aspx

如何解析已更改快捷方式 Target 上的驱动器号的链接的完整路径?

Windows 会在不到一秒的时间内自动执行此操作,但是我需要在我的应用程序中处理大量快捷方式文件(.lnk 文件),而且我总是得到原始的旧路径,我无法像 windows 一样解析路径

例如,如果我在此路径中有一个快捷方式文件:

C:\Test.lnk

而快捷文件的目标是这个

D:\Directory\Test.txt

然后,如果我将硬盘驱动器的盘符从D:"重命名为F:"(硬盘驱动器,而不是目标快捷方式)Windows 仍然可以将快捷方式识别为有效并且可以立即解析快捷方式路径,因此我知道这可以通过 IShellInterface 来完成,但问题是我不知道该怎么做.

我不明白我需要做什么来解决快捷方式,我不知道我是否需要使用 GetPath 方法或 Resolve 方法,或者两者,我也不知道我需要传递哪个窗口句柄到解析方法...然后是在窗口中启动一个 msgbox 还是该方法将返回一个带有解析路径的字符串?,所有这些都让我发疯,我需要一个代码示例.

我正在使用这个类:

导入 System.Runtime.InteropServices导入 System.Text公开课表1Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) 处理 MyBase.LoadDim TargetFilename As String = ResolveShortcut("C:\Test.lnk")结束子<DllImport("shfolder.dll", CharSet:=CharSet.Auto)>朋友共享函数 SHGetFolderPath(hwndOwner As IntPtr, nFolder As Integer, hToken As IntPtr, dwFlags As Integer, lpszPath As StringBuilder) As Integer结束函数<标志()>私有枚举 SLGP_FLAGS''' <summary>检索标准短(8.3格式)文件名</summary>SLGP_SHORTPATH = &H1''' <summary>检索文件的通用命名约定 (UNC) 路径名</summary>SLGP_UNCPRIORITY = &H2'''  检索原始路径名.原始路径可能不存在并且可能包含需要扩展的环境变量SLGP_RAWPATH = &H4结束枚举<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>私有结构 WIN32_FIND_DATAW公共 dwFileAttributes 作为 UInteger公共 ftCreationTime 一样长公共 ftLastAccessTime 一样长公共 ftLastWriteTime 一样长公共 nFileSizeHigh 作为 UInteger公共 nFileSizeLow As UInteger公共 dwReserved0 作为 UInteger公共 dwReserved1 作为 UInteger<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)>公共 cFileName 作为字符串<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=14)>公共 cAlternateFileName 作为字符串端部结构<标志()>私有枚举 SLR_FLAGS''' <总结>''' 如果链接无法解析,则不显示对话框.设置 SLR_NO_UI 时,''' fFlags 的高位字可以设置为超时值,指定''' 解析链接所花费的最长时间.函数返回,如果''' 链接在超时时间内无法解析.如果设置了高位词''' 为零,超时持续时间将设置为默认值 3,000 毫秒'''(3 秒).要指定一个值,请将 fFlags 的高位字设置为所需的超时''' 持续时间,以毫秒为单位.''' </summary>SLR_NO_UI = &H1''' <summary>已过时且不再使用</summary>SLR_ANY_MATCH = &H2''' <summary>如果链接对象已更改,则更新其路径和标识符列表.'''如果设置了SLR_UPDATE,则不需要调用IPersistFile::IsDirty来判断''' 链接对象是否已更改.</summary>SLR_UPDATE = &H4''' <summary>不更新链接信息</summary>SLR_NOUPDATE = &H8''' <summary>不执行搜索启发式</summary>SLR_NOSEARCH = &H10''' <summary>不要使用分布式链接跟踪</summary>SLR_NOTRACK = &H20''' <summary>禁用分布式链接跟踪.默认情况下,分布式链接跟踪轨道''' 基于卷名跨多个设备的可移动媒体.它还使用''' 通用命名约定 (UNC) 路径,用于跟踪其驱动器号的远程文件系统''' 已经改变.设置 SLR_NOLINKINFO 会禁用这两种类型的跟踪.</summary>SLR_NOLINKINFO = &H40''' <summary>调用 Microsoft Windows Installer</summary>SLR_INVOKE_MSI = &H80结束枚举''' <summary>IShellLink 接口允许创建、修改和解析 Shell 链接</summary><ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")>专用接口 IShellLinkW''' <summary>检索Shell链接对象的路径和文件名</summary>Sub GetPath(<Out(), MarshalAs(UnmanagedType.LPWStr)> pszFile As StringBuilder, cchMaxPath As Integer, ByRef pfd As WIN32_FIND_DATAW, fFlags As SLGP_FLAGS)''' <summary>检索外壳链接对象的项目标识符列表</summary>Sub GetIDList(ByRef ppidl As IntPtr)''' <summary>设置指向 Shell 链接对象的项标识符列表 (PIDL) 的指针.</summary>Sub SetIDList(pidl As IntPtr)''' <summary>检索Shell链接对象的描述字符串</summary>Sub GetDescription( pszName As StringBuilder, cchMaxName As Integer)'''  设置 Shell 链接对象的描述.描述可以是任何应用程序定义的字符串</summary>Sub SetDescription( pszName As String)''' <summary>检索Shell链接对象的工作目录名称</summary>Sub GetWorkingDirectory( pszDir As StringBuilder, cchMaxPath As Integer)''' <summary>为Shell链接对象设置工作目录的名称</summary>Sub SetWorkingDirectory( pszDir As String)''' <summary>检索与 Shell 链接对象相关联的命令行参数</summary>Sub GetArguments( pszArgs As StringBuilder, cchMaxPath As Integer)''' <summary>为 Shell 链接对象设置命令行参数</summary>Sub SetArguments( pszArgs As String)''' <summary>检索Shell链接对象的热键</summary>Sub GetHotkey(ByRef pwHotkey As Short)''' <summary>为Shell链接对象设置热键</summary>Sub SetHotkey(wHotkey As Short)''' <summary>检索Shell链接对象的show命令</summary>Sub GetShowCmd(ByRef piShowCmd As Integer)'''  为 Shell 链接对象设置 show 命令.show 命令设置窗口的初始显示状态.</summary>Sub SetShowCmd(iShowCmd As Integer)''' <summary>检索外壳链接对象的图标的位置(路径和索引)</summary>Sub GetIconLocation( pszIconPath As StringBuilder, cchIconPath As Integer, ByRef piIcon As Integer)''' <summary>为Shell链接对象设置图标的位置(路径和索引)</summary>Sub SetIconLocation( pszIconPath As String, iIcon As Integer)''' <summary>设置Shell链接对象的相对路径</summary>Sub SetRelativePath( pszPathRel As String, dwReserved As Integer)''' <summary>尝试找到 Shell 链接的目标,即使它已被移动或重命名 </summary>Sub Resolve(hwnd As IntPtr, fFlags As SLR_FLAGS)''' <summary>设置Shell链接对象的路径和文件名</summary>Sub SetPath( pszFile As String)终端接口<ComImport(), Guid("0000010c-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>公共接口IPersist<PreserveSig()>Sub GetClassID(ByRef pClassID As Guid)终端接口<ComImport(), Guid("0000010b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>公共接口 IPersistFile继承IPersistShadows Sub GetClassID(ByRef pClassID As Guid)<PreserveSig()>函数 IsDirty() 作为整数<PreserveSig()>Sub Load(<[In](), MarshalAs(UnmanagedType.LPWStr)> pszFileName As String, dwMode As UInteger)<PreserveSig()>Sub Save(<[In](), MarshalAs(UnmanagedType.LPWStr)> pszFileName As String, <[In](), MarshalAs(UnmanagedType.Bool)> fRemember As Boolean)<PreserveSig()>Sub SaveCompleted(<[In](), MarshalAs(UnmanagedType.LPWStr)> pszFileName As String)<PreserveSig()>Sub GetCurFile(<[In](), MarshalAs(UnmanagedType.LPWStr)> ppszFileName As String)终端接口const STGM_READ As UInteger = 0将 MAX_PATH 作为整数常量 = 260'来自 ShlGuid.h 的 CLSID_ShellLink<ComImport(), Guid("00021401-0000-0000-C000-000000000046")>公共类 ShellLink结束类公共共享函数 ResolveShortcut(filename As String) As StringDim link As New ShellLink()DirectCast(链接,IPersistFile).加载(文件名,STGM_READ)' TODO:如果我能先解决 hwnd 调用.这处理移动和重命名的文件.' ((IShellLinkW)link).解决(hwnd, 0)Dim sb As New StringBuilder(MAX_PATH)将数据调暗为新 WIN32_FIND_DATAW()DirectCast(link, IShellLinkW).GetPath(sb, sb.Capacity, data, 0)返回 sb.ToString()结束函数结束类

<块引用>

如果您知道一种在驱动器更改时获取完整路径的简单而有效的方法,那么我会接受答案.

但我不想要的是使用旧的 vb6 方法或 vbs 或使用外部应用程序或 For 循环,这就是我尝试使用 apis 的原因,我认为这是提高性能的最佳方式.

解决方案

这仅在连接的网络驱动器上测试(我不会更改本地驱动器来进行测试):

公共共享函数ResolveShortcut(filename As String,hwnd As IntPtr) As StringDim link As New ShellLink()DirectCast(链接,IPersistFile).加载(文件名,STGM_READ)DirectCast(链接,IShellLinkW).解决(hwnd,SLR_FLAGS.SLR_UPDATE)Dim sb As New StringBuilder(MAX_PATH)将数据调暗为新 WIN32_FIND_DATAW()DirectCast(link, IShellLinkW).GetPath(sb, sb.Capacity, data, 0)返回 sb.ToString()结束函数

被称为:

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) 处理 MyBase.LoadDim TargetFilename As String = ResolveShortcut("C:\Test.lnk",Me.Handle)结束子

作为文档,您提到过:

<块引用>

SLR_UPDATE (0x0004)
如果链接对象已更改,请更新其路径和标识符列表.如果设置了 SLR_UPDATE,则不需要调用 IPersistFile::IsDirty 来判断链接对象是否发生了变化.

因此传递表单的句柄(如果需要显示某些对话框,Shell 将需要它)以调用 Resolve 方法并将 SLR_UPDATE 作为第二个参数传递,结果在正确的路径中连接网络驱动器(至少在我的网站).

According to MSDN Information here:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb774952%28v=vs.85%29.aspx

And here:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb774944%28v=vs.85%29.aspx

How I can resolve the fullpath of a link who has changed the driveletter on the shortcut Target?

Windows does this automatically in less than one second, but I need to work with a lot of shortcut files (.lnk files) in my application and I always get the raw old path, I can't resolve the path like windows does.

So for example if I have a shortcut file in this path:

C:\Test.lnk

And the target of the shortcut file is this

D:\Directory\Test.txt

Then If I rename the drive letter of the hard drive from "D:" to "F:" (the harddrive, not the target shortcut) Windows can still recognizing the shortcut as valid and can resolve the shortcut path instantly, so I know this can be possibly to do it with the IShellInterface but the problem is I don't know how to do it.

I don't understand what I need to do to resolve the shortcut, I don't know if I need to use GetPath method or Resolve Method, or both, also I don't know which window handle I need to pass to the resolve method...then is for launch a msgbox in a window or the method will return a string with the reoslved path?, all of this is getting me crazy, I need a code example.

I'm using this Class:

Imports System.Runtime.InteropServices
Imports System.Text

Public Class Form1

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    Dim TargetFilename As String = ResolveShortcut("C:\Test.lnk")
End Sub

<DllImport("shfolder.dll", CharSet:=CharSet.Auto)>
Friend Shared Function SHGetFolderPath(hwndOwner As IntPtr, nFolder As Integer, hToken As IntPtr, dwFlags As Integer, lpszPath As StringBuilder) As Integer
End Function

<Flags()>
Private Enum SLGP_FLAGS
    ''' <summary>Retrieves the standard short (8.3 format) file name</summary>
    SLGP_SHORTPATH = &H1
    ''' <summary>Retrieves the Universal Naming Convention (UNC) path name of the file</summary>
    SLGP_UNCPRIORITY = &H2
    ''' <summary>Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded</summary>
    SLGP_RAWPATH = &H4
End Enum

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Private Structure WIN32_FIND_DATAW
    Public dwFileAttributes As UInteger
    Public ftCreationTime As Long
    Public ftLastAccessTime As Long
    Public ftLastWriteTime As Long
    Public nFileSizeHigh As UInteger
    Public nFileSizeLow As UInteger
    Public dwReserved0 As UInteger
    Public dwReserved1 As UInteger
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)>
    Public cFileName As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=14)>
    Public cAlternateFileName As String
End Structure

<Flags()>
Private Enum SLR_FLAGS
    ''' <summary>
    ''' Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set,
    ''' the high-order word of fFlags can be set to a time-out value that specifies the
    ''' maximum amount of time to be spent resolving the link. The function returns if the
    ''' link cannot be resolved within the time-out duration. If the high-order word is set
    ''' to zero, the time-out duration will be set to the default value of 3,000 milliseconds
    ''' (3 seconds). To specify a value, set the high word of fFlags to the desired time-out
    ''' duration, in milliseconds.
    ''' </summary>
    SLR_NO_UI = &H1
    ''' <summary>Obsolete and no longer used</summary>
    SLR_ANY_MATCH = &H2
    ''' <summary>If the link object has changed, update its path and list of identifiers.
    ''' If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine
    ''' whether or not the link object has changed.</summary>
    SLR_UPDATE = &H4
    ''' <summary>Do not update the link information</summary>
    SLR_NOUPDATE = &H8
    ''' <summary>Do not execute the search heuristics</summary>
    SLR_NOSEARCH = &H10
    ''' <summary>Do not use distributed link tracking</summary>
    SLR_NOTRACK = &H20
    ''' <summary>Disable distributed link tracking. By default, distributed link tracking tracks
    ''' removable media across multiple devices based on the volume name. It also uses the
    ''' Universal Naming Convention (UNC) path to track remote file systems whose drive letter
    ''' has changed. Setting SLR_NOLINKINFO disables both types of tracking.</summary>
    SLR_NOLINKINFO = &H40
    ''' <summary>Call the Microsoft Windows Installer</summary>
    SLR_INVOKE_MSI = &H80
End Enum

''' <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
<ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")>
Private Interface IShellLinkW
    ''' <summary>Retrieves the path and file name of a Shell link object</summary>
    Sub GetPath(<Out(), MarshalAs(UnmanagedType.LPWStr)> pszFile As StringBuilder, cchMaxPath As Integer, ByRef pfd As WIN32_FIND_DATAW, fFlags As SLGP_FLAGS)
    ''' <summary>Retrieves the list of item identifiers for a Shell link object</summary>
    Sub GetIDList(ByRef ppidl As IntPtr)
    ''' <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
    Sub SetIDList(pidl As IntPtr)
    ''' <summary>Retrieves the description string for a Shell link object</summary>
    Sub GetDescription(<Out(), MarshalAs(UnmanagedType.LPWStr)> pszName As StringBuilder, cchMaxName As Integer)
    ''' <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
    Sub SetDescription(<MarshalAs(UnmanagedType.LPWStr)> pszName As String)
    ''' <summary>Retrieves the name of the working directory for a Shell link object</summary>
    Sub GetWorkingDirectory(<Out(), MarshalAs(UnmanagedType.LPWStr)> pszDir As StringBuilder, cchMaxPath As Integer)
    ''' <summary>Sets the name of the working directory for a Shell link object</summary>
    Sub SetWorkingDirectory(<MarshalAs(UnmanagedType.LPWStr)> pszDir As String)
    ''' <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
    Sub GetArguments(<Out(), MarshalAs(UnmanagedType.LPWStr)> pszArgs As StringBuilder, cchMaxPath As Integer)
    ''' <summary>Sets the command-line arguments for a Shell link object</summary>
    Sub SetArguments(<MarshalAs(UnmanagedType.LPWStr)> pszArgs As String)
    ''' <summary>Retrieves the hot key for a Shell link object</summary>
    Sub GetHotkey(ByRef pwHotkey As Short)
    ''' <summary>Sets a hot key for a Shell link object</summary>
    Sub SetHotkey(wHotkey As Short)
    ''' <summary>Retrieves the show command for a Shell link object</summary>
    Sub GetShowCmd(ByRef piShowCmd As Integer)
    ''' <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
    Sub SetShowCmd(iShowCmd As Integer)
    ''' <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
    Sub GetIconLocation(<Out(), MarshalAs(UnmanagedType.LPWStr)> pszIconPath As StringBuilder, cchIconPath As Integer, ByRef piIcon As Integer)
    ''' <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
    Sub SetIconLocation(<MarshalAs(UnmanagedType.LPWStr)> pszIconPath As String, iIcon As Integer)
    ''' <summary>Sets the relative path to the Shell link object</summary>
    Sub SetRelativePath(<MarshalAs(UnmanagedType.LPWStr)> pszPathRel As String, dwReserved As Integer)
    ''' <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
    Sub Resolve(hwnd As IntPtr, fFlags As SLR_FLAGS)
    ''' <summary>Sets the path and file name of a Shell link object</summary>
    Sub SetPath(<MarshalAs(UnmanagedType.LPWStr)> pszFile As String)

End Interface

<ComImport(), Guid("0000010c-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IPersist
    <PreserveSig()>
    Sub GetClassID(ByRef pClassID As Guid)
End Interface


<ComImport(), Guid("0000010b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Public Interface IPersistFile
    Inherits IPersist
    Shadows Sub GetClassID(ByRef pClassID As Guid)
    <PreserveSig()>
    Function IsDirty() As Integer

    <PreserveSig()>
    Sub Load(<[In](), MarshalAs(UnmanagedType.LPWStr)> pszFileName As String, dwMode As UInteger)

    <PreserveSig()>
    Sub Save(<[In](), MarshalAs(UnmanagedType.LPWStr)> pszFileName As String, <[In](), MarshalAs(UnmanagedType.Bool)> fRemember As Boolean)

    <PreserveSig()>
    Sub SaveCompleted(<[In](), MarshalAs(UnmanagedType.LPWStr)> pszFileName As String)

    <PreserveSig()>
    Sub GetCurFile(<[In](), MarshalAs(UnmanagedType.LPWStr)> ppszFileName As String)
End Interface

Const STGM_READ As UInteger = 0
Const MAX_PATH As Integer = 260

' CLSID_ShellLink from ShlGuid.h 
<ComImport(), Guid("00021401-0000-0000-C000-000000000046")> Public Class ShellLink
End Class


Public Shared Function ResolveShortcut(filename As String) As String
    Dim link As New ShellLink()
    DirectCast(link, IPersistFile).Load(filename, STGM_READ)
    ' TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files.  
    ' ((IShellLinkW)link).Resolve(hwnd, 0) 
    Dim sb As New StringBuilder(MAX_PATH)
    Dim data As New WIN32_FIND_DATAW()
    DirectCast(link, IShellLinkW).GetPath(sb, sb.Capacity, data, 0)
    Return sb.ToString()
End Function

End Class

EDIT:

If you know a simple but efficient way to get the fullpath when driveletter changes then just I will accept the answer.

But what I don't want is to use old vb6 methods or vbs or using external apps or For loops, that's why I've tried to use apis I think is the best way for performance.

解决方案

This is only tested with a connected network drive (i won`t change my local driveletters for testing):

Public Shared Function ResolveShortcut(filename As String,hwnd As IntPtr) As String
    Dim link As New ShellLink()
    DirectCast(link, IPersistFile).Load(filename, STGM_READ)

    DirectCast(link, IShellLinkW).Resolve(hwnd, SLR_FLAGS.SLR_UPDATE)

    Dim sb As New StringBuilder(MAX_PATH)
    Dim data As New WIN32_FIND_DATAW()
    DirectCast(link, IShellLinkW).GetPath(sb, sb.Capacity, data, 0)
    Return sb.ToString()
End Function

To be called like:

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    Dim TargetFilename As String = ResolveShortcut("C:\Test.lnk",Me.Handle)
End Sub

As the documentation, you have reffered to, states :

SLR_UPDATE (0x0004)
If the link object has changed, update its path and list of identifiers. If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine whether the link object has changed.

So passing the Handle of the form ( which the Shell will need if it needs to display some dialog ) for calling the Resolve method and passing SLR_UPDATE as second parameter, results for a connected networkdrive in the right path (at least on my site).

这篇关于使用 IShellLink 接口解决已更改驱动器号的快捷方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆