我有一个在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
答案 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