在优化的编译代码

时间:2017-02-13 17:22:57

标签: vb.net optimization crash

我有一个在VS2015和.Net 4.5.2

中运行的大型vb.net x86项目

当它在没有优化的情况下编译并在调试中运行时,它可以正常工作。但是,如果我在发布模式下编译并运行它并且启用了优化,那么我会在相同的无关代码行中获得各种异常。我尝试在发布模式下调试它,但断点是不可靠的。调试它的行为似乎也修改了异常。此外,如果我更改代码(例如将MsgBox放入显示信息),那么问题就会消失。例如,我将一个Arraylist更改为List(Of Control),问题不再发生在之前的位置,但现在转移到别处。

我在不同时间收到以下所有内容: AccessViolationException, NullReferenceException(深入.Net类中的某个地方) 和FatalExecutionEngineError

AccessViolationException中的异常详细说明除了“这通常表明其他内存已损坏”之外什么也没有说明。堆栈跟踪没有意义,并且没有描述它在无效内存地址处的内容。

我也找不到任何有关编译器实际优化的有意义的细节 - 一个解决方案可能是关闭优化但我不明白这样做的好处/负面影响。

优化是否不可靠?怎么可能尝试并确定发生了什么?

我们使用的唯一非托管代码是一些调用以获取与文件扩展名相关的图标 - 然后将其克隆到托管对象中并销毁非托管内存。这是相当标准的,自1.1和4.5.2以来使用相同的API已有10年没有出现过这种情况。

我无法创建一个重现问题的小项目

这是我们用于提取图标的代码,因为它是我现在唯一的潜在原因。它是从其他地方借来的,我无法确定它是否应该如此好:

Public Class IconExtractor

<Flags()> Private Enum SHGFI
    SmallIcon = &H1
    LargeIcon = &H0
    Icon = &H100
    DisplayName = &H200
    Typename = &H400
    SysIconIndex = &H4000
    UseFileAttributes = &H10
End Enum

<StructLayout(LayoutKind.Sequential)>
Private Structure SHFILEINFO
    Public hIcon As IntPtr
    Public iIcon As Integer
    Public dwAttributes As Integer
    <MarshalAs(UnmanagedType.LPStr, SizeConst:=260)> Public szDisplayName As String
    <MarshalAs(UnmanagedType.LPStr, SizeConst:=80)> Public szTypeName As String

    Public Sub New(ByVal B As Boolean)
        hIcon = IntPtr.Zero
        iIcon = 0
        dwAttributes = 0
        szDisplayName = vbNullString
        szTypeName = vbNullString
    End Sub
End Structure

Private Declare Auto Function SHGetFileInfo Lib "shell32" (
ByVal pszPath As String, ByVal dwFileAttributes As Integer,
ByRef psfi As SHFILEINFO, ByVal cbFileInfo As Integer, ByVal uFlags As SHGFI) As Integer

<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
End Function

Public Shared Sub GetIconsForFile(ByVal rstrFileName As String, ByRef rzSmallIcon As Icon, ByRef rzLargeIcon As Icon)
    Dim zFileInfo As New SHFILEINFO(True)
    Dim cbSizeInfo As Integer = Marshal.SizeOf(zFileInfo)
    Dim flags As SHGFI = SHGFI.Icon Or SHGFI.UseFileAttributes Or SHGFI.SmallIcon
    SHGetFileInfo(rstrFileName, 256, zFileInfo, cbSizeInfo, flags)
    ' Use clone so we can destroy immediately
    rzSmallIcon = DirectCast(Icon.FromHandle(zFileInfo.hIcon).Clone, Icon)
    DestroyIcon(zFileInfo.hIcon)

    zFileInfo = New SHFILEINFO(True)
    cbSizeInfo = Marshal.SizeOf(zFileInfo)
    flags = SHGFI.Icon Or SHGFI.UseFileAttributes Or SHGFI.LargeIcon
    SHGetFileInfo(rstrFileName, 256, zFileInfo, cbSizeInfo, flags)
    ' Use clone so we can destroy immediately
    rzLargeIcon = DirectCast(Icon.FromHandle(zFileInfo.hIcon).Clone, Icon)
    DestroyIcon(zFileInfo.hIcon)
End Sub
End Class

1 个答案:

答案 0 :(得分:0)

我偶然发现了解决方案。

我正在阅读SHGETFILEINFO的这个文档 https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx 并发现它在备注中说:你应该从后台线程中调用这个函数。如果不这样做可能会导致UI停止响应

不清楚为什么你应该从后台线程调用它,也不清楚“停止响应”实际上可能表现为什么。

然而,这似乎很可能导致问题,所以我重构在一个单独的线程下执行api调用。这当然似乎有效。互联网上的许多SHGETFILEINFO示例似乎都没有考虑单独的线程要求。

我在这里重现了整个重构代码:

Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Threading

''' <summary>
''' Retrieves the small and large icons registered for a filename based on the file's extension
''' </summary>
Public Class FileIcons

    Private mFileName As String
    Private mSmallIconHandle As IntPtr
    Private mSmallIcon As Icon
    Private mLargeIconHandle As IntPtr
    Private mLargeIcon As Icon

    Public Sub New(ByVal rFileName As String)
        mFileName = rFileName

        Dim t As New Thread(AddressOf GetIconsForFile)
        t.SetApartmentState(ApartmentState.STA)
        t.Start()
        t.Join()

        ' Use clone so we can destroy immediately
        mSmallIcon = DirectCast(Icon.FromHandle(mSmallIconHandle).Clone, Icon)
        DestroyIcon(mSmallIconHandle)

        ' Use clone so we can destroy immediately
        mLargeIcon = DirectCast(Icon.FromHandle(mLargeIconHandle).Clone, Icon)
        DestroyIcon(mLargeIconHandle)
    End Sub

    Public ReadOnly Property SmallIcon As Icon
        Get
            Return mSmallIcon
        End Get
    End Property

    Public ReadOnly Property LargeIcon As Icon
        Get
            Return mLargeIcon
        End Get
    End Property

    Private Sub GetIconsForFile()
        ' Go and extract the small and large icons
        ' Full filename must be < MAX_PATH - which is 260 chars in .Net (apparently) though a file path/length of 256 also causes an error. 
        ' Otherwise SHGetFileInfo gets nothing, Icon.FromHandle then gives "System.ArgumentException: The Win32 handle you passed to Icon is invalid or of the wrong type."
        ' This needs to be stopped in the calling code, or the resulting error trapped 

        Dim zFileInfo As New SHFILEINFO(True)
        Dim cbSizeInfo As Integer = Marshal.SizeOf(zFileInfo)
        Dim flags As SHGFI = SHGFI.Icon Or SHGFI.UseFileAttributes Or SHGFI.SmallIcon
        SHGetFileInfo(mFileName, 256, zFileInfo, cbSizeInfo, flags)
        mSmallIconHandle = zFileInfo.hIcon

        zFileInfo = New SHFILEINFO(True)
        cbSizeInfo = Marshal.SizeOf(zFileInfo)
        flags = SHGFI.Icon Or SHGFI.UseFileAttributes Or SHGFI.LargeIcon
        SHGetFileInfo(mFileName, 256, zFileInfo, cbSizeInfo, flags)
        mLargeIconHandle = zFileInfo.hIcon
    End Sub

#Region "WinAPI"
    <Flags()> Private Enum SHGFI
        SmallIcon = &H1
        LargeIcon = &H0
        Icon = &H100
        DisplayName = &H200
        Typename = &H400
        SysIconIndex = &H4000
        UseFileAttributes = &H10
    End Enum

    <StructLayout(LayoutKind.Sequential)>
    Private Structure SHFILEINFO
        Public hIcon As IntPtr
        Public iIcon As Integer
        Public dwAttributes As Integer
        <MarshalAs(UnmanagedType.LPStr, SizeConst:=260)> Public szDisplayName As String
        <MarshalAs(UnmanagedType.LPStr, SizeConst:=80)> Public szTypeName As String

        Public Sub New(ByVal B As Boolean)
            hIcon = IntPtr.Zero
            iIcon = 0
            dwAttributes = 0
            szDisplayName = vbNullString
            szTypeName = vbNullString
        End Sub
    End Structure

    Private Declare Auto Function SHGetFileInfo Lib "shell32" (
        ByVal pszPath As String,
        ByVal dwFileAttributes As Integer,
        ByRef psfi As SHFILEINFO,
        ByVal cbFileInfo As Integer,
        ByVal uFlags As SHGFI) As Integer

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
    End Function
#End Region

End Class