I am trying to iterate through some files and get their shell icons; for this I use DirectoryInfo.EnumerateFileSystemInfossome P / Invoke to call Win32 . But the combination of the two seems to corrupt memory somewhere inside, which leads to ugly crashes. SHGetFileInfo
I shot down my code on two similar tests, both of which, it would seem, for no reason. If I do not call DirectoryInfo.EnumerateFileSystemInfos, there is no accident; if I don’t call SHGetFileInfo, there is no accident. Please note that I removed the actual use of the objects FileSystemInfoin my code, as I can make it reproduce simply by iterating over them and re-querying the text file icon. But why?
Here are my complete, minimal test cases. Run them in the VS debugger to make sure that optimization is not enabled:
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
{
public class Program
{
public static void Main()
{
var sources = new List<BitmapSource>();
var dir = new DirectoryInfo(@".");
ulong iteration = 0;
while (true)
{
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);
if (result != IntPtr.Zero && shFileInfo.hIcon != IntPtr.Zero)
{
var bmpSource = Imaging.CreateBitmapSourceFromHIcon(
shFileInfo.hIcon,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
sources.Add(bmpSource);
}
GC.Collect();
++iteration;
}
}
}
public static void OtherBugRepro()
{
var sources = new List<BitmapSource>();
var dir = new DirectoryInfo(@".");
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);
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);
}
}
Can anyone spot an error? Any help is appreciated!
source
share