Extract thumbnail for any file in Windows

后端 未结 2 1669
刺人心
刺人心 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;
          }
      }
      

提交回复
热议问题