Flow.Launcher/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs

100 lines
3.1 KiB
C#
Raw Normal View History

using System;
using System.Runtime.InteropServices;
using System.IO;
2024-12-10 12:44:28 +00:00
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
2024-12-10 12:44:28 +00:00
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
using Windows.Win32.Graphics.Gdi;
2020-04-21 09:12:17 +00:00
namespace Flow.Launcher.Infrastructure.Image
{
2024-12-10 12:44:28 +00:00
/// <summary>
/// Subclass of <see cref="Windows.Win32.UI.Shell.SIIGBF"/>
/// </summary>
[Flags]
public enum ThumbnailOptions
{
None = 0x00,
BiggerSizeOk = 0x01,
InMemoryOnly = 0x02,
IconOnly = 0x04,
ThumbnailOnly = 0x08,
InCacheOnly = 0x10,
}
public class WindowsThumbnailProvider
{
// Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows
2024-12-10 12:44:28 +00:00
private static readonly Guid GUID_IShellItem = typeof(IShellItem).GUID;
2024-12-10 12:44:28 +00:00
private static readonly HRESULT S_ExtractionFailed = (HRESULT)0x8004B200;
public static BitmapSource GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
{
2024-12-10 12:44:28 +00:00
HBITMAP hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
try
{
return Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
finally
{
// delete HBitmap to avoid memory leaks
2024-12-10 12:44:28 +00:00
PInvoke.DeleteObject(hBitmap);
}
}
2024-12-10 12:44:28 +00:00
private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
{
2024-12-10 12:44:28 +00:00
var retCode = PInvoke.SHCreateItemFromParsingName(
fileName,
null,
GUID_IShellItem,
out var nativeShellItem);
2024-12-10 12:44:28 +00:00
if (retCode != HRESULT.S_OK)
throw Marshal.GetExceptionForHR(retCode);
2024-12-10 12:44:28 +00:00
if (nativeShellItem is not IShellItemImageFactory imageFactory)
{
2024-12-10 12:44:28 +00:00
Marshal.ReleaseComObject(nativeShellItem);
2024-12-11 03:08:57 +00:00
nativeShellItem = null;
2024-12-10 12:44:28 +00:00
throw new InvalidOperationException("Failed to get IShellItemImageFactory");
}
2024-12-10 12:44:28 +00:00
SIZE size = new SIZE
{
cx = width,
cy = height
};
2024-12-10 12:44:28 +00:00
HBITMAP hBitmap = default;
try
{
2024-12-10 12:44:28 +00:00
try
{
imageFactory.GetImage(size, (SIIGBF)options, &hBitmap);
}
catch (COMException ex) when (ex.HResult == S_ExtractionFailed && options == ThumbnailOptions.ThumbnailOnly)
{
// Fallback to IconOnly if ThumbnailOnly fails
imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap);
}
}
finally
{
2024-12-11 03:08:57 +00:00
if (nativeShellItem != null)
{
Marshal.ReleaseComObject(nativeShellItem);
}
}
2024-12-10 12:44:28 +00:00
return hBitmap;
}
}
2024-12-10 12:44:28 +00:00
}