Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# get thumbnail from file via windows api

Windows explorer has the ability to show thumbnails of files. These thumbnails are provided by core and third-party shell extensions.

I know how to extend the shell to provide thumbnails to Windows.

What I want to do is retrieve the thumbnail image from any file on the system via the shell using C#. Is this possible?

Essentially, I'm writing a custom file browser and I want to show thumbnails, and can't possibly parse every file on the planet to make my own thumbnails.

Clarification: Many answers seem to be centered around web page thumbnails, or scaling an image. But that's not at all what I'm looking for. What I want is to ask Windows for the thumbnail representation of these file types: .DOC, .PDF, .3DM, .DWG... and mabye about a dozen more. I don't want to parse, render, and make thumbnails myself, because Windows already knows how.

The code I posted as an answer actually works... maybe it can be simplified and cleaned up a bit.

like image 614
Brian Gillespie Avatar asked Sep 17 '09 15:09

Brian Gillespie


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

Is C language easy?

Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.

What is C full form?

Full form of C is “COMPILE”. One thing which was missing in C language was further added to C++ that is 'the concept of CLASSES'.


2 Answers

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!

like image 82
Christian Nunciato Avatar answered Sep 21 '22 22:09

Christian Nunciato


Microsoft Office Thumbnails in SharePoint at http://msdn.microsoft.com/en-us/library/aa289172(VS.71).aspx. is exactly what you want. (I had no trouble converting the VB.NET code into C# using http://www.developerfusion.com/tools/convert/vb-to-csharp/.)

Regarding the code you posted in your answer, Thumbnail Extraction Using the Shell at http://www.vbaccelerator.com/home/net/code/libraries/Shell_Projects/Thumbnail_Extraction/article.asp is the original article the code is from. I believe nearly all IExtractImage samples (you find searching) are based on this article's code, due to naming convention, comments, and so on, that are carried from the original. Since this article dates from April 2003, it carries some non-standard (non-.NET) coding conventions. I did some basic testing, and there are garbage collection issues, freeing unmanaged resource issues, and other clean-up issues. Therefore, I make a strong recommendation to avoid the code in that article. Moreover, the code is structured is such a way to make maintenance difficult.

There is a clean simplier version on MSDN, dated July 2005, called Microsoft Office Thumbnails in SharePoint at http://msdn.microsoft.com/en-us/library/aa289172(VS.71).aspx. This code and the article's code share similarities, which leads me to believe the Thumbnail Extraction Using the Shell article is the basis for the SharePoint article. The VB.NET version of GetThumbnailImage ignores the longestEdge parameter, but the C++ version uses it and documents the use of the ORIGSIZE and QUALITY flags. In addition, the code illustrates how to use .NET's FreeCoTaskMem instead of the Shell's IMalloc and SHGetMalloc.

IExtractImage works with files, folders, and other namespace objects. The MSDN code works with hidden files, whereas, the vbAccelerator code needs then SHCONTF_INCLUDEHIDDEN added to the EnumObjects call. In addition, the vbAccelerator enumerates over the shell folder's objects looking for the specified file, which seems a waste of time. This may have been needed to find the correct "relative" PIDL that is used for the GetUIObjectOf call.


ShellThumbnail (Work-In-Progress)

Complete sample project at http://cid-7178d2c79ba0a7e3.office.live.com/self.aspx/.Public/ShellThumbnail.zip.

Imports System.Runtime.InteropServices  Namespace Shell      ''' <summary>     ''' Generates a thumbnail of a folder's picture or a file's image.     ''' </summary>     ''' <remarks>This is the "Folder's Picture" and not the "Folder's Icon"! Use SHGetFileInfo to generate the thumbnail for a folder's icon or for a file that does not have a thumbnail handler.</remarks>     ''' <reference>Microsoft Office Thumbnails in SharePoint at http://msdn.microsoft.com/en-us/library/aa289172%28VS.71%29.aspx.</reference>     Public Class ShellThumbnail          'TODO - Work out the details for image size and IEIFLAG handling.  #Region " Determining Thumbnail Size and Quality [documentation] "          'http://support.microsoft.com/kb/835823         'Determining Thumbnail Size and Quality         'Browse to HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer. Create or modify two DWORDs called ThumbnailSize and ThumbnailQuality. For ThumbnailSize set the value in pixels, with the default being 96. For ThumbnailQuality set the value as a number that represents the percentage quality between 50 and 100.           'http://www.pctools.com/guides/registry/detail/1066/ (modified)         '  User Key: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer]         'System Key: [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer]         'Value Name: ThumbnailSize, ThumbnailQuality         ' Data Type: REG_DWORD (DWORD Value)         'Value Data: Size in pixels (32-255), Quality Percentage (50-100)           'Microsoft® Windows® XP Registry Guide          'Jerry Honeycutt          '09/11/2002          'Microsoft Press         'http://www.microsoft.com/mspress/books/sampchap/6232.aspx#118         '<H3><I><A name=118></A>Thumbnails</I></H3>The Thumbnails category controls the          'quality of thumbnails in Windows Explorer. Table 5-10 describes the values for          'Image Quality and Size. Create values that you don't see in the registry. The          'default value for <CODE>ThumbnailQuality</CODE> is <CODE>0x5A</CODE>. The          'default value for <CODE>ThumbnailSize</CODE> is <CODE>0x60</CODE>. Keep in mind          'that higher quality and larger thumbnails require more disk space, which is not          'usually a problem, but they also take longer to display. Changing the quality          'does not affect thumbnails that already exist on the file system.         '<P><B>Table 5-10 </B><I>Values in Thumbnails</I>         '<P>         '<TABLE border=0 cellSpacing=1 cellPadding=4 width="100%">         '<TBODY>         '<TR>         '<TD bgColor=#999999 vAlign=top><B>Setting</B></TD>         '<TD bgColor=#999999 vAlign=top><B>Name</B></TD>         '<TD bgColor=#999999 vAlign=top><B>Type</B></TD>         '<TD bgColor=#999999 vAlign=top><B>Data</B></TD></TR>         '<TR>         '<TD bgColor=#cccccc          'vAlign=top><CODE><B>HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer</B></CODE></TD>         '<TD bgColor=#cccccc vAlign=top> </TD>         '<TD bgColor=#cccccc vAlign=top> </TD>         '<TD bgColor=#cccccc vAlign=top> </TD></TR>         '<TR>         '<TD bgColor=#cccccc vAlign=top><CODE>Image Quality</CODE></TD>         '<TD bgColor=#cccccc vAlign=top><CODE>ThumbnailQuality</CODE></TD>         '<TD bgColor=#cccccc vAlign=top><CODE>REG_DWORD</CODE></TD>         '<TD bgColor=#cccccc vAlign=top><CODE>0x32 - 0x64</CODE></TD></TR>         '<TR>         '<TD bgColor=#cccccc vAlign=top><CODE>Size (pixels)</CODE></TD>         '<TD bgColor=#cccccc vAlign=top><CODE>ThumbnailSize</CODE></TD>         '<TD bgColor=#cccccc vAlign=top><CODE>REG_DWORD</CODE></TD>         '<TD bgColor=#cccccc vAlign=top><CODE>0x20 - 0xFF</CODE></TD></TR></TBODY></TABLE></P>  #End Region          Public Shared ReadOnly DefaultThumbnailSize As New System.Drawing.Size(96, 96)          Public Const DefaultColorDepth As Integer = 32          ''' <summary>         ''' Used to request an image from an object, such as an item in a Shell folder.         ''' </summary>         ''' <param name="path">An absolute path to a file or folder.</param>         Public Shared Function ExtractImage(ByVal path As String) As Bitmap             Return ExtractImage(path, System.Drawing.Size.Empty, DefaultColorDepth, 0)         End Function          ''' <summary>         ''' Used to request an image from an object, such as an item in a Shell folder.         ''' </summary>         ''' <param name="path">An absolute path to a file or folder.</param>         ''' <param name="size"></param>         Public Shared Function ExtractImage(ByVal path As String, ByVal size As System.Drawing.Size) As Bitmap             Return ExtractImage(path, size, DefaultColorDepth, 0)         End Function          ''' <summary>         ''' Used to request an image from an object, such as an item in a Shell folder.         ''' </summary>         ''' <param name="path">An absolute path to a file or folder.</param>         ''' <param name="size"></param>         ''' <param name="recommendedColorDepth">The recommended color depth in units of bits per pixel. The default is 32.</param>         Public Shared Function ExtractImage(ByVal path As String, ByVal size As System.Drawing.Size, ByVal recommendedColorDepth As Integer) As Bitmap             Return ExtractImage(path, size, recommendedColorDepth, 0)         End Function          ''' <summary>         ''' Used to request an image from an object, such as an item in a Shell folder.         ''' </summary>         ''' <param name="path">An absolute path to a file or folder.</param>         ''' <param name="size"></param>         ''' <param name="recommendedColorDepth">The recommended color depth in units of bits per pixel. The default is 32.</param>         Private Shared Function ExtractImage(ByVal path As String, ByVal size As System.Drawing.Size, ByVal recommendedColorDepth As Integer, ByVal flags As IEIFLAG) As Bitmap             Dim oResult As Bitmap = Nothing              Dim oDesktopFolder As IShellFolder = Nothing             Dim hParentIDL As IntPtr             Dim hIDL As IntPtr              Dim oParentFolder As IShellFolder             Dim hParentFolder As IntPtr             Dim oExtractImage As IExtractImage             Dim hExtractImage As IntPtr              'Divide the file name into a path and file/folder name.             Dim sFolderName As String = System.IO.Path.GetDirectoryName(path)             Dim sBaseName As String = System.IO.Path.GetFileName(path)              'Get the desktop IShellFolder.             If SHGetDesktopFolder(oDesktopFolder) <> Missico.Win32.S_OK Then                 Throw New System.Runtime.InteropServices.COMException             End If              'Get the parent folder for the specified path.             oDesktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, sFolderName, 0, hParentIDL, 0)             oDesktopFolder.BindToObject(hParentIDL, IntPtr.Zero, ShellGUIDs.IID_IShellFolder, hParentFolder)             oParentFolder = CType(Marshal.GetTypedObjectForIUnknown(hParentFolder, GetType(IShellFolder)), IShellFolder)              'Get the file/folder's IExtractImage             oParentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, sBaseName, 0, hIDL, 0)             oParentFolder.GetUIObjectOf(IntPtr.Zero, 1, New IntPtr() {hIDL}, ShellGUIDs.IID_IExtractImage, IntPtr.Zero, hExtractImage)              'Free the pidls. The Runtime Callable Wrappers (RCW) should automatically release the COM objects.             Marshal.FreeCoTaskMem(hParentIDL)             Marshal.FreeCoTaskMem(hIDL)              Marshal.FinalReleaseComObject(oParentFolder)             Marshal.FinalReleaseComObject(oDesktopFolder)              If hExtractImage = IntPtr.Zero Then                  'There is no handler for this file, which is odd. I believe we should default the file's type icon.                 Debug.WriteLine(String.Format("There is no thumbnail for the specified file '{0}'.", path), "ShellThumbnail.ExtractImage")              Else                  oExtractImage = CType(Marshal.GetTypedObjectForIUnknown(hExtractImage, GetType(IExtractImage)), IExtractImage)                  'Set the size and flags                 Dim oSize As Missico.Win32.SIZE 'must specify a size                 Dim iFlags As IEIFLAG = flags Or IEIFLAG.IEIFLAG_ORIGSIZE Or IEIFLAG.IEIFLAG_QUALITY Or IEIFLAG.IEIFLAG_ASPECT                  If size.IsEmpty Then                      oSize.cx = DefaultThumbnailSize.Width                     oSize.cy = DefaultThumbnailSize.Height                  Else                      oSize.cx = size.Width                     oSize.cy = size.Height                  End If                   Dim hBitmap As IntPtr                 Dim sPath As New System.Text.StringBuilder(Missico.Win32.MAX_PATH, Missico.Win32.MAX_PATH)                   oExtractImage.GetLocation(sPath, sPath.Capacity, 0, oSize, recommendedColorDepth, iFlags)                  'if the specified path is to a folder then IExtractImage.Extract fails.                  Try                      oExtractImage.Extract(hBitmap)                  Catch ex As System.Runtime.InteropServices.COMException                      'clear the handle since extract failed                     hBitmap = IntPtr.Zero                      Debug.WriteLine(String.Format("There is no thumbnail for the specified folder '{0}'.", path), "ShellThumbnail.ExtractImage")                  Finally                      Marshal.FinalReleaseComObject(oExtractImage)                  End Try                  If Not hBitmap.Equals(IntPtr.Zero) Then                      'create the image from the handle                     oResult = System.Drawing.Bitmap.FromHbitmap(hBitmap)                      'dump the properties to determine what kind of bitmap is returned                     'Missico.Diagnostics.ObjectDumper.Write(oResult)                      'Tag={ }                     'PhysicalDimension={Width=96, Height=96}                     'Size={Width=96, Height=96}                     'Width=96                     'Height=96                     'HorizontalResolution=96                     'VerticalResolution=96                     'Flags=335888                     'RawFormat={ }                     'PixelFormat=Format32bppRgb                     'Palette={ }                     'FrameDimensionsList=...                     'PropertyIdList=...                     'PropertyItems=...                      Missico.Win32.DeleteObject(hBitmap) 'release the handle                  End If              End If              Return oResult         End Function      End Class  End Namespace 
like image 42
AMissico Avatar answered Sep 19 '22 22:09

AMissico