dcsimg
Results 1 to 17 of 17

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

  1. #1

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

    [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,967

    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
    Addicted Member
    Join Date
    Aug 2011
    Posts
    188

    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
    Addicted Member
    Join Date
    Aug 2011
    Posts
    188

    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,967

    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
    Addicted Member
    Join Date
    Aug 2011
    Posts
    188

    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,967

    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
    Addicted Member
    Join Date
    Aug 2011
    Posts
    188

    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
    Addicted Member
    Join Date
    Aug 2011
    Posts
    188

    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,967

    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
    Addicted Member
    Join Date
    Aug 2011
    Posts
    188

    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.

  12. #12
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    532

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

    Hi, fafalone!

    Thank you for information about this interface and nice examples.

    I'm trying to use IKnownFolderManager::GetFolderIds method to enum all known folder ids, however VB returns: "Compile error: function or interface marked as restricted, or function uses an Automation type not supported in Visual Basic".

    Can you please take a look, how can it be resolved?

    Code:
        Dim kfid As UUID
        Dim nCount As Long
        Dim pakfid As Long
        Dim i As Long
        Dim ptr As Long
        
        Dim pKFM As KnownFolderManager
        Set pKFM = New KnownFolderManager
        
        If S_OK = pKFM.GetFolderIds(pakfid, nCount) Then
            
            ptr = pakfid
            
            For i = 1 To nCount
                memcpy kfid, ByVal ptr, LenB(kfid)
                ptr = ptr + LenB(kfid)
                Stop
            Next
            CoTaskMemFree pakfid
        End If
        
        Set pKFM = Nothing
    Here is your implementation:
    Code:
        [
            odl,
            uuid({8BE2D872-86AA-4D47-B776-32CCA40C7018})
        ]
            interface IKnownFolderManager : IUnknown {
    ...
                HRESULT _stdcall GetFolderIds(
                                    [out] UUID** ppKFId,
                                    [in, out] long* pCount);
    ...
    Thanks,
    Stanislav.

  13. #13

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

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

    Probably going to be the first param there, don't think VB likes the out-only usertype. First thought is to change it to long* and try to get a pointer to use; dimension an array of uuid(pCount) and use CopyMemory. If that works I'll post it in a bit.

  14. #14
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    532

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

    OMG, there are 104 special folders on my Win7 ))))))))))))

    fafalone, hi, and thank you.

    Code:
        long GetFolderIds(
            [out] long* ppKFId,
            [in, out] UINT *pCount);
    do the job. I also changed HRESULT return type by long to be able to check operation for success.

    I combined two methods to get physical path:
    1) your method with child items enum - (IShellItem -> GetDisplayName (SIGDN_FILESYSPATH)) + added the same for the root IShellItem
    2) and IKnownFolder -> GetPath

    It looks like m.1 with child enum doesn't make sense for anything, but Recycle bin.
    Also, Root IShellItem -> GetDisplayName worse than m.2 because it return Public special folders, not current user.

    Code:
    Option Explicit
    
    Private Declare Function memcpy Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) As Long
    Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long)
    Private Declare Function SysReAllocString Lib "oleaut32" (ByVal pBSTR As Long, ByVal lpWStr As Long) As Long
    
    Private Sub Form_Load()
        Dim kfid As UUID
        Dim nCount As Long
        Dim pakfid As Long
        Dim i As Long
        Dim ptr As Long
        Dim flags As Long
        Dim lpPath As Long
        Dim sPath$, sName$, sLocName$
        
        Dim pKFM As KnownFolderManager
        Set pKFM = New KnownFolderManager
        
        Dim pKF As IKnownFolder
        Dim pKFD As KNOWNFOLDER_DEFINITION
        
        Dim pItem As IShellItem
        Dim penum1 As IEnumShellItems
        Dim pChild As IShellItem
        Dim pcl As Long
        
        pKFM.GetFolderIds pakfid, nCount
    
        if nCount > 0 then
            ptr = pakfid
    
            For i = 1 To nCount
                memcpy kfid, ByVal ptr, LenB(kfid)  'array[idx] -> UUID
                ptr = ptr + LenB(kfid)
                
                If S_OK = pKFM.GetFolder(kfid, pKF) Then  'UUID -> IKnownFolder
                
                    If Not (pKF Is Nothing) Then
                        
                        pKF.GetFolderDefinition pKFD         'IKnownFolder -> KNOWNFOLDER_DEFINITION
                        sName = BStrFromLPWStr(pKFD.pszName) 'get name
                        
                        sPath = "": lpPath = 0
                        
                        On Error Resume Next
                        pKF.GetShellItem KF_FLAG_DEFAULT, IID_IShellItem, pItem
                        On Error GoTo 0
                        
                        'RETRIEVE PATH - method 1 (IShellItem -> GetDisplayName (SIGDN_FILESYSPATH))
                        
                        If Not (pItem Is Nothing) Then
                            'try root
                            pItem.GetAttributes SFGAO_FILESYSTEM, flags
                            If flags And SFGAO_FILESYSTEM Then
                                pItem.GetDisplayName SIGDN_FILESYSPATH, lpPath
                                sPath = BStrFromLPWStr(lpPath, True)
                            Else
                                'if no success -> try child
                                
                                If sName <> "NetworkPlacesFolder" Then 'it can freeze the program
                                
                                    pItem.BindToHandler ByVal 0&, BHID_EnumItems, IID_IEnumShellItems, penum1
            
                                    If Not (penum1 Is Nothing) Then
                                        If penum1.Next(1&, pChild, pcl) = S_OK Then
                                            pChild.GetAttributes SFGAO_FILESYSTEM, flags
                                            If flags And SFGAO_FILESYSTEM Then
                                                pChild.GetDisplayName SIGDN_FILESYSPATH, lpPath
                                                sPath = BStrFromLPWStr(lpPath, True)
                                            End If
                                        End If
                                    End If
                                End If
                            End If
                        End If
                        
                        'RETRIEVE PATH - method 2 (IKnownFolder -> GetPath)
    
                        sPath = "": lpPath = 0
    
                        flags = (KF_FLAG_SIMPLE_IDLIST Or KF_FLAG_DONT_VERIFY Or KF_FLAG_DEFAULT_PATH Or KF_FLAG_NOT_PARENT_RELATIVE)
                        On Error Resume Next
                        pKF.GetPath flags, lpPath           'IKnownFolder -> physical path
                        If Err.Number = 0 And lpPath <> 0 Then
                            sPath = BStrFromLPWStr(lpPath, True)
                        End If
                        On Error GoTo 0
                        
                        Debug.Print sName & " = " & sPath
                    End If
                End If
            Next
            
            CoTaskMemFree pakfid
        end if
            
        Set pKFM = Nothing
    End Sub
    
    Private Function BStrFromLPWStr(lpWStr As Long, Optional ByVal CleanupLPWStr As Boolean = True) As String
        If lpWStr = 0 Then Exit Function
        SysReAllocString VarPtr(BStrFromLPWStr), lpWStr
        If CleanupLPWStr Then CoTaskMemFree lpWStr
    End Function
    Attached Files Attached Files
    Last edited by Dragokas; Aug 3rd, 2018 at 01:30 AM. Reason: Fixed the code

  15. #15
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    532

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

    fafalone, by the way, KNOWN_FOLDER_FLAG Enumeration is updated.

  16. #16

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

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

    Thanks, will include the updated def with next release.

    I'm not following you with the User vs. Public issue; I looked at both results and the only difference I see is Library objects where the shell item gives the default save location (more useful) and the getpath gives the actual object file. And current drive recycler parsing path vs. nothing.


    If you plan on really doing anything with these folders, you're probably going to want SIGDN_DESKTOP_ABSOLUTEPARSING, you'll lose the friendly name in the root but be able to enumerate/read/write the ones currently returning blank in cases where they contain file sys objects.

  17. #17
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    532

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

    Quote Originally Posted by fafalone
    I'm not following you with the User vs. Public issue
    There are LOT of mistakes and differences:

    m1:
    MusicLibrary = C:\Users\Public\Music\Sample Music
    VideosLibrary = C:\Users\Public\Videos\Sample Videos
    RecycleBinFolder = C:\$Recycle.Bin\S-1-5-21-4161311594-4244952198-1204953518-1000
    LocalizedResourcesDir =
    UsersFilesFolder = C:\Users\Alex
    MyComputerFolder = C:\
    DocumentsLibrary = C:\Users\Alex\Documents\Bandicut
    PicturesLibrary = C:\Users\Public\Pictures\Sample Pictures
    Original Images =
    SamplePlaylists =
    Playlists =
    OEM Links =
    Libraries =
    m2:
    MusicLibrary = C:\Users\Alex\AppData\Roaming\Microsoft\Windows\Libraries\Music.library-ms
    VideosLibrary = C:\Users\Alex\AppData\Roaming\Microsoft\Windows\Libraries\Videos.library-ms
    RecycleBinFolder =
    LocalizedResourcesDir = C:\Windows\resources\0419
    UsersFilesFolder =
    MyComputerFolder =
    DocumentsLibrary = C:\Users\Alex\AppData\Roaming\Microsoft\Windows\Libraries\Documents.library-ms
    PhotoAlbums = C:\Users\Alex\Pictures\Slide Shows
    PicturesLibrary = C:\Users\Alex\AppData\Roaming\Microsoft\Windows\Libraries\Pictures.library-ms
    Original Images = C:\Users\Alex\AppData\Local\Microsoft\Windows Photo Gallery\Original Images
    SamplePlaylists = C:\Users\Public\Music\Sample Playlists
    Playlists = C:\Users\Alex\Music\Playlists
    OEM Links = C:\ProgramData\OEM Links
    Libraries = C:\Users\Alex\AppData\Roaming\Microsoft\Windows\Libraries
    The only place m1 is suitable I think for "UsersFilesFolder", and shell subitem method for "RecycleBinFolder".

    BTW. Don't you know what is the difference between:
    Code:
    "Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
    "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
    I saw somewhere recommendations to use only User Shell Folders. However, by comaparing this list with IKnownFolderManager method, I see that special folders taken from both reg. keys.
    There are some duplicates. Curious, how they interact.
    Of course, in XP it's better to use SHGetFolderPath.

    If you plan on really doing anything with these folders, you're probably going to want SIGDN_DESKTOP_ABSOLUTEPARSING
    Thank you. SIGDN_DESKTOP_ABSOLUTEPARSING returns a little bit better results that m2, like:
    CSCFolder = csc://{S-1-5-21-4161311594-4244952198-1204953518-1000}
    DocumentsLibrary = ::{031E4825-7B94-4DC3-B131-E946B44C8DD5}\Documents.library-ms
    Games = ::{ED228FDF-9EA8-4870-83B1-96B02CFE0D52}
    instead of:
    CSCFolder =
    DocumentsLibrary = C:\Users\Alex\AppData\Roaming\Microsoft\Windows\Libraries\Documents.library-ms
    Games =
    However, I don't need shell parsing names. And m2 is exactly what I want. My goal: to create a log where specialist can evaluate anomalies in system settings and make decision is system malfunction caused by them; or just for cases where need to do identification where some spec.folder located, like if somebody optimized free space on C: using moving spec.folder / symlink method or w/e and if now he no longer remembers where and what he moved

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