Page 1 of 2

Get the Shell Thumbnail for files

Posted: Wed Mar 29, 2006 10:14 pm
by freak
This is just a little something i played around with. Maybe it is usefull to somebody.
It allows to easily get a preview image from the shell like the ones explorer displays.
This requires PB4 beta8 to run.

It behaves just like CreateImage(). Supports #PB_Any as well as static numbers.
The function fails (returns 0) if there is no preview supported for the file type.

Here is the procedure:

Code: Select all

Procedure GetShellThumbnail(FileName$, Image, Width, Height, Depth = #PB_Image_DisplayFormat)
  Protected Result = 0, ImageResult
  Protected Desktop.IShellFolder, Folder.IShellFolder
  Protected Extract.IExtractImage
  Protected *pidlFolder.ITEMIDLIST, *pidlFile.ITEMIDLIST  
  Protected Priority, Flags, Bitmap = 0, size.SIZE

  If SHGetDesktopFolder_(@Desktop) >= 0
    If Desktop\ParseDisplayName(#Null, #Null, GetPathPart(FileName$), #Null, @*pidlFolder, #Null) = #S_OK
      If Desktop\BindToObject(*pidlFolder, #Null, ?IID_IShellFolder, @Folder) = #S_OK
        If Folder\ParseDisplayName(#Null, #Null, GetFilePart(FileName$) , #Null, @*pidlFile, #Null) = #S_OK
          If Folder\GetUIObjectOf(#Null, 1, @*pidlFile, ?IID_IExtractImage, 0, @Extract) = #S_OK

            ImageResult = CreateImage(Image, Width, Height, Depth)
            If ImageResult
              If Image = #PB_Any
                Image = ImageResult
              EndIf   
              If Depth = #PB_Image_DisplayFormat 
                Depth = ImageDepth(Image)
              EndIf
                                 
              size\cx = Width
              size\cy = Height
              
              If Extract\GetLocation(Space(#MAX_PATH), #MAX_PATH, @Priority, @size, Depth, @Flags) >= 0                
                If Extract\Extract(@Bitmap) >= 0 And Bitmap 
                                 
                  If StartDrawing(ImageOutput(Image))
                    DrawImage(Bitmap, 0, 0)
                    StopDrawing()                    
                    Result = ImageResult
                  EndIf
                  
                  DeleteObject_(Bitmap)
                EndIf
              EndIf                
              Extract\Release()
            EndIf
            
            If Result = 0
              FreeImage(Image)
            EndIf            
          EndIf
                    
          CoTaskMemFree_(*pidlFile)
        EndIf                       
        Folder\Release()
      EndIf     
      CoTaskMemFree_(*pidlFolder)      
    EndIf    
    Desktop\Release()
  EndIf

  ProcedureReturn Result
  
  DataSection  
    IID_IShellFolder: ; {000214E6-0000-0000-C000-000000000046}
      Data.l $000214E6
      Data.w $0000, $0000
      Data.b $C0, $00, $00, $00, $00, $00, $00, $46
  
    IID_IExtractImage: ; {BB2E617C-0920-11D1-9A0B-00C04FC2D6C1}
      Data.l $BB2E617C
      Data.w $0920, $11D1
      Data.b $9A, $0B, $00, $C0, $4F, $C2, $D6, $C1
  EndDataSection  
EndProcedure
A simple example:

Code: Select all

#ExplorerGadget = 0
#ImageGadget    = 1
#Image = 0

If OpenWindow(0, 0, 0, 500, 400, "Shell Thumbnails", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  CreateGadgetList(WindowID(0))
  ExplorerTreeGadget(#ExplorerGadget, 5, 5, 190, 390, "C:\")
  ImageGadget(#ImageGadget, 225, 100, 250, 200, 0, #PB_Image_Border)

  Repeat
    Event = WaitWindowEvent()
    
    If Event = #PB_Event_Gadget And EventGadget() = #ExplorerGadget And EventType() = #PB_EventType_Change      
    
      If GetShellThumbnail(GetGadgetText(#ExplorerGadget), #Image, 250, 200)
        SetGadgetState(#ImageGadget, ImageID(#Image))
      Else
        SetGadgetState(#ImageGadget, 0)
      EndIf
      
    EndIf
    
  Until Event = #PB_Event_CloseWindow
EndIf
End
Here is a more complex example. It uses SHGetFileInfo_() to get an Icon for the file
if no preview is available to create a nice folder preview:

Code: Select all

#ExplorerGadget = 0
#ScrollGadget   = 1
#ProgressGadget = 2

Structure File
  FileName$
  Text$
  Image.l  
  Gadget.l ; Its a ContainerGadget, so the others inside are freed as well
EndStructure

NewList File.File()

If OpenWindow(0, 0, 0, 800, 600, "Shell Thumbnails", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  CreateGadgetList(WindowID(0))
  ExplorerTreeGadget(#ExplorerGadget, 5, 5, 240, 590, "C:\", #PB_Explorer_NoFiles)
  ScrollAreaGadget(#ScrollGadget, 250, 5, 545, 570, 520, 10, 10)
  CloseGadgetList()
  ProgressBarGadget(#ProgressGadget, 250, 580, 545, 15, 0, 100)

  Repeat
    Event = WaitWindowEvent()
    
    If Event = #PB_Event_Gadget And EventGadget() = #ExplorerGadget And EventType() = #PB_EventType_Change      
    
      ; Clear the old content of the display
      ;
      ForEach File()
        FreeGadget(File()\Gadget)
        FreeImage(File()\Image)
      Next File()
      ClearList(File())
      
      ; Creating the thumbnail may take a little time for certain file types
      ; (for example big PowerPoint presentations), especially when there are lots
      ; of files in a folder. So the text information is read first (which is quite fast)
      ; and the images are created later with a progressbar to indicate the progress
      ;      
      Count = 0
      If ExamineDirectory(0, GetGadgetText(#ExplorerGadget), "*.*")
        While NextDirectoryEntry(0)
          If DirectoryEntryType(0) = #PB_DirectoryEntry_File
            AddElement(File())
            File()\FileName$ = GetGadgetText(#ExplorerGadget) + DirectoryEntryName(0)
            File()\Text$ = "Name: " + File()\FileName$ + Chr(13) 
            File()\Text$ + "Size: " + Str(DirectoryEntrySize(0)) + " Bytes" + Chr(13)
            File()\Text$ + FormatDate("Created: %mm/%dd/%yyyy", DirectoryEntryDate(0, #PB_Date_Created)) + Chr(13)
            File()\Text$ + FormatDate("Modified: %mm/%dd/%yyyy", DirectoryEntryDate(0, #PB_Date_Modified)) + Chr(13)
            File()\Text$ + FormatDate("Accessed: %mm/%dd/%yyyy", DirectoryEntryDate(0, #PB_Date_Accessed))            
            Count + 1
          EndIf
        Wend
      EndIf  
      
      If Count > 0
              
        OpenGadgetList(#ScrollGadget)
        SetGadgetState(#ProgressGadget, 0) 
        While WindowEvent(): Wend ; Refresh the display so it looks better
        
        ForEach File()
          index = ListIndex(File())
          
          ; Create the thumbnail
          File()\Image = GetShellThumbnail(File()\FileName$, #PB_Any, 100, 80)            
          
          ; If no image could be created, we try to read the Icon for the file type and
          ; show that
          ;
          If File()\Image = 0
            File()\Image = CreateImage(#PB_Any, 100, 80)
            If File()\Image And StartDrawing(ImageOutput(File()\Image))                
              Box(0, 0, 100, 80, $FFFFFF)
              If SHGetFileInfo_(@File()\FileName$, 0, @info.SHFILEINFO, SizeOf(SHFILEINFO), #SHGFI_ICON|#SHGFI_LARGEICON)
                DrawImage(info\hIcon, 34, 24)
                DestroyIcon_(info\hIcon)
              EndIf                
              StopDrawing()
            EndIf                           
          EndIf
          
          File()\Gadget = ContainerGadget(#PB_Any, 5, 5+index*90, 510, 80, #PB_Container_Flat)
            ImageGadget(#PB_Any, 0, 0, 100, 80, ImageID(File()\Image))          
            TextGadget(#PB_Any, 105, 5, 400, 70, File()\Text$)
          CloseGadgetList()
        
          ; Update the Gadget states and refresh the display
          ;
          SetGadgetState(#ProgressGadget, (index * 100)/Count)
          SetGadgetAttribute(#ScrollGadget, #PB_ScrollArea_InnerHeight, (index+1)*90 + 10)  
          While WindowEvent(): Wend
        Next File()
        
        CloseGadgetList()       
      EndIf    
 
      SetGadgetState(#ProgressGadget, 100)    
      SetGadgetAttribute(#ScrollGadget, #PB_ScrollArea_InnerHeight, Count*90 + 10)      
      
    EndIf
    
  Until Event = #PB_Event_CloseWindow
EndIf
End

Posted: Wed Mar 29, 2006 10:34 pm
by dell_jockey
Danke Timo!

Posted: Thu Mar 30, 2006 7:45 am
by dige
Wow!! That rocks. Thank you fr34k!

Posted: Thu Mar 30, 2006 10:10 am
by Dare2
Awesome. Thank you!

Posted: Thu Mar 30, 2006 4:25 pm
by Trond
I think there is an error somewhere, because no preview is shown for my deneba canvas files when I use the simple example, but the preview is shown in windows explorer and in the complex example.

Posted: Thu Mar 30, 2006 4:32 pm
by techjunkie
Wow! Really nice! Thanks! :D

Posted: Thu Mar 30, 2006 6:55 pm
by va!n
fantastic *10/10 points*
great work fr34k!

Posted: Thu Mar 30, 2006 8:53 pm
by freak
Trond: Are you sure its not just the file icon you see in the complex example ?
It works fine here for all file types.

Posted: Fri Mar 31, 2006 4:36 pm
by Trond
freak wrote:Trond: Are you sure its not just the file icon you see in the complex example ?
It works fine here for all file types.
Of course I am sure.
Simple example (file icon show in file list): http://img92.imageshack.us/my.php?image=blur2560mz.png
Advanced example (preview shows): http://img483.imageshack.us/my.php?image=blur2569ic.png
Windows explorer: http://img394.imageshack.us/my.php?image=blur2567yd.png

Posted: Sat Apr 01, 2006 9:10 pm
by Sparkie
Very useful! Thank you freak. :)

Posted: Wed Jul 05, 2006 1:23 pm
by freak
Just a quick note that this code needs a CoInitialize_(0) call (and a CoUninitialize_() at the end)
to work properly.

The ExplorerTreeGadget() does these calls automatically, thats why it works as it is,
but does not work if you take the gadget out of the code.

Posted: Tue Oct 07, 2008 4:58 pm
by TheMaster
Real great work!!!

However, could anyone please give me a short hint, how can I select a thumbnail within the gadget. The idea is to pass the selected thumbnail image file name which is in the gadget list to another programme (image editor paint.net). Paint.net is missing the preview or browser function but with this snipplet I could very easy preview the files and pass them to paint.net by using the run command.


Many thanks

Posted: Wed Oct 08, 2008 5:45 pm
by Hi-Toro
Excellent piece of work.

Re: Get the Shell Thumbnail for files

Posted: Tue Nov 30, 2010 12:06 am
by c4s
Great work!

A small tip: PureBasic now supports alpha channels, so do the following to maintain it:
  • Use "32" as Depth
  • Put "| #PB_Image_Transparent" at the end of "CreateImage(Image, Width, Height, Depth)" (to create a fully transparent image)
  • Set "DrawImage()" to "DrawAlphaImage()"

I still have a small question:
How is it possible to find out the real thumbnail dimension (or the original one) using this interface stuff?

Re: Get the Shell Thumbnail for files

Posted: Tue Nov 30, 2010 1:02 pm
by TomS
Looks nice. Thanks for sharing.
But images are up-side-down (bmp and jpeg).
Thumbnails of Videos and OpenOffice Docs look fine. (second Example)

-PB 4.51 - Win7 Home Premium 32bit