我正在尝试迭代一些文件并获取它们的shell图标;为了实现这一点,我使用DirectoryInfo.EnumerateFileSystemInfos
和一些P / Invoke来调用Win32 SHGetFileInfo
函数.但是两者的结合似乎在内部某处破坏了内存,导致了丑陋的崩溃.
我把我的代码归结为两个类似的测试用例,这两个测试用例似乎都没有理由崩溃.如果我不调用DirectoryInfo.EnumerateFileSystemInfos,则不会出现崩溃;如果我不调用SHGetFileInfo,则不会出现崩溃.请注意,我已经删除了我的代码中FileSystemInfo对象的实际使用,因为我可以通过迭代它们并一遍又一遍地询问文本文件图标来重现它.但为什么?
这是我完整的,最小的测试用例.在VS调试器下运行它们以确保未启用任何优化:
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
namespace IconCrashRepro
{
// Compile for .NET 4 (I'm using 4.5.1).
// Also seems to fail in 3.5 with GetFileSystemInfos() instead of EnumerateFileSystemInfos()
public class Program
{
// Compile for .NET 4 (I'm using 4.5.1)
public static void Main()
{
// Keep a list of the objects we generate so
// that they're not garbage collected right away
var sources = new List<BitmapSource>();
// Any directory seems to do the trick, so long
// as it's not empty. Within VS, '.' should be
// the Debug folder
var dir = new DirectoryInfo(@".");
// Track the number of iterations, just to see
ulong iteration = 0;
while (true)
{
// This is where things get interesting -- without the EnumerateFileSystemInfos,
// the bug does not appear. Without the call to SHGetFileInfo, the bug also
// does not appear. It seems to be the combination that causes problems.
var infos = dir.EnumerateFileSystemInfos().ToList();
Debug.Assert(infos.Count > 0);
foreach (var info in infos)
{
var shFileInfo = new SHFILEINFO();
var result = SHGetFileInfo(".txt", (uint)FileAttributes.Normal, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
//var result = SHGetFileInfo(info.FullName, (uint)info.Attributes, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
if (result != IntPtr.Zero && shFileInfo.hIcon != IntPtr.Zero)
{
var bmpSource = Imaging.CreateBitmapSourceFromHIcon(
shFileInfo.hIcon,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
sources.Add(bmpSource);
// Originally I was releasing the handle, but even if
// I don't the bug occurs!
//DestroyIcon(shFileInfo.hIcon);
}
// Execution fails during Collect; if I remove the
// call to Collect, execution fails later during
// CreateBitmapSourceFromHIcon (it calls
// AddMemoryPressure internally which I suspect
// results in a collect at that point).
GC.Collect();
++iteration;
}
}
}
public static void OtherBugRepro()
{
// Rename this to Main() to run.
// Removing any single line from this method
// will stop it from crashing -- including the
// empty if and the Debug.Assert!
var sources = new List<BitmapSource>();
var dir = new DirectoryInfo(@".");
var infos = dir.EnumerateFileSystemInfos().ToList();
Debug.Assert(infos.Count > 0);
// Crashes on the second iteration -- says that
// `infos` has been modified during loop execution!!
foreach (var info in infos)
{
var shFileInfo = new SHFILEINFO();
var result = SHGetFileInfo(".txt", (uint)FileAttributes.Normal, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
if (result != IntPtr.Zero && shFileInfo.hIcon != IntPtr.Zero)
{
if (sources.Count == 1000) { }
}
}
}
[StructLayout(LayoutKind.Sequential)]
private struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}
private const uint SHGFI_ICON = 0x100;
private const uint SHGFI_LARGEICON = 0x0;
private const uint SHGFI_SMALLICON = 0x1;
private const uint SHGFI_USEFILEATTRIBUTES = 0x10;
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SHGetFileInfo([MarshalAs(UnmanagedType.LPWStr)] string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyIcon(IntPtr hIcon);
}
}
谁能发现这个bug?任何帮助表示赞赏!
解决方法:
您正在调用函数的Unicode版本,但是传递结构的ANSI版本.您需要在SHFILEINFO结构声明中指定CharSet.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
标签:c,crash,pinvoke,net-4-5 来源: https://codeday.me/bug/20190624/1278586.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。