using System.Runtime.InteropServices; using System.Windows.Media; using System.Windows.Media.Imaging; namespace DeskCore; /// Holt das Icon (bzw. Vorschaubild) einer Datei so, wie der Explorer es anzeigt. public static class ShellIcon { public static ImageSource? Get(string path, int size = 64) { IntPtr hbm = IntPtr.Zero; try { SHCreateItemFromParsingName(path, IntPtr.Zero, typeof(IShellItemImageFactory).GUID, out var factory); int hr = factory.GetImage(new SIZE { cx = size, cy = size }, 0, out hbm); Marshal.ReleaseComObject(factory); if (hr != 0 || hbm == IntPtr.Zero) return null; return FromHBitmap(hbm); } catch { return null; } finally { if (hbm != IntPtr.Zero) DeleteObject(hbm); } } // CreateBitmapSourceFromHBitmap verliert den Alphakanal, daher Pixel selbst kopieren. private static BitmapSource? FromHBitmap(IntPtr hbm) { GetObject(hbm, Marshal.SizeOf(), out BITMAP bm); int w = bm.bmWidth, h = Math.Abs(bm.bmHeight); if (w <= 0 || h <= 0) return null; var bmi = new BITMAPINFOHEADER { biSize = Marshal.SizeOf(), biWidth = w, biHeight = -h, // top-down biPlanes = 1, biBitCount = 32, }; var bits = new byte[w * h * 4]; IntPtr hdc = GetDC(IntPtr.Zero); try { if (GetDIBits(hdc, hbm, 0, (uint)h, bits, ref bmi, 0) == 0) return null; } finally { ReleaseDC(IntPtr.Zero, hdc); } bool hasAlpha = false; for (int i = 3; i < bits.Length; i += 4) if (bits[i] != 0) { hasAlpha = true; break; } var src = BitmapSource.Create(w, h, 96, 96, hasAlpha ? PixelFormats.Pbgra32 : PixelFormats.Bgr32, null, bits, w * 4); src.Freeze(); return src; } [ComImport, Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellItemImageFactory { [PreserveSig] int GetImage(SIZE size, int flags, out IntPtr phbm); } [StructLayout(LayoutKind.Sequential)] private struct SIZE { public int cx, cy; } [StructLayout(LayoutKind.Sequential)] private struct BITMAP { public int bmType, bmWidth, bmHeight, bmWidthBytes; public ushort bmPlanes, bmBitsPixel; public IntPtr bmBits; } [StructLayout(LayoutKind.Sequential)] private struct BITMAPINFOHEADER { public int biSize, biWidth, biHeight; public ushort biPlanes, biBitCount; public int biCompression, biSizeImage, biXPelsPerMeter, biYPelsPerMeter, biClrUsed, biClrImportant; } [DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)] private static extern void SHCreateItemFromParsingName(string path, IntPtr pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IShellItemImageFactory ppv); [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr h); [DllImport("gdi32.dll")] private static extern int GetObject(IntPtr h, int size, out BITMAP bm); [DllImport("gdi32.dll")] private static extern int GetDIBits(IntPtr hdc, IntPtr hbm, uint start, uint lines, byte[] bits, ref BITMAPINFOHEADER bmi, uint usage); [DllImport("user32.dll")] private static extern IntPtr GetDC(IntPtr hwnd); [DllImport("user32.dll")] private static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc); }