Jump to content

Get the right icon (overlay, ghosted and more) with SHGetFileInfo


LarsJ
 Share

Recommended Posts

2014-06-10: Deprecated See this example.

Update #2: 2012-04-23

Added an item to the View menu to show the images in the System Image List. On startup the System Image List will be shown. Corrected some minor errors.

Zipfile below is updated.

Update #1: 2012-04-21

I've been working on this one and added options to get information about an overlay icon, a ghosted icon and an open folder icon. There is also added and option to test if a file/folder is compressed. In that case the name is drawn with a blue forecolor in the ListView.

Use the options to define how you want the icons drawn in the ListView. If you want the icons drawn with an overlay icon, then set the Overlay option. Unset the Overlay option if you don't want the overlay icon.

An icon can only be drawn with an overlay icon in the ListView if the original file/icon you drag/drop has an overlay icon.

Zipfile below is updated.

2012-03-28

To get the right icon you must call SHGetFileInfo with a pointer to a fully qualified PIDL identifying the file or folder. And you must initialize COM prior to calling SHGetFileInfo.

The UDF contains functions to initialize and uninitialize COM, to get and free the PIDL, and to call SHGetFileInfo with a pointer to the PIDL. The index of the icon image in the System Image List is returned. The UDF contains a function to get a handle to the System Image List too.

; =================================================================================================
; File ..........: GetIcon.au3
; Description ...: A collection of functions to get the right icon with SHGetFileInfo
; Author ........: LarsJ
; =================================================================================================

Global Const $_GetIcon_dllShell32 = DllOpen( "shell32.dll" )

Func _GetIcon_Init()
  _GetIcon_CoInitialize()
EndFunc

Func _GetIcon_Exit()
  _GetIcon_CoUninitialize()
  DllClose( $_GetIcon_dllShell32 )
EndFunc

Func _GetIcon_CoInitialize()
  ; Component Object Model (COM) must be
  ; initialized prior to calling SHGetFileInfo
  Local $aRet = DllCall("ole32.dll", "long", "CoInitialize", "ptr", 0)
  If @error Then Return SetError(1, 0, 0)
  Return $aRet[0]
EndFunc

Func _GetIcon_CoUninitialize()
  ; CoUninitialize must be called once for
  ; each successful call to CoInitialize
  DllCall("ole32.dll", "none", "CoUninitialize")
  If @error Then Return SetError(1, 0, 0)
  Return 0
EndFunc

; Use parameter False (default) for
; small icons and True for large icons.
; Copied from "SMF - Search my Files" by KaFu
Func _GetIcon_GetSystemImageList( $bLargeIcons = False )
  Local $tSHFILEINFO = DllStructCreate( $tagSHFILEINFO )
  Local $dwFlags = BitOR( $SHGFI_USEFILEATTRIBUTES, $SHGFI_SYSICONINDEX )
  If Not $bLargeIcons Then $dwFlags = BitOR( $dwFlags, $SHGFI_SMALLICON )
  Local $hIml = _WinAPI_ShellGetFileInfo( ".txt", $dwFlags, $FILE_ATTRIBUTE_NORMAL, $tSHFILEINFO )
  If @error Then Return SetError( @error, 0, 0 )
  Return $hIml
EndFunc

; This is the central function calling SHGetFileInfo with a pointer to the
; PIDL and returning the index of the icon image in the System Image List.
; $sPath must be a full path to be able to get a fully qualified PIDL.
Func _GetIcon_GetIconIndex( $sPath )

  ; Get the PIDL associated with the file or folder
  ; It must be a fully qualified PIDL
  ; PIDL = pointer to an ITEMIDLIST structure
  Local $pidl = _GetIcon_ILCreateFromPath( $sPath )
  If @error Then Return SetError(1, 0, 0)

  Local $tSHFILEINFO = DllStructCreate( $tagSHFILEINFO )
  Local $iFlags = BitOr( $SHGFI_PIDL, $SHGFI_SYSICONINDEX )
  ; SHGetFileInfo must be called with a pointer to the PIDL
  _GetIcon_ShellGetFileInfo( $pidl, $iFlags, 0, $tSHFILEINFO )
  If @error Then Return SetError(1, 0, 0)
  Local $iIcon = DllStructGetData( $tSHFILEINFO, "iIcon" )

  ; The PIDL must be released with ILFree()
  _GetIcon_ILFree( $pidl )
  If @error Then Return SetError(1, 0, 0)

  Return $iIcon

EndFunc

; Copied from WinAPIEx.au3 by Yashied and modified to identify
; the file or folder through a pointer to a fully qualified PIDL.
Func _GetIcon_ShellGetFileInfo($pidl, $iFlags, $iAttributes, ByRef $tSHFILEINFO)
  Local $aRet = DllCall($_GetIcon_dllShell32, 'dword_ptr', 'SHGetFileInfoW', 'ptr', $pidl, 'dword', $iAttributes, 'ptr', DllStructGetPtr($tSHFILEINFO), 'uint', DllStructGetSize($tSHFILEINFO), 'uint', $iFlags)
  If @error Then Return SetError(1, 0, 0)
  Return $aRet[0]
EndFunc

Func _GetIcon_ILCreateFromPath($sPath)
  Local $aRet = DllCall($_GetIcon_dllShell32, "ptr", "ILCreateFromPathW", "wstr", $sPath)
  If @error Then Return SetError(1, 0, 0)
  Return $aRet[0]
EndFunc

Func _GetIcon_ILFree($pidl)
  DllCall($_GetIcon_dllShell32, "none", "ILFree", "ptr", $pidl)
  If @error Then Return SetError(1, 0, 0)
  Return 0
EndFunc

The central function _GetIcon_GetIconIndex( $sPath ) must be called with a full path to be able to get a fully qualified PIDL.

The UDF must be saved as GetIcon.au3.

You need APIConstants.au3 and WinAPIEx.au3 by Yashied.

This is an example with a ListView. Drag some files or folders and drop them on the ListView. The proper icons should be shown in the ListView.

 

#include <GuiConstantsEx.au3>
#include <ListViewConstants.au3>
#include <GuiListView.au3>

#include "APIConstants.au3"
#include "WinAPIEx.au3"
#include "GetIcon.au3"

Opt( "MustDeclareVars", 1 )

Global $hGui, $hLV, $iIcons = 0
Global $idDropFiles, $aFiles[1]
Global $hIml = _GetIcon_GetSystemImageList( True ) ; Large icons <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;Global $hIml = _GetIcon_GetSystemImageList() ; Small icons <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; Component Object Model (COM) must be
; initialized prior to calling SHGetFileInfo
_GetIcon_Init() ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

MainScript()

Func MainScript()

  $hGui = GUICreate( "ListView", 400, 300, 800, 300, BitOR( $GUI_SS_DEFAULT_GUI, $WS_SIZEBOX ), BitOR( $WS_EX_ACCEPTFILES, $WS_EX_TOPMOST ) )

  Local $idLV = GUICtrlCreateListView( "", 0, 0, 400, 300 )
  GUICtrlSetStyle( $idLV, BitOR( $GUI_SS_DEFAULT_LISTVIEW, $LVS_ICON ) )
  GUICtrlSetState( $idLV, $GUI_DROPACCEPTED )
  $hLV = ControlGetHandle( $hGui, "", $idLV )
  _GUICtrlListView_SetExtendedListViewStyle( $hLV, $LVS_EX_DOUBLEBUFFER )
  _GUICtrlListView_SetImageList( $hLV, $hIml, 0 )

  $idDropFiles = GUICtrlCreateDummy()
  GUIRegisterMsg( $WM_DROPFILES, "WM_DROPFILES" )

  GUISetState( @SW_SHOW )

  While 1

    Switch GUIGetMsg()

      Case $GUI_EVENT_CLOSE
        ExitLoop

      Case $GUI_EVENT_RESIZED
        GUICtrlSendMsg( $idLV, $LVM_ARRANGE, 0, $iIcons )

      Case $idDropFiles
        LVupdate()

    EndSwitch

  WEnd

  ; CoUninitialize must be called once for
  ; each successful call to CoInitialize
  _GetIcon_Exit() ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  GUIDelete($hGui)
  Exit

EndFunc

Func WM_DROPFILES( $hWnd, $Msg, $wParam, $lParam )
  Switch $hWnd
    Case $hGui
      Local $aRet, $iSize, $sFileName
      $aRet = DllCall( "shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", -1, "ptr", 0, "int", 255 )
      ReDim $aFiles[$aRet[0]]
      For $i = 0 To $aRet[0] - 1
        $iSize = DllCall( "shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", $i, "ptr", 0, "int", 0 )
        $iSize = $iSize[0] + 1
        $sFileName = DllStructCreate( "char[" & $iSize & "]" )
        DllCall( "shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", $i, "ptr", DllStructGetPtr( $sFileName ), "int", $iSize )
        $aFiles[$i] = DllStructGetData( $sFileName, 1 )
        $sFileName = 0
      Next
      GUICtrlSendToDummy( $idDropFiles )
      Return 0
  EndSwitch
EndFunc

Func LVupdate()
  Local $iImage, $sText
  _GUICtrlListView_BeginUpdate( $hLV )
  For $file In $aFiles
    $iIcons += 1               ; $file must be a full path to be able to get a fully qualified PIDL
    $iImage = _GetIcon_GetIconIndex( $file ) ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    $sText = StringRight( $file, StringLen( $file ) - StringInStr( $file, "\", 0, -1 ) )
    If $sText = "" Then $sText = $file
    _GUICtrlListView_AddItem( $hLV, $sText, $iImage )
  Next
  _GUICtrlListView_EndUpdate( $hLV )
EndFunc

An example where COM is necessary to get the right icon is mht-files created with MS Office programs. These files usually get a special Office icon and not the standard Internet icon. COM is needed to get the right icon for these files.

Update 2012-04-23:

The zip can be opened with 7-Zip.

GetIcon.zip

2014-06-10: Deprecated See this example.

Edited by LarsJ
Link to comment
Share on other sites

  • 4 weeks later...
Link to comment
Share on other sites

Link to comment
Share on other sites

  • 7 months later...

Hi Adventurer, Everybody

I've been using GetIcon.au3 in a little project. Everythings working fine but I have one question. When I use the $_GetIcon_fOverlayIcon Flag i see the arrow ( for shortcut files) over the Icon. If I use the $hImlLarge imagelist, I see a the large overlay arrow with large icons and if I use the $hImlSmall imagelist I see the small arrow with small icons. Thats normal. But what if I would like to Use the small arrow on a large icon, like the icons that are on the desktop (e.g. win8) ?? Been searching for hours but still haven't found a solution.

Thanks,

riva

Link to comment
Share on other sites

riva, In this example I'm just using SHGetFileInfo to extract the icon information that already exists. E.g. I can extract information for an overlay icon if an overlay icon is applied.

Then I can use this information to set the state and statemask for the LV item to show the overlay icon in the ListView too.

In the ListView I can add and remove overlay icons as I want by setting the state and statemask to the appropriate values. And I can replace an existing overlay icon with another one in the System Image List.

It's also possible to create a new overlay icon with an icon editor, add it to the System Image List with _GetIcon_GetIconIndex(), swap an existing overlay icon with the new one with _GUIImageList_Swap(), and then use the new overlay icon in stead of the old one. I think the graphics in the two icons must have the same size.

This is probably the opportunities available. I don't think there are other ways to manipulate the overlay icons in this example. I don't think it's possible to use an overlay icon in the small System Image List with a large icon.

One question. If you drag a large icon with a small arrow from the desktop in Win 8 and drop it on the ListView will this icon be shown properly (as a large icon with a small arrow)? I can't test it myself, I have no access to Win 8.

Regards Lars.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...