Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

FileGetIcon - adding the file's original icon to a treeview


  • Please log in to reply
6 replies to this topic
Icarus
  • Members
  • 851 posts
  • Last active: Jan 02 2012 11:17 AM
  • Joined: 24 Nov 2005
Hello,

Not sure if this was discussed before, but I have quickly built a replacement for the useless windows start menu, and I was using a treeview to display the contents of both start menu folders.

Right now, I am using one icon for a folder, and one icon for a file.

Is there a neat and simple enough way to read the icon from each file (to see the icon as the OS sees it), and add it as an icon to the treeview?

I guess this is a two part question:
1. Is there any way to implement an "iconID = FileGetIcon( filepath )" function
2. Is there a way to dynamically increase the icon list of a treeview, or to bypass using it altogether (and just apply icon to a TV_ADDed item on the fly).

Thanks in advance.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
[*:2ryj2gm6]Search for SHGetFileInfo in the example on the ListView page of the help file.
[*:2ryj2gm6]Image lists are expanded automatically; simply add images.

Icarus
  • Members
  • 851 posts
  • Last active: Jan 02 2012 11:17 AM
  • Joined: 24 Nov 2005
Thanks a lot, checking the example now.

Icarus
  • Members
  • 851 posts
  • Last active: Jan 02 2012 11:17 AM
  • Joined: 24 Nov 2005
Ok,

I noticed that for LNK files, I do not get the icon of the file it points to.
So, this means I have to use FileGetShortcut to read each LNK, and get its executable and icon information, and then use the dllcall for getting the icon itself.

Correct?

jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006
Correct.

EDIT: ...or not. :?

Icarus
  • Members
  • 851 posts
  • Last active: Jan 02 2012 11:17 AM
  • Joined: 24 Nov 2005
Excellent.
If I am able to create a clean enough function, I will post it here.

Icarus
  • Members
  • 851 posts
  • Last active: Jan 02 2012 11:17 AM
  • Joined: 24 Nov 2005
Ok, for some reason, icons did not show in LNK files in the original example, but when I started working, I could see it works nicely.

So, for anyone else who is interested getting the file icon out of a file, to be displayed in a listview or treeview, here is what I am using:

;---------------------------------------------------
; Function: 
;     IconNumber := AddFileIcon( file, imageListID )
;
; IN:   file        = full file path
;       ImageListID = the ID of a previously created image list
;
; The function will read the icon associated with the file, 
; add the icon to the image list and return the icon number
; to be used with the "Icon" property of TreeView or ListView
;
; Based on AutoHotkeys help for TreeView
;---------------------------------------------------

#SingleInstance Force

ImageListID := IL_Create(10)

Gui Add, TreeView, w300 h300 vMainList ImageList%ImageListID%
Gui Show 


; To test, change the below variables: Point the first to a folder with LNK files 
; and the second to a folder with other files.
FolderWithLnks = C:\Documents and Settings\All Users\Start Menu\Programs\Accessories\Communications
FolderWithExes = C:\WINDOWS

Loop %FolderWithLnks%\*.*
{ 
  IconNumber := AddFileIcon( A_LoopFileFUllPath, ImageListID )
  TV_Add( A_LoopFileName, 0, "Icon" . IconNumber  )
}

Loop %FolderWithExes%\*.*
{ 
  IconNumber := AddFileIcon( A_LoopFileFUllPath, ImageListID )
  TV_Add( A_LoopFileName, 0, "Icon" . IconNumber  )
}

Return

;---------------------------------------------------
; THE FUNCTION
;---------------------------------------------------
AddFileIcon( file, imageList ) {
  ; Ensure the variable has enough capacity to hold the longest file path. This is done
  ; because ExtractAssociatedIconA() needs to be able to store a new filename in it.
  VarSetCapacity(Filename, 260)
  sfi_size = 352
  VarSetCapacity(sfi, sfi_size)

  FileName := file

  ; Build a unique extension ID to avoid characters that are illegal in variable names,
  ; such as dashes.  This unique ID method also performs better because finding an item
  ; in the array does not require search-loop.
  SplitPath, FileName,,, FileExt  ; Get the file's extension.
  If FileExt in EXE,ICO,ANI,CUR
  {
    ExtID := FileExt  ; Special ID as a placeholder.
    IconNumber = 0  ; Flag it as not found so that these types can each have a unique icon.
  }
  Else  ; Some other extension/file-type, so calculate its unique ID.
  {
    ExtID = 0  ; Initialize to handle extensions that are shorter than others.
    Loop 7     ; Limit the extension to 7 characters so that it fits in a 64-bit value.
    {
      StringMid, ExtChar, FileExt, A_Index, 1
      If Not ExtChar  ; No more characters.
        Break
      
      ; Derive a Unique ID by assigning a different bit position to each character:
      ExtID := ExtID | (Asc(ExtChar) << (8 * (A_Index - 1)))
    }

    ; Check if this file extension already has an icon in the ImageLists. If it does,
    ; several calls can be avoided and loading performance is greatly improved,
    ; especially for a folder containing hundreds of files:
    IconNumber := IconArray%ExtID%
  }
    
  If Not IconNumber  ; There is not yet any icon for this extension, so load it.
  {
    ; Get the high-quality small-icon associated with this file extension:
    If not DllCall("Shell32\SHGetFileInfoA", "str", FileName, "uint", 0, "str", sfi, "uint", sfi_size, "uint", 0x101)  ; 0x101 is SHGFI_ICON+SHGFI_SMALLICON
      IconNumber = 9999999  ; Set it out of bounds to display a blank icon.
        
    Else ; Icon successfully loaded.
    {
      ; Extract the hIcon member from the structure:
      hIcon = 0
      Loop 4
      hIcon += *(&sfi + A_Index-1) << 8*(A_Index-1)
      
      ; Add the HICON directly to the small-icon and large-icon lists.
      ; Below uses +1 to convert the returned index from zero-based to one-based:
      IconNumber := DllCall("ImageList_ReplaceIcon", "uint", imageList, "int", -1, "uint", hIcon) + 1
      
      ; Now that it's been copied into the ImageLists, the original should be destroyed:
      DllCall("DestroyIcon", "uint", hIcon)
      ; Cache the icon to save memory and improve loading performance:
      IconArray%ExtID% := IconNumber
    }
  }

  ; The icon is now in the list, return its number
  Return IconNumber
}


GuiEscape:
  ExitApp
Return