Extract thumbnail for any file in Windows

后端 未结 2 1670
刺人心
刺人心 2020-11-28 06:12

What\'s the most efficient way of extracting thumbnails from any file, not just just images at varying sizes?

I\'ve looked all over, most promising of which was Wind

相关标签:
2条回答
  • 2020-11-28 06:42

    This is an improved version of the (awesome) answer that was accepted. The following was changed:

    • Use unsafe code instead of SetPixel() -> that improves performance drastically
    • remove the whole isAlphaBitmap check. I have no idea why that was implemented in the first place, but I see no reason to determine between two images which contain the same content (I just return the Bitmap with transparency, regardless if that changed something)

      [Flags]
      public enum ThumbnailOptions
      {
          None = 0x00,
          BiggerSizeOk = 0x01,
          InMemoryOnly = 0x02,
          IconOnly = 0x04,
          ThumbnailOnly = 0x08,
          InCacheOnly = 0x10
      }
      
      public class WindowsThumbnailProvider
      {
          private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
      
          [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
          internal static extern int SHCreateItemFromParsingName(
              [MarshalAs(UnmanagedType.LPWStr)] string path,
              // The following parameter is not used - binding context.
              IntPtr pbc,
              ref Guid riid,
              [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
      
          [DllImport("gdi32.dll")]
          [return: MarshalAs(UnmanagedType.Bool)]
          internal static extern bool DeleteObject(IntPtr hObject);
      
          [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
          public static extern unsafe IntPtr memcpy(void* dst, void* src, UIntPtr count);
      
          public static Bitmap GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
          {
              var hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
      
              try
              {
                  // return a System.Drawing.Bitmap from the hBitmap
                  return GetBitmapFromHBitmap(hBitmap);
              }
              finally
              {
                  // delete HBitmap to avoid memory leaks
                  DeleteObject(hBitmap);
              }
          }
      
          public static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap)
          {
              Bitmap bmp = Image.FromHbitmap(nativeHBitmap);
      
              if (Image.GetPixelFormatSize(bmp.PixelFormat) < 32)
                  return bmp;
      
              using (bmp)
                  return CreateAlphaBitmap(bmp, PixelFormat.Format32bppArgb);
          }
      
          public static unsafe Bitmap CreateAlphaBitmap(Bitmap srcBitmap, PixelFormat targetPixelFormat)
          {
              var result = new Bitmap(srcBitmap.Width, srcBitmap.Height, targetPixelFormat);
      
              var bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
              var srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat);
              var destData = result.LockBits(bmpBounds, ImageLockMode.ReadOnly, targetPixelFormat);
      
              var srcDataPtr = (byte*) srcData.Scan0;
              var destDataPtr = (byte*) destData.Scan0;
      
              try
              {
                  for (int y = 0; y <= srcData.Height - 1; y++)
                  {
                      for (int x = 0; x <= srcData.Width - 1; x++)
                      {
                          //this is really important because one stride may be positive and the other negative
                          var position = srcData.Stride * y + 4 * x;
                          var position2 = destData.Stride * y + 4 * x;
      
                          memcpy(destDataPtr + position2, srcDataPtr + position, (UIntPtr) 4);
                      }
                  }
              }
              finally
              {
                  srcBitmap.UnlockBits(srcData);
                  result.UnlockBits(destData);
              }
      
              return result;
          }
      
          private static IntPtr GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
          {
              IShellItem nativeShellItem;
              Guid shellItem2Guid = new Guid(IShellItem2Guid);
              int retCode = SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out nativeShellItem);
      
              if (retCode != 0)
                  throw Marshal.GetExceptionForHR(retCode);
      
              NativeSize nativeSize = new NativeSize();
              nativeSize.Width = width;
              nativeSize.Height = height;
      
              IntPtr hBitmap;
              HResult hr = ((IShellItemImageFactory) nativeShellItem).GetImage(nativeSize, options, out hBitmap);
      
              Marshal.ReleaseComObject(nativeShellItem);
      
              if (hr == HResult.Ok) return hBitmap;
      
              throw Marshal.GetExceptionForHR((int) hr);
          }
      
          [ComImport]
          [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
          [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
          internal interface IShellItem
          {
              void BindToHandler(IntPtr pbc,
                  [MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
                  [MarshalAs(UnmanagedType.LPStruct)] Guid riid,
                  out IntPtr ppv);
      
              void GetParent(out IShellItem ppsi);
              void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
              void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
              void Compare(IShellItem psi, uint hint, out int piOrder);
          }
      
          internal enum SIGDN : uint
          {
              NORMALDISPLAY = 0,
              PARENTRELATIVEPARSING = 0x80018001,
              PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
              DESKTOPABSOLUTEPARSING = 0x80028000,
              PARENTRELATIVEEDITING = 0x80031001,
              DESKTOPABSOLUTEEDITING = 0x8004c000,
              FILESYSPATH = 0x80058000,
              URL = 0x80068000
          }
      
          internal enum HResult
          {
              Ok = 0x0000,
              False = 0x0001,
              InvalidArguments = unchecked((int) 0x80070057),
              OutOfMemory = unchecked((int) 0x8007000E),
              NoInterface = unchecked((int) 0x80004002),
              Fail = unchecked((int) 0x80004005),
              ElementNotFound = unchecked((int) 0x80070490),
              TypeElementNotFound = unchecked((int) 0x8002802B),
              NoObject = unchecked((int) 0x800401E5),
              Win32ErrorCanceled = 1223,
              Canceled = unchecked((int) 0x800704C7),
              ResourceInUse = unchecked((int) 0x800700AA),
              AccessDenied = unchecked((int) 0x80030005)
          }
      
          [ComImport]
          [Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
          [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
          internal interface IShellItemImageFactory
          {
              [PreserveSig]
              HResult GetImage(
                  [In, MarshalAs(UnmanagedType.Struct)] NativeSize size,
                  [In] ThumbnailOptions flags,
                  [Out] out IntPtr phbm);
          }
      
          [StructLayout(LayoutKind.Sequential)]
          internal struct NativeSize
          {
              private int width;
              private int height;
      
              public int Width
              {
                  set { width = value; }
              }
      
              public int Height
              {
                  set { height = value; }
              }
          }
      
          [StructLayout(LayoutKind.Sequential)]
          public struct RGBQUAD
          {
              public byte rgbBlue;
              public byte rgbGreen;
              public byte rgbRed;
              public byte rgbReserved;
          }
      }
      
    0 讨论(0)
  • 2020-11-28 06:55

    Some time ago I wrote a ThumbnailProvider that loads a thumbnail from the Win API. Supports transparent images. This is the implementation:

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;
    using System.IO;
    
        namespace ThumbnailGenerator
        {
            [Flags]
            public enum ThumbnailOptions
            {
                None = 0x00,
                BiggerSizeOk = 0x01,
                InMemoryOnly = 0x02,
                IconOnly = 0x04,
                ThumbnailOnly = 0x08,
                InCacheOnly = 0x10,
            }
    
            public class WindowsThumbnailProvider
            {
                private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
    
                [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
                internal static extern int SHCreateItemFromParsingName(
                    [MarshalAs(UnmanagedType.LPWStr)] string path,
                    // The following parameter is not used - binding context.
                    IntPtr pbc,
                    ref Guid riid,
                    [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
    
                [DllImport("gdi32.dll")]
                [return: MarshalAs(UnmanagedType.Bool)]
                internal static extern bool DeleteObject(IntPtr hObject);
    
                [ComImport]
                [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
                [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
                internal interface IShellItem
                {
                    void BindToHandler(IntPtr pbc,
                        [MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
                        [MarshalAs(UnmanagedType.LPStruct)]Guid riid,
                        out IntPtr ppv);
    
                    void GetParent(out IShellItem ppsi);
                    void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
                    void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
                    void Compare(IShellItem psi, uint hint, out int piOrder);
                };
    
                internal enum SIGDN : uint
                {
                    NORMALDISPLAY = 0,
                    PARENTRELATIVEPARSING = 0x80018001,
                    PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
                    DESKTOPABSOLUTEPARSING = 0x80028000,
                    PARENTRELATIVEEDITING = 0x80031001,
                    DESKTOPABSOLUTEEDITING = 0x8004c000,
                    FILESYSPATH = 0x80058000,
                    URL = 0x80068000
                }
    
                internal enum HResult
                {
                    Ok = 0x0000,
                    False = 0x0001,
                    InvalidArguments = unchecked((int)0x80070057),
                    OutOfMemory = unchecked((int)0x8007000E),
                    NoInterface = unchecked((int)0x80004002),
                    Fail = unchecked((int)0x80004005),
                    ElementNotFound = unchecked((int)0x80070490),
                    TypeElementNotFound = unchecked((int)0x8002802B),
                    NoObject = unchecked((int)0x800401E5),
                    Win32ErrorCanceled = 1223,
                    Canceled = unchecked((int)0x800704C7),
                    ResourceInUse = unchecked((int)0x800700AA),
                    AccessDenied = unchecked((int)0x80030005)
                }
    
                [ComImportAttribute()]
                [GuidAttribute("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
                [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
                internal interface IShellItemImageFactory
                {
                    [PreserveSig]
                    HResult GetImage(
                    [In, MarshalAs(UnmanagedType.Struct)] NativeSize size,
                    [In] ThumbnailOptions flags,
                    [Out] out IntPtr phbm);
                }
    
                [StructLayout(LayoutKind.Sequential)]
                internal struct NativeSize
                {
                    private int width;
                    private int height;
    
                    public int Width { set { width = value; } }
                    public int Height { set { height = value; } }
                };
    
                [StructLayout(LayoutKind.Sequential)]
                public struct RGBQUAD
                {
                    public byte rgbBlue;
                    public byte rgbGreen;
                    public byte rgbRed;
                    public byte rgbReserved;
                }
    
                public static Bitmap GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
                {
                    IntPtr hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
    
                    try
                    {
                        // return a System.Drawing.Bitmap from the hBitmap
                        return GetBitmapFromHBitmap(hBitmap);
                    }
                    finally
                    {
                        // delete HBitmap to avoid memory leaks
                        DeleteObject(hBitmap);
                    }
                }
    
                public static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap)
                {
                    Bitmap bmp = Bitmap.FromHbitmap(nativeHBitmap);
    
                    if (Bitmap.GetPixelFormatSize(bmp.PixelFormat) < 32)
                        return bmp;
    
                    return CreateAlphaBitmap(bmp, PixelFormat.Format32bppArgb);
                }
    
                public static Bitmap CreateAlphaBitmap(Bitmap srcBitmap, PixelFormat targetPixelFormat)
                {
                    Bitmap result = new Bitmap(srcBitmap.Width, srcBitmap.Height, targetPixelFormat);
    
                    Rectangle bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
    
                    BitmapData srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat);
    
                    bool isAlplaBitmap = false;
    
                    try
                    {
                        for (int y = 0; y <= srcData.Height - 1; y++)
                        {
                            for (int x = 0; x <= srcData.Width - 1; x++)
                            {
                                Color pixelColor = Color.FromArgb(
                                    Marshal.ReadInt32(srcData.Scan0, (srcData.Stride * y) + (4 * x)));
    
                                if (pixelColor.A > 0 & pixelColor.A < 255)
                                {
                                    isAlplaBitmap = true;
                                }
    
                                result.SetPixel(x, y, pixelColor);
                            }
                        }
                    }
                    finally
                    {
                        srcBitmap.UnlockBits(srcData);
                    }
    
                    if (isAlplaBitmap)
                    {
                        return result;
                    }
                    else
                    {
                        return srcBitmap;
                    }
                }
    
                private static IntPtr GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
                {
                    IShellItem nativeShellItem;
                    Guid shellItem2Guid = new Guid(IShellItem2Guid);
                    int retCode = SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out nativeShellItem);
    
                    if (retCode != 0)
                        throw Marshal.GetExceptionForHR(retCode);
    
                    NativeSize nativeSize = new NativeSize();
                    nativeSize.Width = width;
                    nativeSize.Height = height;
    
                    IntPtr hBitmap;
                    HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(nativeSize, options, out hBitmap);
    
                    Marshal.ReleaseComObject(nativeShellItem);
    
                    if (hr == HResult.Ok) return hBitmap;
    
                    throw Marshal.GetExceptionForHR((int)hr);
                }
            }
        }
    

    Then you can use it in the following way:

    int THUMB_SIZE = 256;
    Bitmap thumbnail = WindowsThumbnailProvider.GetThumbnail(
       fileName, THUMB_SIZE, THUMB_SIZE, ThumbnailOptions.None);
    

    Remember that you need to Dispose() the bitmap after using it.

    0 讨论(0)
提交回复
热议问题