使用IShellLink接口解析更改其驱动器号的快捷方式

时间:2013-09-19 03:47:24

标签: .net vb.net api winapi shortcuts

根据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,或者该方法将返回一个带有重新路径的字符串?,所有这些让我发疯,我需要一个代码示例。

我正在使用这个类:

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
  

编辑:

如果你知道一个简单而有效的方法来获得当时的电子邮件更改时的完整路径,那么我将接受答案。

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

1 个答案:

答案 0 :(得分:1)

仅使用连接的网络驱动器测试(我不会更改我的本地驱动器进行测试):

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

被称为:

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

正如您所提到的文档所述:

  

SLR_UPDATE(0x0004)
      如果链接对象已更改,请更新其路径和标识符列表。如果设置了SLR_UPDATE,则无需调用IPersistFile :: IsDirty来确定链接对象是否已更改。

因此,为了调用Resolve方法并将SLR_UPDATE作为第二个参数传递,传递窗体的句柄(Shell需要显示一些对话框时需要),导致连接的网络驱动器在正确的路径中(至少在我的路径上)位点)。