启动某些 .lnk 文件并使用 Process.Start 和 SHGetFileInfo() 获取其图标时出错

Error when launching some .lnk files and getting their icon using Process.Start and SHGetFileInfo()

由于一些奇怪和未知的原因,我在启动一些快捷方式和获取它们的图标时遇到问题,使用以下方法:

Public Shared Sub Launch(itemToLaunch As String)
        Process.Start(itemToLaunch)
End Sub



Public Function GetShellIcon(ByVal path As String) As Icon

        Dim info As SHFILEINFO = New SHFILEINFO()
        Dim retval As IntPtr = SHGetFileInfo(path, 0, info, Marshal.SizeOf(info), SHGFI_ICON Or SHGFI_SMALLICON Or SHGFI_LARGEICON)

        If retval = IntPtr.Zero Then
            Return New Icon(GetType(Control), "Error.ico")
        End If

        Dim cargt() As Type = {GetType(IntPtr)}
        Dim ci As ConstructorInfo = GetType(Icon).GetConstructor(BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, cargt, Nothing)
        Dim cargs() As Object = {info.IconHandle}
        Dim icon As Icon = CType(ci.Invoke(cargs), Icon)

        Return icon
    End Function

 <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
    Private Structure SHFILEINFO
        Public IconHandle As IntPtr
        Public IconIndex As Integer
        Public Attributes As UInteger
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)>
        Public DisplayString As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=80)>
        Public TypeName As String
    End Structure

    Private Declare Auto Function SHGetFileInfo Lib "Shell32.dll" (path As String, attributes As Integer, ByRef info As SHFILEINFO, infoSize As Integer, flags As Integer) As IntPtr

    Public Const SHGFI_ICON = &H100
    Public Const SHGFI_SMALLICON = &H1
    Public Const SHGFI_LARGEICON = &H0         ' Large icon

这些方法几乎适用于任何项目,但有时在尝试执行快捷方式文件时,它们会在 System.dll 中向我发送 System.ComponentModel.Win32Exception,并且在这些相同的文件上,获取它们的图标.

它给了我以下消息(由 Process.Start 给出,使用带有 ErrorDialog=True 的 ProcessStartInfo 参数调用):

此错误与 .lnk 文件的路径不正确指向不存在的文件时引发的错误不同:

例如,您可以这样重现此问题:

定位到Windows 7安装以下文件:
C:\Program Files\DVD Maker\DVDMaker.exe(原生 Windows 7)

C:\Program Files\WinRAR\WinRAR.exe(v5.0 64位,但我猜这与另一个版本具有相同的效果)

C:\Program Files\Windows NT\Accessories\wordpad.exe(原生 Windows 7)

创建一个 Visual basic 项目,将 4 个 PictureBoxes 放到一个 Form 上并命名它们:

还有一些标签可以帮助您自己。

然后copy/paste下面的代码变成Form代码window:

Imports System.Reflection
Imports System.Runtime.InteropServices

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Me.ExeOrigPictureBox.Tag = "C:\Program Files\WinRAR\WinRAR.exe"
        Me.ExeCopyPictureBox.Tag = "C:\Users\Moi\Desktop\WinRAR.exe"

        Me.LnkOrigPictureBox.Tag = "C:\Users\Moi\Desktop\WinRAR.exe linkorig.lnk"
        Me.LnkCopyPictureBox.Tag = "C:\Users\Moi\Desktop\WinRAR.exe linkcopy.lnk"

        Me.ExeOrigPictureBox.Image = GetShellIcon(Me.ExeOrigPictureBox.Tag).ToBitmap
        Me.ExeCopyPictureBox.Image = GetShellIcon(Me.ExeCopyPictureBox.Tag).ToBitmap

        Me.LnkOrigPictureBox.Image = GetShellIcon(Me.LnkOrigPictureBox.Tag).ToBitmap
        Me.LnkCopyPictureBox.Image = GetShellIcon(Me.LnkCopyPictureBox.Tag).ToBitmap
    End Sub

    Private Sub ExeOrigPictureBox_Click(sender As Object, e As EventArgs) Handles ExeOrigPictureBox.Click, ExeCopyPictureBox.Click, LnkOrigPictureBox.Click, LnkCopyPictureBox.Click
        Dim pBox As PictureBox = DirectCast(sender, PictureBox)

        Dim pi As ProcessStartInfo = New ProcessStartInfo
        pi.FileName = pBox.Tag
        pi.ErrorDialog = True
        Process.Start(pi)
    End Sub
End Class

Module Shell32
    Public Function GetShellIcon(ByVal path As String) As Icon

        Dim info As SHFILEINFO = New SHFILEINFO()
        Dim retval As IntPtr = SHGetFileInfo(path, 0, info, Marshal.SizeOf(info), SHGFI_ICON Or SHGFI_SMALLICON Or SHGFI_LARGEICON)

        If retval = IntPtr.Zero Then
            Return New Icon(GetType(Control), "Error.ico")
        End If

        Dim cargt() As Type = {GetType(IntPtr)}
        Dim ci As ConstructorInfo = GetType(Icon).GetConstructor(BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, cargt, Nothing)
        Dim cargs() As Object = {info.IconHandle}
        Dim icon As Icon = CType(ci.Invoke(cargs), Icon)

        Return icon
    End Function

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
    Private Structure SHFILEINFO
        Public IconHandle As IntPtr
        Public IconIndex As Integer
        Public Attributes As UInteger
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)>
        Public DisplayString As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=80)>
        Public TypeName As String
    End Structure

    Private Declare Auto Function SHGetFileInfo Lib "Shell32.dll" (path As String, attributes As Integer, ByRef info As SHFILEINFO, infoSize As Integer, flags As Integer) As IntPtr

    Public Const SHGFI_ICON = &H100
    Public Const SHGFI_SMALLICON = &H1
    Public Const SHGFI_LARGEICON = &H0         ' Large icon
End Module

然后执行。

您将获得以下内容:

单击任何显示良好的图标启动 WinRar 应用程序。
单击显示错误的图标会显示此错误:

用像 "C:\Users\Moi\Desktop\WinRARdontexistshere.exe linkorig.lnk" 这样的错误路径更改 Me.LnkOrigPictureBox.Tag 的值并做同样的事情会显示另一个视觉效果和错误(如预期的那样):

这不适用于 DVDMaker.exe

但是 wordpad.exe、图标和应用程序启动一切正常。

(我已经测试了 lower/uppercase 的情况,看它是否干扰,但这不是问题)

我在其他一些应用程序上注意到了这个问题,但不了解其原因,例如:

和其他标准 Windows 应用程序。

当 copy/pasting 有问题的文件路径 C:\Users\Moi\Desktop\WinRAR.exe linkorig.lnk 到 Windows 资源管理器标题栏时,WinRAR.exe 应用程序启动。

当然同样的事情是我double-click.lnk 文件。

当 copy/pasted 进入 Windows-R 命令 window.

时,它也能很好地启动

如果通过从放置在 C:\Users\Moi\Desktop\ 文件夹中的 command-line window 中键入 WinRAR.lnk 来调用,也会启动。

我 运行 到 Windows 7 64 位。该应用程序是使用 Visual Studio Express 2015 编译的。我以管理员身份登录(在 Windows 安装时创建的唯一默认帐户)。运行编译后的应用程序 "as an administrator" 不会改变任何东西。

我尝试使用以下配置,但没有成功:

        Dim info As ProcessStartInfo = New ProcessStartInfo(--- here the path ---)
        info.CreateNoWindow = False
        info.UseShellExecute = False
        info.RedirectStandardError = True
        info.RedirectStandardOutput = True
        info.RedirectStandardInput = True
        Dim whatever As Process = Process.Start(info)

如何解决这个启动问题,以及这些文件的图标检索问题?

哇...我很幸运地找到了答案,当我看到,用在网上找到的一些例子进行了一些测试,使用时也出现了图标问题和尝试使用相应文件时的错误消息一个标准 OpenFileDialog。我怀疑 .Net 框架中存在错误。解决方案就在这附近,我仍然不太了解其深层原因。

问题如下:

该项目在项目设置中默认定义为 运行 with .Net Framework 4.5

我使用 Framework 4
将其切换为 运行 运行应用程序:没有问题

我使用 Framework 4.5
将其切换回 运行 完全没有问题。