一个右键菜单引发的崩溃
本帖最后由 gufengaoyue 于 2024-5-22 14:15 编辑我在VB.NET中,用sharpshell做了一个右键菜单的类库。
其中用System.Drawing中的image等来处理图标作为右键菜单的图标来显示。
但经过测试,大概使用了72次后(右键点击72次左右),菜单就崩溃了(右键菜单变黑变窄,没有文字图标,只有纯黑或纯白)。
调试时,在崩溃的时候出现以下提示,崩溃后,原本正常工作的System.Drawing下的类如Image,Icon,等等,都变成nothing(失去了System.Drawing引用):
“explorer.exe”(CLR v4.0.30319: DefaultDomain): 已加载“C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Drawing.resources\v4.0_4.0.0.0_zh-Hans_b03f5f7f11d50a3a\System.Drawing.resources.dll”。模块已生成,不包含符号。
引发的异常:“System.ArgumentException”(位于 System.Drawing.dll 中)
**icon** 是 Nothing。
加载图标 ,我用过了异步、同步,或者缓存等方式,但都不能改变崩溃的结果。
subMenu.Image = Image.FromFile(img_path)
subMenu.Image = Await IconManager.LoadIconAsync(img_path)
这时候,只有重启Explorer才恢复正常。但再经过72次后,又会崩溃。(似乎C#也这样)
请教大佬,这是什么原因?是System.Drawing本身的bug,还是类库或Explorer本身的限制?
重要的一点:如果不使用图标,就是不用System.Drawing,Explorer就不会崩溃。
恳请知道的大佬不吝赐教,万分感激!{:1_893:}
本帖最后由 刺心 于 2024-5-22 17:39 编辑
出现这个问题可能是由于资源泄漏,特别是在使用 System.Drawing 和处理图标时。每次加载图像时,如果没有正确释放资源,可能会导致内存和资源的耗尽,从而导致崩溃。以下是一些可能的解决方法和优化建议:
1. 确保正确释放资源
确保在使用完图像和图标后,调用 Dispose 方法释放资源。例如:
Dim img As Image = Nothing
Try
img = Image.FromFile(img_path)
subMenu.Image = img
Finally
If img IsNot Nothing Then
img.Dispose()
End If
End Try
2. 使用 Using 语句
Using 语句可以自动处理资源的释放:
Using img As Image = Image.FromFile(img_path)
subMenu.Image = img
End Using
对于异步方法,也应确保正确释放资源:
Using icon As Icon = Await IconManager.LoadIconAsync(img_path)
Using bmp As Bitmap = icon.ToBitmap()
subMenu.Image = CType(bmp.Clone(), Image)
End Using
End Using
3. 缓存图标
如果图标是固定的,可以考虑将图标缓存起来,而不是每次都重新加载。这可以减少资源的使用频率:
Dim iconCache As New Dictionary(Of String, Image)
Function GetIcon(img_path As String) As Image
If Not iconCache.ContainsKey(img_path) Then
iconCache(img_path) = Image.FromFile(img_path)
End If
Return iconCache(img_path)
End Function
subMenu.Image = GetIcon(img_path)
4. 检查图像格式和大小
确保图像格式和大小合适,以避免不必要的内存占用。
5. 监控资源使用
使用工具监控 Explorer 的内存和资源使用情况,确保找出确切的资源泄漏点。例如,使用 Process Explorer 或者内存分析工具。
6. 处理图像转换
在处理图像转换时,也要注意资源释放:
Using icon As Icon = Await IconManager.LoadIconAsync(img_path)
Using bmp As Bitmap = icon.ToBitmap()
subMenu.Image = CType(bmp.Clone(), Image)
End Using
End Using
7. 避免频繁加载和释放
如果可能,减少频繁的加载和释放操作。例如,保持菜单项在整个应用程序的生命周期中使用相同的图标。
通过以上方法,你应该能够减少资源泄漏的风险,避免 Explorer 因资源耗尽而崩溃。如果问题仍然存在,建议进一步检查 System.Drawing 或相关库的使用方式,或者考虑使用其他图形库,如 System.Drawing.Common 或 SkiaSharp,以提高稳定性和性能。
8.使用 System.Drawing.Common
System.Drawing.Common 是 .NET Core 和 .NET 5+ 的跨平台版本,但其使用方法与原来的 System.Drawing 类似。
安装
首先,通过 NuGet 安装 System.Drawing.Common:
Install-Package System.Drawing.Common
使用 Using 语句来确保资源的释放:
Imports System.Drawing
Public Sub SetMenuImage(subMenu As ToolStripMenuItem, imgPath As String)
Using img As Image = Image.FromFile(imgPath)
subMenu.Image = CType(img.Clone(), Image)
End Using
End Sub
对于异步方法:
Imports System.Drawing
Public Async Function LoadIconAsync(imgPath As String) As Task(Of Image)
Return Await Task.Run(Function()
Using icon As New Icon(imgPath)
Return CType(icon.ToBitmap().Clone(), Image)
End Using
End Function)
End Function
Public Async Sub SetMenuImageAsync(subMenu As ToolStripMenuItem, imgPath As String)
Using img As Image = Await LoadIconAsync(imgPath)
subMenu.Image = img
End Using
End Sub
使用 SkiaSharp
SkiaSharp 是一个高性能的跨平台图形库。
安装
首先,通过 NuGet 安装 SkiaSharp:
Install-Package SkiaSharp
使用 SkiaSharp 加载和处理图像:
Imports SkiaSharp
Public Sub SetMenuImage(subMenu As ToolStripMenuItem, imgPath As String)
Using inputStream As New SKFileStream(imgPath)
Using skBitmap As SKBitmap = SKBitmap.Decode(inputStream)
Using skImage As SKImage = SKImage.FromBitmap(skBitmap)
Using skData As SKData = skImage.Encode()
Using ms As New MemoryStream(skData.ToArray())
subMenu.Image = Image.FromStream(ms)
End Using
End Using
End Using
End Using
End Using
End Sub
对于异步方法:
Imports SkiaSharp
Public Async Function LoadImageAsync(imgPath As String) As Task(Of Image)
Return Await Task.Run(Function()
Using inputStream As New SKFileStream(imgPath)
Using skBitmap As SKBitmap = SKBitmap.Decode(inputStream)
Using skImage As SKImage = SKImage.FromBitmap(skBitmap)
Using skData As SKData = skImage.Encode()
Using ms As New MemoryStream(skData.ToArray())
Return Image.FromStream(ms)
End Using
End Using
End Using
End Using
End Using
End Function)
End Function
Public Async Sub SetMenuImageAsync(subMenu As ToolStripMenuItem, imgPath As String)
Using img As Image = Await LoadImageAsync(imgPath)
subMenu.Image = img
End Using
End Sub
刺心 发表于 2024-5-22 17:35
出现这个问题可能是由于资源泄漏,特别是在使用 System.Drawing 和处理图标时。每次加载图像时,如果没有正 ...
非常感谢您的细心回复!
试过using和dispose,但无法加载图标,感觉可能释放掉以后就不能加载。
异步和缓存也都试过了,都不行。
感觉就是System.Drawing的毛病,只要不用SystemDrawing就没事。
没试过其他库,找个机会再试试。 右键菜单不错,可以讲下怎么制作的吗 无敌小儿 发表于 2024-5-23 09:13
右键菜单不错,可以讲下怎么制作的吗
用的sharpshell做的,你可以参考下官网,上面有范例
https://github.com/dwmkerr/sharpshell gufengaoyue 发表于 2024-5-22 18:03
非常感谢您的细心回复!
试过using和dispose,但无法加载图标,感觉可能释放掉以后就不能加载。
异步和 ...
你可以看看其他人写的试试。或者用一下其他的库 刺心 发表于 2024-5-23 11:34
你可以看看其他人写的试试。或者用一下其他的库
之前也有人写过这样的右键菜单,我试了一下,同样会崩溃。
菜单文字和图标没了,变黑框了 gufengaoyue 发表于 2024-5-23 11:40
之前也有人写过这样的右键菜单,我试了一下,同样会崩溃。
菜单文字和图标没了,变黑框了
学习到了。谢谢大佬
页:
[1]