Get the Shell Thumbnail for files

Share your advanced PureBasic knowledge/code with the community.
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Get the Shell Thumbnail for files

Post 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
quidquid Latine dictum sit altum videtur
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

Danke Timo!
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
dige
Addict
Addict
Posts: 1240
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Post by dige »

Wow!! That rocks. Thank you fr34k!
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Awesome. Thank you!
@}--`--,-- A rose by any other name ..
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post 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.
techjunkie
Addict
Addict
Posts: 1126
Joined: Wed Oct 15, 2003 12:40 am
Location: Sweden
Contact:

Post by techjunkie »

Wow! Really nice! Thanks! :D
Image
(\__/)
(='.'=) This is Bunny. Copy and paste Bunny into your
(")_(") signature to help him gain world domination.
va!n
Addict
Addict
Posts: 1104
Joined: Wed Apr 20, 2005 12:48 pm

Post by va!n »

fantastic *10/10 points*
great work fr34k!
va!n aka Thorsten

Intel i7-980X Extreme Edition, 12 GB DDR3, Radeon 5870 2GB, Windows7 x64,
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post 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.
quidquid Latine dictum sit altum videtur
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post 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
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post by Sparkie »

Very useful! Thank you freak. :)
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post 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.
quidquid Latine dictum sit altum videtur
TheMaster
New User
New User
Posts: 5
Joined: Sun Jan 16, 2005 1:23 pm

Post 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
Hi-Toro
Enthusiast
Enthusiast
Posts: 265
Joined: Sat Apr 26, 2003 3:23 pm

Post by Hi-Toro »

Excellent piece of work.
James Boyd
http://www.hi-toro.com/
Death to the Pixies!
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: Get the Shell Thumbnail for files

Post 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?
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
User avatar
TomS
Enthusiast
Enthusiast
Posts: 342
Joined: Sun Mar 18, 2007 2:26 pm
Location: Munich, Germany

Re: Get the Shell Thumbnail for files

Post 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
Post Reply