C# Get Thumbnail from File via Windows API

C# get thumbnail from file via windows api

Ran across this today -- it's a few months old, but it got the job done for me (on Win7, extracting thumbnails on MPEG-4 files):

  • Source : https://github.com/dbarros/WindowsAPICodePack
  • Nuget : https://www.nuget.org/packages/WindowsAPICodePack-Shell

Code:

ShellFile shellFile = ShellFile.FromFilePath(pathToYourFile);
Bitmap shellThumb = shellFile.Thumbnail.ExtraLargeBitmap;

Hope it helps!

Extract thumbnail for any file in Windows

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.

How to get thumbnails from a file

There are different types of thumbnails that can be retrieved from Windows.

  1. Picture
  2. Song Album Art
  3. Document Icon
  4. Folder
  5. File Group
  6. Single Item

Microsoft has a nice sample project called FileThumbnails that lets you play with each type. This project was updated for Windows10 and VS 2019 in March 2020. Although it's a universal windows project instead of winforms.

After playing with the different modes I found the one you are after for Solid Edge files is #6.


internal class FileExtensions
{
public static readonly string[] SEfiles = new string[] { ".dft", ".par", ".asm" };
}

FileOpenPicker openPicker = new FileOpenPicker();
foreach (string extension in FileExtensions.SEfiles)
{
openPicker.FileTypeFilter.Add(extension);
}

StorageFile file = await openPicker.PickSingleFileAsync();
if (file != null)
{
const ThumbnailMode thumbnailMode = ThumbnailMode.SingleItem;

bool fastThumbnail = FastThumbnailCheckBox.IsChecked.Value;
ThumbnailOptions thumbnailOptions = ThumbnailOptions.UseCurrentScale;
if (fastThumbnail)
{
thumbnailOptions |= ThumbnailOptions.ReturnOnlyIfCached;
}

using (StorageItemThumbnail thumbnail = await file.GetScaledImageAsThumbnailAsync(thumbnailMode, size, thumbnailOptions))
{
if (thumbnail != null)
{
MainPage.DisplayResult(ThumbnailImage, OutputTextBlock, thumbnailMode.ToString(), size, file, thumbnail, false);
}
else
{
rootPage.NotifyUser(Errors.NoThumbnail, NotifyType.StatusMessage);
}
}
}

public static void DisplayResult(Image image, TextBlock textBlock, string thumbnailModeName, uint size, IStorageItem item, StorageItemThumbnail thumbnail, bool isGroup)
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(thumbnail);
image.Source = bitmapImage;

textBlock.Text = String.Format("ThumbnailMode.{0}\n"
+ "{1} used: {2}\n"
+ "Requested size: {3}\n"
+ "Returned size: {4}x{5}",
thumbnailModeName,
isGroup ? "Group" : item.IsOfType(StorageItemTypes.File) ? "File" : "Folder",
item.Name,
size,
thumbnail.OriginalWidth,
thumbnail.OriginalHeight);
}

Result Example:

Sample Image

Get thumbnail of Network DWG file by Windows API Code Pack

There must be something wrong happening on your side because the following code works here:

using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Shell;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
string fileName = @"\\PC\Users\Public\bitmap.bmp";
ShellFile shellFile = ShellFile.FromFilePath(fileName);
ShellThumbnail thumbnail = shellFile.Thumbnail;
var pictureBox = new PictureBox
{
Image = thumbnail.Bitmap,
Dock = DockStyle.Fill
};
Controls.Add(pictureBox);
}
}
}

Sample Image

Check the following:

  • try with another extension to see if it affects all of them or not
  • try to re-register thumbnails handlers, just a guess but SageThumbs might fix this by registering it and unregistering it as the default handler for extensions
  • if that matters, I've used the Code Pack I've myself pushed to NuGet : https://www.nuget.org/packages/WindowsAPICodePack-Shell/ (not sure that might be the issue since I haven't changed anything)

VB.NET get any file thumbnail in picturebox as an image image programmatically

Try the following which is adapted from C# get thumbnail from file via windows api

Download/install NuGet package Microsoft-WindowsAPICodePack-Shell

Imports Microsoft.WindowsAPICodePack.Shell
Imports System.IO
...

Private Function GetThumbnailBytes(filename As String, desiredHeight As Integer) As Byte()
Dim thumbnailBytes As Byte()

Using sfile As ShellFile = ShellFile.FromFilePath(filename)
Dim thumbBmp As Bitmap = sfile.Thumbnail.ExtraLargeBitmap

'compute new width
Dim Ratio As Double = CDbl(thumbBmp.Width / thumbBmp.Height)
Dim height As Integer = desiredHeight
Dim width As Integer = CInt(height / Ratio)

'resize
Using resizedBmp As Bitmap = New Bitmap(thumbBmp, width, height)
Using ms As MemoryStream = New MemoryStream()
resizedBmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
thumbnailBytes = ms.ToArray()
End Using
End Using
End Using

Return thumbnailBytes
End Function

Private Sub btnRun_Click(sender As Object, e As EventArgs) Handles btnRun.Click

Using ofd As OpenFileDialog = New OpenFileDialog()
If ofd.ShowDialog() = DialogResult.OK Then

Dim thumbnailBytes = GetThumbnailBytes(ofd.FileName, 60)
Dim thumbnail = GetThumbnail(ofd.FileName, 60)

Using ms As MemoryStream = New MemoryStream(thumbnailBytes)
PictureBox1.Image = Image.FromStream(ms)
PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage
End Using
End If
End Using
End Sub

Resources

  • C# get thumbnail from file via windows api


Related Topics



Leave a reply



Submit