Here we go. I hope the comments make it understandable.
Basically all you need is the IShellFolder Interface to walk the shell namespace.
Code: Select all
; Special Shell folders CLSID values:
;
; Note: those are "relative" values, so since the control panel is inside
; "My Computer", the path to it is actually this:
; "::{20d04fe0-3aea-1069-a2d8-08002b30309d}\::{21ec2020-3aea-1069-a2dd-08002b30309d}"
; Some can be used directly (like My Network Places), but most need to
; be put together with their parents CLSIDs to work.
;
; ::{d20ea4e1-3957-11d2-a40b-0c5020524153} Administrative Tools
; ::{85bbd920-42a0-1069-a2e4-08002b30309d} Briefcase
; ::{21ec2020-3aea-1069-a2dd-08002b30309d} Control Panel
; ::{d20ea4e1-3957-11d2-a40b-0c5020524152} Fonts
; ::{ff393560-c2a7-11cf-bff4-444553540000} History
; ::{00020d75-0000-0000-c000-000000000046} Inbox
; ::{00028b00-0000-0000-c000-000000000046} Microsoft Network
; ::{20d04fe0-3aea-1069-a2d8-08002b30309d} My Computer
; ::{450d8fba-ad25-11d0-98a8-0800361b1103} My Documents
; ::{208d2c60-3aea-1069-a2d7-08002b30309d} My Network Places
; ::{1f4de370-d627-11d1-ba4f-00a0c91eedba} Network Computers
; ::{7007acc7-3202-11d1-aad2-00805fc1270e} Network Connections
; ::{2227a280-3aea-1069-a2de-08002b30309d} Printers And Faxes
; ::{7be9d83c-a729-4d97-b5a7-1b7313c39e0a} Programs Folder
; ::{645ff040-5081-101b-9f08-00aa002f954e} Recycle Bin
; ::{e211b736-43fd-11d1-9efb-0000f8757fcd} Scanners And Cameras
; ::{d6277990-4c6a-11cf-8d87-00aa0060f5bf} Scheduled Tasks
; ::{48e7caab-b918-4e58-a94d-505519c795dc} Start Menu Folder
; ::{7bd29e00-76c1-11cf-9dd0-00a0c9034933} Temporary Internet Files
; ::{bdeadf00-c265-11d0-bced-00a0c90ab50f} Web Folders
; Some stuff that is needed:
;
#SHCONTF_FOLDERS = $0020; // only want folders enumerated (SFGAO_FOLDER)
#SHCONTF_NONFOLDERS = $0040; // include non folders
#SHCONTF_INCLUDEHIDDEN = $0080; // show items normally hidden
#SHCONTF_INIT_ON_FIRST_NEXT = $0100; // allow EnumObject() To Return before validating enum
#SHCONTF_NETPRINTERSRCH = $0200; // hint that client is looking For printers
#SHCONTF_SHAREABLE = $0400; // hint that client is looking sharable resources (remote shares)
#SHCONTF_STORAGE = $0800; // include all items With accessible storage And their ancestors
#_SHGDN_NORMAL = $0000; // Default (display purpose)
#_SHGDN_INFOLDER = $0001; // displayed under a folder (relative)
#_SHGDN_FOREDITING = $1000; // For in-place editing
#_SHGDN_FORADDRESSBAR = $4000; // UI friendly parsing name (remove ugly stuff)
#_SHGDN_FORPARSING = $8000; // parsing name For ParseDisplayName()
Structure STRRET
uType.l
StructureUnion
*pOleStr
uOffset.l
cStr.b[#MAX_PATH]
EndStructureUnion
EndStructure
#STRRET_WSTR = 0
#STRRET_OFFSET = $1
#STRRET_CSTR = $2
DataSection
IID_IShellFolder: ; {000214E6-0000-0000-C000-000000000046}
Data.l $000214E6
Data.w $0000, $0000
Data.b $C0, $00, $00, $00, $00, $00, $00, $46
EndDataSection
; Helper function to read the content of the filled STRRET structure
; correctly, and release any memory it allocated.
;
Procedure.s ReadSTRRET(*ret.STRRET)
If *ret\uType = #STRRET_WSTR And *ret\pOleStr
Result$ = PeekS(*ret\pOleStr, -1, #PB_Unicode)
CoTaskMemFree_(*ret\pOleStr)
*ret\pOleStr = 0
ElseIf *ret\uType = #STRRET_OFFSET
Result$ = "" ; SDK is not fully clear on how to handle this.
ElseIf *ret\uType = #STRRET_CSTR
Result$ = PeekS(@*ret\cstr[0], #MAX_PATH, #PB_Ascii)
EndIf
ProcedureReturn Result$
EndProcedure
; Function to recursively scan a Shell folder up to a maximum level
; of recursivity (so not the entire file structure is read.)
;
Procedure ScanShellFolder(Folder.IShellFolder, Sublevel, MaxSublevel)
; Try to get an enumerator object for the children of this object.
; Play around a bit with the flags to exclude certain objects from the enumeration
;
If Folder\EnumObjects(0, #SHCONTF_FOLDERS|#SHCONTF_NONFOLDERS|#SHCONTF_INCLUDEHIDDEN|#SHCONTF_SHAREABLE, @Enum.IEnumIDList) = #S_OK
; get the child *idl pointers from the enumerator object
;
While Enum\Next(1, @*idl.ITEMIDLIST, 0) = #NOERROR
; get the display name for the object
;
If Folder\GetDisplayNameOf(*idl, #_SHGDN_NORMAL, @result.STRRET) = #S_OK
Name$ = ReadSTRRET(@result)
EndIf
; get a "parsable" name for the object
; for files or normal folders, this is a path
;
If Folder\GetDisplayNameOf(*idl, #_SHGDN_FORPARSING, @result.STRRET) = #S_OK
Path$ = ReadSTRRET(@result)
EndIf
; add to tree
;
AddGadgetItem(0, -1, Name$ + " (" + Path$ + ")", 0, Sublevel)
index = CountGadgetItems(0)-1
If Sublevel <= MaxSublevel
;
; To scan the children of this item as well, we need to get a IShellFolder
; pointer from the *idl value
;
If Folder\BindToObject(*idl, 0, ?IID_IShellFolder, @NewFolder.IShellFolder) = #S_OK
; once again a scan
ScanShellFolder(NewFolder, Sublevel + 1, MaxSublevel)
NewFolder\Release()
EndIf
EndIf
SetGadgetItemState(0, index, #PB_Tree_Expanded)
; free idl memory
CoTaskMemFree_(*idl)
Wend
; free enumerator object
Enum\Release()
EndIf
EndProcedure
; Code Start
;
If OpenWindow(0, 0, 0, 400, 500, "Shell Folders", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
CreateGadgetList(WindowID(0))
TreeGadget(0, 10, 10, 380, 480)
; To get to a shell folder, we always start with the desktop object, so get it:
;
If SHGetDesktopFolder_(@Desktop.IShellFolder) = #NOERROR
; Now parse the CLSID to get to the "My Network Places".
; Try the second line to explore the content of the Control Panel
;
If Desktop\ParseDisplayName(0, 0, "::{208d2c60-3aea-1069-a2d7-08002b30309d}", 0, @*idl.ITEMIDLIST, 0) = #S_OK
;If Desktop\ParseDisplayName(0, 0, "::{20d04fe0-3aea-1069-a2d8-08002b30309d}\::{21ec2020-3aea-1069-a2dd-08002b30309d}", 0, @*idl.ITEMIDLIST, 0) = #S_OK
; What we have now is the *idl pointer. This sort of a path in the Shell
; From that we get a new IShellFolder object representing the "My Network Places"
;
If Desktop\BindToObject(*idl, 0, ?IID_IShellFolder, @Network.IShellFolder) = #S_OK
; Lets get the display name of "My Network Places"
; This is done on the parent object, with our *idl
;
If Desktop\GetDisplayNameOf(*idl, #_SHGDN_NORMAL, @result.STRRET) = #S_OK
AddGadgetItem(0, -1, ReadSTRRET(@result), 0, 0)
EndIf
; call our scan procedure for this object. max 4 levels depth
;
ScanShellFolder(Network, 1, 3)
SetGadgetItemState(0, 0, #PB_Tree_Expanded)
; Release objects
Network\Release()
EndIf
; the memory used by an IDL must be freed
CoTaskMemFree_(*idl)
EndIf
; Release desktop object
Desktop\Release()
EndIf
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
End