[VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager-VBForums
Results 1 to 11 of 11

Thread: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    1,790

    [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Using the KnownFolderManager Object

    oleexp includes the IKnownFolderManager and IKnownFolder interfaces.

    If plan on doing any work with the Known Folders that replaced CSIDL Special Locations and you're working exclusively with Vista and higher, there's now the IKnownFolderManager interface, for which Windows provides a default instance of, which makes your job much easier.

    Code:
    Dim pKFM as KnownFolderManager
    Set pKFM = New KnownFolderManager
    Now you have a ready-to-use manager that gives you the following:

    .FindFolderFromPath /IDList - Have the path of a special folder and want to get its IKnownFolder interface to find out information about it? You can specify a full or partial path. If you work with PIDLs, e.g. the result from a folder browser that you could use here directly without converting back and forth to a string path, there's a function to get a known folder directly from that as well.


    .FolderIdFromCsidl - Still working with CSIDLs? This will ease the transition into support Known Folders.

    .GetFolder / .GetFolderByName - You can use either the GUID or canonical name to return a Known Folder object.

    Code:
    Dim pikf As IKnownFolder
    pKFM.FindFolderFromPath StrPtr("C:\Users\Jon\Downloads"), FFFP_EXACTMATCH, pikf
    Once you have a Known Folder, in the form of a IKnownFolder object, you can get tons of information about it:

    From the main IKnownFolder object, you can get all its file system information, like its PROPERTYKEY, path, pidl, or even an IShellItem interface for it (you can also change the path with SetPath), then there's a significant subset of information in the description:
    Code:
    pikf.GetFolderDefinition desc
    pikf.GetId pid
    PrintGUID pid
    Debug.Print "Icon=" & BStrFromLPWStr(desc.pszIcon, False)
    Debug.Print "Name=" & BStrFromLPWStr(desc.pszName, False)
    Debug.Print "Description=" & BStrFromLPWStr(desc.pszDescription, False)
    Debug.Print "LocalizedName=" & BStrFromLPWStr(desc.pszLocalizedName, False)
    Debug.Print "ToolTip=" & BStrFromLPWStr(desc.pszToolTip, False)
    Debug.Print "Category=" & desc.category 'peruser, common, etc
    Debug.Print "Attributes=" & desc.dwAttributes
    
    FreeKnownFolderDefinitionFields desc
    This is by far the easiest way to work with these special folders on newer versions of Windows.

    Most of the oleexp projects use this, but again:
    Code:
    Public Declare Function SysReAllocString Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long
    Public Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long) ' Frees memory allocated by the shell
    
    Public Function BStrFromLPWStr(lpWStr As Long, Optional ByVal CleanupLPWStr As Boolean = True) As String
    SysReAllocString VarPtr(BStrFromLPWStr), lpWStr
    If CleanupLPWStr Then CoTaskMemFree lpWStr
    End Function
    
    Public Sub FreeKnownFolderDefinitionFields(pKFD As KNOWNFOLDER_DEFINITION)
    Call CoTaskMemFree(pKFD.pszName)
    Call CoTaskMemFree(pKFD.pszDescription)
    Call CoTaskMemFree(pKFD.pszRelativePath)
    Call CoTaskMemFree(pKFD.pszParsingName)
    Call CoTaskMemFree(pKFD.pszToolTip)
    Call CoTaskMemFree(pKFD.pszLocalizedName)
    Call CoTaskMemFree(pKFD.pszIcon)
    Call CoTaskMemFree(pKFD.pszSecurity)
    
    End Sub
    'also handy,
    Public Declare Function StringFromGUID2 Lib "ole32.dll" (ByRef rguid As Any, ByVal lpsz As String, ByVal cchMax As Long) As Long
    
    Public Sub PrintGUID(TempGUID As UUID)
    Dim GuidStr As String
    Dim lLen As Long
    
    GuidStr = Space(80)
    lLen = StringFromGUID2(TempGUID, GuidStr, 80)
    
    If (lLen) Then
        GuidStr = StrConv(Left$(GuidStr, (lLen - 1) * 2), vbFromUnicode)
        Debug.Print GuidStr
    End If
    End Sub

  2. #2

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    1,790

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Here's a practical code example, generating a menu with special folders complete with their custom icons.


    UPDATE Sep 4, 2017: Code has been updated to use mIID.bas; you'll need that module to run the code as it appears now. mIID.bas is an included add-on module in the oleexp download. The declares were also missing enums for MENUITEMINFOW, they've been added.

    Code:
    Dim MII As MENUITEMINFOW
    Dim i As Long, j As Long, k As Long
    Dim hIcon As Long
    Dim pKFM As KnownFolderManager
    Dim pKNF As IKnownFolder
    Dim kfd As KNOWNFOLDER_DEFINITION
    Dim tID As UUID
    Dim isiif As IShellItemImageFactory
    Dim isi As IShellItem
    Dim hMenu As Long
    Dim sCap As String
    Dim idCmd As Long
    Dim pt As POINTAPI
    Dim s1 As String, lp1 As Long
    Const widBaseDSF As Long = 1000
    
    hMenu = CreatePopupMenu()
    Set pKFM = New KnownFolderManager
    
    
    'Begin folder blocks
    For k = 0 To 10 'number special folders in GetSpecialFolderMenuItem
        GetSpecialFolderMenuItem k, tID
        pKFM.GetFolder tID, pKNF
        pKNF.GetFolderDefinition kfd
        sCap = BStrFromLPWStr(kfd.pszName)
        pKNF.GetShellItem 0, IID_IShellItemImageFactory, isiif
        isiif.GetImage 16, 16, SIIGBF_ICONONLY, hIcon
        With MII
                    .cbSize = Len(MII)
                    .fMask = MIIM_ID Or MIIM_STRING Or MIIM_BITMAP
                    .wID = widBaseDSF + j
                    .cch = Len(sCap)
                    .dwTypeData = StrPtr(sCap)
                    .hbmpItem = hIcon
                    
                    Call InsertMenuItemW(hMenu, j, True, MII)
        
                Call DestroyIcon(hIcon)
                j = j + 1
        End With
        Set isiif = Nothing
        Set pKNF = Nothing
    Next k
    'end folder blocks
    
    Call GetCursorPos(pt)
    
    idCmd = TrackPopupMenu(hMenu, TPM_LEFTBUTTON Or TPM_RIGHTBUTTON Or TPM_LEFTALIGN Or TPM_TOPALIGN Or TPM_HORIZONTAL Or TPM_RETURNCMD, pt.X - 50, pt.Y + 10, 0, Me.hWnd, 0)
    
    If idCmd Then
        GetSpecialFolderMenuItem idCmd - 1000, tID
        pKFM.GetFolder tID, pKNF
        pKNF.GetShellItem 0, IID_IShellItem, isi
        isi.GetDisplayName SIGDN_FILESYSPATH, lp1
        s1 = BStrFromLPWStr(lp1)
        ShellExecute Me.hWnd, "explore", s1, "", "", SW_NORMAL
    End If
    
    End Sub
    Private Sub GetSpecialFolderMenuItem(nIdx As Long, tID As UUID)
    Select Case nIdx
        Case 0: tID = FOLDERID_Profile
        Case 1: tID = FOLDERID_Downloads
        Case 2: tID = FOLDERID_Documents
        Case 3: tID = FOLDERID_Videos
        Case 4: tID = FOLDERID_Music
        Case 5: tID = FOLDERID_Pictures
        Case 6: tID = FOLDERID_Contacts
        Case 7: tID = FOLDERID_Favorites
        Case 8: tID = FOLDERID_Links
        Case 9: tID = FOLDERID_RoamingAppData
        Case 10: tID = FOLDERID_Windows
    End Select
    End Sub
    This shows how handy the object is... instead of extracting and converting and using menu image classes, it's just a couple lines to get the shell item image factory, which returns an hBitmap directly compatible with menus. That code can be placed anywhere, like a button click, and will open the folder clicked on.

    Here's the supporting API declares (and all the known folders, not just the ones I picked, in case you want others)... most of these should be standard in any major project so it's not really as bad as it looks...
    Code:
    Public Declare Function CLSIDFromString Lib "ole32" (ByVal lpszGuid As Long, pGuid As Any) As Long
    Public Declare Function SysReAllocString Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long
    Public Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long) ' Frees memory allocated by the shell
    Public Declare Function InsertMenuItemW Lib "user32" (ByVal hMenu As Long, ByVal uItem As Long, ByVal fByPosition As Boolean, lpmii As MENUITEMINFOW) As Boolean
    Public Declare Function DestroyIcon Lib "user32.dll" (ByVal hIcon As Long) As Long
    Public Declare Function CreatePopupMenu Lib "user32" () As Long
    Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
    Public Declare Function TrackPopupMenu Lib "user32" (ByVal hMenu As Long, ByVal wFlags As TPM_wFlags, ByVal X As Long, ByVal Y As Long, ByVal nReserved As Long, ByVal hWnd As Long, lprc As Any) As Long
    Public Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As ShowWindowTypes) As Long
    Public Enum ShowWindowTypes
        SW_HIDE = 0
        SW_SHOWNORMAL = 1
        SW_NORMAL = 1
        SW_SHOWMINIMIZED = 2
        SW_SHOWMAXIMIZED = 3
        SW_MAXIMIZE = 3
        SW_SHOWNOACTIVATE = 4
        SW_SHOW = 5
        SW_MINIMIZE = 6
        SW_SHOWMINNOACTIVE = 7
        SW_SHOWNA = 8
        SW_RESTORE = 9
        SW_SHOWDEFAULT = 10
    End Enum
    Public Enum TPM_wFlags
      TPM_LEFTBUTTON = &H0
      TPM_RIGHTBUTTON = &H2
      TPM_LEFTALIGN = &H0
      TPM_CENTERALIGN = &H4
      TPM_RIGHTALIGN = &H8
      TPM_TOPALIGN = &H0
      TPM_VCENTERALIGN = &H10
      TPM_BOTTOMALIGN = &H20
    
      TPM_HORIZONTAL = &H0         ' Horz alignment matters more
      TPM_VERTICAL = &H40            ' Vert alignment matters more
      TPM_NONOTIFY = &H80           ' Don't send any notification msgs
      TPM_RETURNCMD = &H100
      
      TPM_HORPOSANIMATION = &H400
      TPM_HORNEGANIMATION = &H800
      TPM_VERPOSANIMATION = &H1000
      TPM_VERNEGANIMATION = &H2000
      TPM_NOANIMATION = &H4000
    End Enum
    Public Type MENUITEMINFOW
      cbSize As Long
      fMask As MII_Mask
      fType As MF_Type              ' MIIM_TYPE
      fState As MF_State             ' MIIM_STATE
      wID As Long                       ' MIIM_ID
      hSubMenu As Long            ' MIIM_SUBMENU
      hbmpChecked As Long      ' MIIM_CHECKMARKS
      hbmpUnchecked As Long  ' MIIM_CHECKMARKS
      dwItemData As Long          ' MIIM_DATA
      dwTypeData As Long        ' MIIM_TYPE
      cch As Long                       ' MIIM_TYPE
      hbmpItem As Long
      
    End Type
    Public Enum MII_Mask
      MIIM_STATE = &H1
      MIIM_ID = &H2
      MIIM_SUBMENU = &H4
      MIIM_CHECKMARKS = &H8
      MIIM_TYPE = &H10
      MIIM_DATA = &H20
      MIIM_BITMAP = &H80
      MIIM_STRING = &H40
    End Enum
    Public Enum MF_Type
      MFT_STRING = MF_STRING
      MFT_BITMAP = MF_BITMAP
      MFT_MENUBARBREAK = MF_MENUBARBREAK
      MFT_MENUBREAK = MF_MENUBREAK
      MFT_OWNERDRAW = MF_OWNERDRAW
      MFT_RADIOCHECK = &H200
      MFT_SEPARATOR = MF_SEPARATOR
      MFT_RIGHTORDER = &H2000
      MFT_RIGHTJUSTIFY = MF_RIGHTJUSTIFY
    End Enum
    
    Public Enum MF_State
      MFS_GRAYED = &H3
      MFS_DISABLED = MFS_GRAYED
      MFS_CHECKED = MF_CHECKED
      MFS_HILITE = MF_HILITE
      MFS_ENABLED = MF_ENABLED
      MFS_UNCHECKED = MF_UNCHECKED
      MFS_UNHILITE = MF_UNHILITE
      MFS_DEFAULT = MF_DEFAULT
    End Enum
    Public Function BStrFromLPWStr(lpWStr As Long, Optional ByVal CleanupLPWStr As Boolean = True) As String
    SysReAllocString VarPtr(BStrFromLPWStr), lpWStr
    If CleanupLPWStr Then CoTaskMemFree lpWStr
    End Function

  3. #3
    Lively Member
    Join Date
    Aug 2011
    Posts
    125

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Hey fafalone. This looks interesting and potentially promising for an issue I'm working on. Can you post some sample code as to how to get the path for one of these KnownFolders? That would be most helpful!

  4. #4
    Lively Member
    Join Date
    Aug 2011
    Posts
    125

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Code:
    Dim pikf As IKnownFolder
    pKFM.FindFolderFromPath "C:\Users\Jon\Downloads", FFFP_EXACTMATCH, pikf
    This code fails. It appears that the path argument is wanting a Long, not a String. I tried passing the path like this StrPtr(Path) but that didn't help the problem. It seems that the interface to this changed since you created your code examples?

    I'm using the latest version of the OLEEXP TLB.

  5. #5

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    1,790

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Is your name Jon too?

    If you did change the username to yours, what do you mean fails. Does pikf = Nothing after that? What version of Windows-- is that path still the Downloads folder in it? It works on Win7 for me. But in any case, you're much better off loading from folder id rather than worry about getting the current user name to figure out a hard path... mIID has all the KnownFolder ids to use with .GetFolder (FOLDERID_x).

    And yes you are meant to pass StrPtr(path); it was updated to that so that Unicode worked.

    That's going from a file system path to a IKnownFolder object though-- your post before that says you want to get the path?

    To get your Downloads path, for example, you'd go
    Code:
    Dim lpPath As Long, sPath As String
    pKFM.GetFolder FOLDERID_Downloads, pikf
    pikf.GetPath KF_FLAG_DEFAULT, lpPath
    SysReAllocString VarPtr(sPath), lpPath
    CoTaskMemFree lpPath
    You can get the official path with that code, then feed the result back to FindFolderFromPath, if you want to double check that it's working.

  6. #6
    Lively Member
    Join Date
    Aug 2011
    Posts
    125

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Thanks fafalone! I'll try out your code snippet.

    And no my name is not Jon! LOL. I guess you had two different users checking this out over the weekend? Which would be odd since your post is over 2 years old!

  7. #7

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    1,790

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Nono.. you put my Username in your post, C:\Users\Jon, thats why I was saying that lol, to make sure you were using your username and not a hard coded path w/ mine.

    Also check out my code in your recycle bin thread that started this, I went a little crazy with it

  8. #8
    Lively Member
    Join Date
    Aug 2011
    Posts
    125

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    I got your code snippet to work but I had to change the alloc string line to:

    Code:
    SysReAllocString sPath, lpPath
    Rather than what you had:

    Code:
    SysReAllocString VarPtr(sPath), lpPath
    SysReAllocString wants a String as the first parameter.

    Once I did that, the path was returned. Thanks for your help with this!

  9. #9
    Lively Member
    Join Date
    Aug 2011
    Posts
    125

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    When I try this on the Recycle Bin folder I get an automation error on this line:

    Code:
    pikf.GetPath KF_FLAG_DEFAULT, lpPath
    Here's the complete code I'm using, which works fine when I use FOLDERID_Downloads rather than FOLDERID_RecycleBinFolder:

    Code:
        Dim pKFM    As KnownFolderManager
        Set pKFM = New KnownFolderManager
        
        Dim pikf    As IKnownFolder
        Dim lpPath  As Long
        Dim sPath   As String
        
        'pKFM.GetFolder FOLDERID_Downloads, pikf
        pKFM.GetFolder FOLDERID_RecycleBinFolder, pikf  '<--Automation Error on this line
        pikf.GetPath KF_FLAG_DEFAULT, lpPath
        SysReAllocString sPath, lpPath
        
        MsgBox sPath
    
        CoTaskMemFree lpPath
        Set pKFM = Nothing

  10. #10

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    1,790

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Do you have any files in your Recycle Bin? As noted in the other thread, the folder only exists if it's in use. And to confirm, you added the mIID.bas module right? FOLDERID_RecycleBinFolder should be
    Code:
    Public Function FOLDERID_RecycleBinFolder() As UUID
    Static iid As UUID
     If (iid.Data1 = 0) Then Call DEFINE_UUID(iid, &HB7534046, CInt(&H3ECB), CInt(&H4C18), &HBE, &H4E, &H64, &HCD, &H4C, &HB7, &HD6, &HAC)
     FOLDERID_RecycleBinFolder = iid
    End Function

  11. #11
    Lively Member
    Join Date
    Aug 2011
    Posts
    125

    Re: [VB6, Vista+] Code snippet: KnownFolders made easy with IKnownFolderManager

    Yes, many files are in the Recycle Bin.

    Latest OLEEXP tlb downloaded just last week and in Project references, mIID.bas is in the project, FolderId_RecycleBinFolder UUID definition is correct.

    I even created a brand new project with just your code in it and these references and modules with the same result.

Posting Permissions

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



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.