Results 1 to 11 of 11

Thread: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like DropBox

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like DropBox

    Everyone is familiar with the shortcut arrow-- this is an example of an overlay icon, a status indicator placed on top of another icon. Most existing VB file browser examples handle showing these by checking the attributes to see if it's a link or shared. But there's other icons- several more placed by Windows indicating things like offline files, security locks, permission shields, as well as custom ones- one of the most popular is DropBox. So if you want your app to display these as well, you need to look beyond file attributes to the IShellIconOverlay interface.

    Requirements
    -Windows XP or higher
    VB6:
    -oleexp v4.0 or higher
    -oleexp addon mIID.bas added (included in oleexp download)
    twinBASIC:
    Windows Development Library for twinBASIC (References->Available packages)

    Usage
    The GetOverlayIconIndex returns a 1-based index number, so you should determine a valid choice by checking if >0. Assigning an invalid choice (<1 or >15) may result in the main icon not being rendered at all.
    If you're using a control such as a ListView or TreeView and are not already assigning overlays, they're typically added like this:
    lvi.StateMask = LVIS_OVERLAYMASK
    lvi.State = INDEXTOOVERLAYMASK(overlayindex)

    where lvi is an LVITEM and this is followed with LVM_INSERTITEM or LVM_SETITEM. TreeViews are nearly identical. Do not set the overlay if there is none (the valid results mentioned above... do not set the statemask/state if the index is 0 or -1).

    The Code
    Code:
    Public Declare Function ILCreateFromPathW Lib "shell32" (ByVal pwszPath As LongPtr) As LongPtr
    Public Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal PV As LongPtr)
    Public Declare Function SHGetDesktopFolder Lib "shell32" (ppshf As IShellFolder) As Long
    
    Public Function GetOverlayIconIndex(sPath As String, sFile As String) As Long
    'Returns the overlay index for a file icons (like the shortcut arrow)
    Dim iDL As LongPtr
    Dim psf As IShellFolder
    Dim povr As IShellIconOverlay
    #If TWINBASIC Then
    'Assume using WinDevLib
    Dim pUnk As IUnknownUnrestricted
    #Else
    Dim pUnk As oleexp.IUnknown
    #End If
    Dim pcid As LongPtr, pche As Long, lAt As Long
    
    iDL = ILCreateFromPathW(StrPtr(sPath))
    If iDL Then
        Set psf = GetIShellFolder(isfDesktop, iDL)
        psf.ParseDisplayName 0&, 0&, StrPtr(sFile), pche, pcid, 0&
        If (psf Is Nothing) = False Then
            Set pUnk = psf
            pUnk.QueryInterface IID_IShellIconOverlay, povr
            If (povr Is Nothing) Then
                Debug.Print "GetOverlayIconIndex failed to get ishelliconoverlay " & sFile
            Else
                If pcid Then
                    Dim pio As Long
                    On Error Resume Next 'CRITICAL: files with no overlay return -1 AND raise a runtime error
                    povr.GetOverlayIndex pcid, pio 'VarPtr(pio) 
                    GetOverlayIconIndex = pio
                    On Error GoTo 0
                Else
                    Debug.Print sFile & "::GetOverlayIconIndex no child pidl"
                End If
            End If
        Else
            Debug.Print "GetOverlayIconIndex::no IShellFolder"
        End If
        Call CoTaskMemFree(pcid)
        Call CoTaskMemFree(iDL)
    Else
        Debug.Print "GetOverlayIconIndex::no pidl"
    End If
    
    End Function
    
    'Generic support functions you may already have if working with IShellFolder
    Public Function GetIShellFolder(isfParent As IShellFolder, pidlRel As LongPtr) As IShellFolder
      Dim isf As IShellFolder
      On Error GoTo out
    
      Call isfParent.BindToObject(pidlRel, 0, IID_IShellFolder, isf)
    
    out:
      If Err Or (isf Is Nothing) Then
        Set GetIShellFolder = isfDesktop
      Else
        Set GetIShellFolder = isf
      End If
    
    End Function
    Public Function isfDesktop() As IShellFolder
      Static isf As IShellFolder
      If (isf Is Nothing) Then Call SHGetDesktopFolder(isf)
      Set isfDesktop = isf
    End Function
    Notes
    -If a file doesn't have an overlay, the COM interface throws a runtime error (0x80004005 automation error unspecified). The code snippet above uses On Error Resume Next to suppress this, but if you have 'Break On All Errors' enabled, it will come up.

    -The overlay index returned includes the standard shortcut and share overlays; you can eliminate code checking for them separately.

    -Here's the INDEXTOOVERLAYMASK function mentioned earlier if you need it:
    Code:
    Public Function INDEXTOOVERLAYMASK(iOverlay As Long) As Long
      '   INDEXTOOVERLAYMASK(i)   ((i) << 8)
      INDEXTOOVERLAYMASK = iOverlay * (2 ^ 8)
    End Function
    UPDATE- Code updated to free child pidl as well; not freeing it causes memory leakage. Call CoTaskMemFree(pcid)
    Last edited by fafalone; Mar 9th, 2026 at 08:56 AM. Reason: Update for tB/x64 compatibility

  2. #2
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    976

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    This seems not to work in Win11. Error E_FAIL = &H80004005 is raised if the item got a shortcut icon associated.
    Have not tried on my old Win10 machine due to electrical error.

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    It's working on Win10 for me I'll try to check 11 tomorrow. If you're checking shortcut files, did you include the .lnk in the name?

    I tested in tB with WinDevLib...

    Code:
        Public Function GetOverlayIconIndex(sPath As String, sFile As String) As Long
        'Returns the overlay index for a file icons (like the shortcut arrow)
        Dim iDL As LongPtr
        Dim psf As IShellFolder
        Dim povr As IShellIconOverlay
        Dim pUnk As IUnknownUnrestricted
        Dim pcid As LongPtr, pche As Long, lAt As Long
    
        iDL = ILCreateFromPathW(StrPtr(sPath))
        If iDL Then
            Set psf = GetIShellFolder(isfDesktop, iDL)
            psf.ParseDisplayName 0&, 0&, StrPtr(sFile), pche, pcid, 0&
            If (psf Is Nothing) = False Then
                Set pUnk = psf
                pUnk.QueryInterface IID_IShellIconOverlay, povr
                If (povr Is Nothing) Then
                    Debug.Print "GetOverlayIconIndex failed to get ishelliconoverlay " & sFile
                Else
                    If pcid Then
                        Dim pio As Long
                        On Error Resume Next 'CRITICAL: files with no overlay return -1 AND raise a runtime error
                        povr.GetOverlayIndex pcid, pio
                        GetOverlayIconIndex = pio
                        On Error GoTo 0
                    Else
                        Debug.Print sFile & "::GetOverlayIconIndex no child pidl"
                    End If
                End If
            Else
                Debug.Print "GetOverlayIconIndex::no IShellFolder"
            End If
            Call CoTaskMemFree(pcid)
            Call CoTaskMemFree(iDL)
        Else
            Debug.Print "GetOverlayIconIndex::no pidl"
        End If
    
        End Function
    
        'Generic support functions you may already have if working with IShellFolder
        Public Function GetIShellFolder(isfParent As IShellFolder, pidlRel As LongPtr) As IShellFolder
          Dim isf As IShellFolder
          On Error GoTo out
    
          Call isfParent.BindToObject(pidlRel, 0, IID_IShellFolder, isf)
    
        out:
          If Err Or (isf Is Nothing) Then
            Set GetIShellFolder = isfDesktop
          Else
            Set GetIShellFolder = isf
          End If
    
        End Function
        Public Function isfDesktop() As IShellFolder
          Static isf As IShellFolder
          If (isf Is Nothing) Then Call SHGetDesktopFolder(isf)
          Set isfDesktop = isf
        End Function
        
        Private Sub Form_Load() Handles Form.Load
            Debug.Print GetOverlayIconIndex("C:\temp", "1.gif.lnk")
        End Sub
    (Note WDL differs from oleexp in not using VarPtr for povr.GetOverlayIndex)

  4. #4
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    976

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    Yes, I tried a .lnk file and I tried with a .lnk folder same error.
    Also same error without VarPtr for .lnk file.
    Yes, without .lnk extention for the shortcut the pidl creation fails.
    I take a full path to a item and convert it to a IShellItem
    And BHID it and gets the IShellFolder.
    If it fails it binds to desktopfolder.
    Or should it be immediate parent?
    ShBindToParent API before binding to Desktop if failes?

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    Sorry I'm not following -- "Yes, without .lnk extention for the shortcut the pidl creation fails." : This would be expected if you're trying to get an IShellItem/IShellFolder for a shortcut... you need to include the .lnk.

    After SHCreateItemFromParsingName you can

    Code:
        Dim upi As IParentAndItem, pidlPar As LongPtr, psf As IShellFolder, pidlRel As LongPtr
                Set upi = siItem
                upi.GetParentAndItem pidlPar, psf, pidlRel
        Dim pOvr As IShellIconOverlay
        Set pOvr = psf
        pOvr.GetOverlayIndex pidlRel, nIconOvr 'VarPtr(nIconOvr) with oleexp.tlb

  6. #6
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    976

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    You asked me if I included .lnk in the name. Therefore I answered in such way
    It works now as a standalone try with the pidlChild from IParentAndItem which interface had fell out of my mind.
    But when I put it in an folder enumeration I get 0-value on .lnk files.
    The error seems to be a ”pidl related” fault. I do put in the pidlEnum pidl extraxted from the pISI_Enum. Tried also with a full pidl combined from the startpidl and enumerated pidl but still 0-overlay. It should be index 2.
    No exeption errors thank god

  7. #7

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    Tried the code I posted in #3 with a folder shortcut and no problem, returns 2 -- but again you always have to include the .lnk extension to get an IShellItem, pidl, IShellFolder child, etc.

    It sounds like you're not using the code I posted so I couldn't be sure of the issue without code to reproduce the problem.

  8. #8
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    976

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    Yes, the code works clockwork for one IShellItem object and one IShellFolder object.
    But the problem arise when I enumerate a folder’s items. Just a standard IShellItem enumeration.
    And setting pIPAI for every new pISI_Enum.
    In that case pidlChild as in the single object try does not work in enumeration because it does not return correct pidl.
    The pidlChild in the GetParent event does not work in enumeration. Throws same error as in the first post i did.
    It accepts the pidlEnum as explained.
    So the the enumeration example only differs in the IParentAndItem is switching IShellItems and IShellFolders and the pidlEnum is used instead.

  9. #9

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,654

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    You're going to have to post an example of the issue I can run.

  10. #10
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    976

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    Yes, here it comes. Attached as a zip-file.
    However in your one-file example as your sample, you are not responsible to sort out and isolate the .lnk-attribute.
    If you not do so before calling this event in an enumeration there will be problems since there is no overlays associated in the non-link items.
    That was the problem. I was to tired yesterday (swedish time) to sort that out.
    In this sample I have done so.

    Code:
    Public Sub EnumFolder(ByVal pidl As Long)
       Dim pISI As IShellItem2
       Dim pIESI As IEnumShellItems
       Dim pIPIDL As IPersistIDList
       Dim pISI_Enum As IShellItem2
       Dim pIPAI As IParentAndItem
       Dim pISF As IShellFolder
       Dim pIUNK As oleexp.IUnknown
       Dim pISIO As IShellIconOverlay
       Dim pidlEnum As Long
       Dim pidlChild As Long
       Dim lDispName As Long
       Dim n As Long
       Dim nOverLayIcon As Long
       Dim dwAttribsIn As SFGAO_Flags
       Dim dwAttribsOut As Long
       Dim hr As Long
       
       If pidl = 0 Then Exit Sub
       
       hr = SHCreateShellItem(0, 0, pidl, pISI)
       
       If hr = S_OK Then
          hr = pISI.BindToHandler(0&, BHID_EnumItems, IID_IEnumShellItems, pIESI)
          If hr = S_OK Then
             n = 0
             Do While pIESI.Next(1, pISI_Enum, 0) = S_OK
                If Not (pISI_Enum Is Nothing) Then Set pIPIDL = pISI_Enum
                If Not (pIPIDL Is Nothing) Then pIPIDL.GetIDList pidlEnum
                Set pIPAI = pISI_Enum
                If Not (pIPAI Is Nothing) Then
                   pIPAI.GetParentAndItem 0, pISF, pidlChild
                   If Not (pISF Is Nothing) Then Set pISF = GetDesktopFolder
                   Set pIUNK = pISF
                   hr = pIUNK.QueryInterface(IID_IShellIconOverlay, pISIO)
                End If
                dwAttribsIn = SFGAO_LINK '<--- SELECT SHORTCUT
                pISI_Enum.GetAttributes dwAttribsIn, dwAttribsOut '<--- CHECK FOR SHORTCUTS
                If dwAttribsOut = SFGAO_LINK Then '<--- LINK ATTRIBUTES MUST BE SORTED OUT AND ISOLATED BEFORE CALLING pISIO::GetOverlayIndex
                   If Not (pISIO Is Nothing) Then pISIO.GetOverlayIndex pidlChild, VarPtr(nOverLayIcon)
                   pISI_Enum.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lDispName
                   Form1.List1.AddItem Trim(GetStrAFromBStr(lDispName)) & " , OverLayIndex: " & CStr(nOverLayIcon)
                End If
                n = n + 1
                Set pISI_Enum = Nothing
                Set pISIO = Nothing
                CoTaskMemFree pidlEnum
             Loop
          End If
       End If
       
       Set pISI = Nothing
       Set pIESI = Nothing
       CoTaskMemFree pidl
       pidl = 0
    End Sub
    Fafalon's type library or similar need to be used.

    Thanks for your support
    Attached Files Attached Files

  11. #11
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    976

    Re: [VB6] Code Snippet: Get file overlay (e.g. shortcut arrow), inc. customs like Dro

    And here is a screenshoot from my ucListView project with correct overlay retrieved in LVN_GETDISPINFO event instead of direct in the enum procedure. Note I have custom drawing activated for greyscale icons with blue glowing edges
    Attached Images Attached Images  

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width