Jump to content

ObjCreateInterface example: Enumerating and Browsing the Desktop

Recommended Posts

Posted Image

Windows 7

I've had a chance to test the script on Windows 7.

To be able to browse system folders e.g. the Control Panel on Windows 7 you have to run the script, EnumDesktopGUI.au3, in the proper way. If you are using Windows 7 64 bit you have to run the script as a 64 bit program to be able to browse the system folders.

On Windows XP the generel look and appearance of the GUI is OK. There are problems on Windows 7: The menu bar doesn't look good. Disabled icons are looking very bad. The drop down menu of some of the buttons can erase parts of other controls. The grouping in the ListView of the items in Computer folder is looking very clumsy and the items seems to be mixed up in some way. Also for the Control Panel some items are mixed up and some items appears both as folders and files. I will try to find a solution to these issues.

Update #2: 2012-08-20

See first post dated 2012-04-19 below.

zip-files are attached to the bottom of the post.

Severe error

Fixed an error which could lead to a crash immediately before GUI was closed down.

Data for the TreeView was deleted and memory freed while the TreeView still had focus.


_GUICtrlListView_RegisterSortCallBack() and related functions are used for sorting. The functions are modified to group files and folders, and to properly sort by size, by type and by time. The functions are provided in the file SortListView.au3.

It can be time consuming to sort a large number of files and folders. To prevent using time on both updating and sorting the ListView when a new folder is selected in the TreeView, the sorting mechanism is reset to default when a new folder is selected. The default sorting is the order of files and folders as provided in the "While $IEnumIDList.Next() ... WEnd" loops.

Virtual folders

It can be a lengthy process to expand a large number of subfolders in the TreeView. To avoid this multiple subfolders can be grouped into a number of virtual folders with a specific number of subfolders in each virtual folder. The virtual folders are only created in the TreeView and ListView. Not on the disk.

Virtual folders will be used when the number of subfolders exceeds 300. The number of subfolders in each virtual folder is set to 100. These limits can be changed in the options. And the functionality can be disabled completely.

This is testet in a folder with 4000 subfolders and 100 pictures in each subfolder. Expanding the subfolders is much much faster when using virtual folders. In this example there will be created 40 virtual folders named "1 - 100", "101 - 200", ..., "3901 - 4000". Each virtual folder will contain 100 subfolders. But the 100 subfolders will not be expanded until you click a virtual folder. That means that in stead of creating 4000 subfolders at one time (which is extremely time consuming) there will be created 40 virtual folders, and when you click a virtual folder there will be created 100 subfolders at a time.

Other updates

Right click menu in TreeView: Refresh

Added a SplitterBar control between the TreeView and ListView

Added grouping information to the ListView for My Computer folder

If an icon file EnumDesktopGUI.ico exists, this icon will be used for the GUI

Option to save GUI position and size on exit

Option to set TreeView startup folder

More info to the Details View

Fixed some minor issues

Update #1: 2012-04-25

In post #3 wraithdu pointed out that the built-in function ObjCreateInterface could be used in stead of AutoItObject.

An advantage in using ObjCreateInterface is that according to the helpfile you can use a struct as a parameter in the description string for the methods of the interface. It works in this example. No need for a lot of calls to DllStructGetPtr().

With ObjCreateInterface enumeration of a folder looks like this:

; The address that receives an IShellFolder Interface pointer
$pParentFolder = GetParentFolder( ... )

; Create an IDispatch-Object for the IShellFolder Interface
$IShellFolder = ObjCreateInterface( $pParentFolder, $sIID_IShellFolder, $dtagIShellFolder )

; $pIEnumIDList is the address that receives an IEnumIDList interface pointer of the enumeration object
If $IShellFolder.EnumObjects( $NULL, BitOR( $SHCONTF_FOLDERS, $SHCONTF_INCLUDEHIDDEN ), $pIEnumIDList ) = $S_OK Then

    ; Create an IDispatch-Object for the IEnumIDList Interface
    $IEnumIDList = ObjCreateInterface( $pIEnumIDList, $sIID_IEnumIDList, $dtagIEnumIDList )

    ; Enumerate the folders                                  ; Param 1 [in] : Step value as in a For-loop
    While $IEnumIDList.Next( 1, $pidlRel, $iFetched ) = $S_OK ; Param 2 [out]: PIDL relative to parent folder
        $iFolders += 1                                       ; Param 3 [out]: 0 if no more PIDLs, 1 else

The zipfile below is updated with new au3-files (the 3 EnumDesktop-files) that uses ObjCreateInterface. This version is not depending on AutoItObject and AutoItObject.au3 is not included.

In this example ObjCreateInterface seems to be working flawlessly.


I've been looking at AutoItObject for some time. I've got inspiration for this example from a similar C++ example and from this thread http://www.autoitscript.com/forum/index.php?showtopic=123365.

The GUI in the example is a Windows Explorer Viewer with a TreeView and a ListView. The root of the TreeView is the Desktop. You can browse the Desktop by expanding the folders in the TreeView. Select a folder to show the subfolders and files in the ListView. Right click on an item in the ListView to get file properties. Right click in the free area of the ListView to print a list of item names. Double click or press Enter on an item in the ListView to open a file or execute a program.

With AutoItObject a folder doesn't have to be a file system folder. It can be any folder in the Desktop e.g. the Control Panel. The Desktop in this case means the root of the Shell's namespace.

To enumerate a folder using AutoItObject you use code like this:

; The address that receives an IShellFolder Interface pointer
$pParentFolder = GetParentFolder( ... )

; Create an IDispatch-Object for the IShellFolder Interface
$IShellFolder = _AutoItObject_WrapperCreate( $pParentFolder, $dtagIShellFolder )

; The address that receives an IEnumIDList interface pointer of the enumeration object
$aRet = $IShellFolder.EnumObjects( $NULL, BitOR( $SHCONTF_FOLDERS, $SHCONTF_INCLUDEHIDDEN ), 0 )
If $aRet[0] = $S_OK Then

    $pIEnumIDList = $aRet[3]

    ; Create an IDispatch-Object for the IEnumIDList Interface
    $IEnumIDList = _AutoItObject_WrapperCreate( $pIEnumIDList, $dtagIEnumIDList )

    ; Enumerate the folders
    $aRet = $IEnumIDList.Next( 1, 0, 0 ) ; Param 1 [in] : Step value as in a For-loop
    While $aRet[3]                       ; Param 2 [out]: PIDL relative to parent folder
        $iFolders += 1                   ; Param 3 [out]: 0 if no more PIDLs, 1 else
        $aRet = $IEnumIDList.Next( 1, 0, 0 )

Enumeration of a folder in this way uses PIDLs (pointers to ITEMIDLIST structures) in stead of file and folder names. See http://msdn.microsoft.com/en-us/library/windows/desktop/cc144090(v=vs.85).aspx.

The zipfile below contains a number of files:

WSP.dll is used for NM_RETURN notifications in the ListView.

The central file in the example is EnumDesktopFuncs.au3. The most important functions in the file are

  • GetParentFolder() - returns an IShellFolder Interface pointer to the parent folder
  • EnumTreeViewObjects() - expands the folders of the parent folder in the TreeView
  • EnumListViewObjects() - shows the folders and files in the ListView for the parent folder
  • InvokeCommand() - opens a file or runs a program when an item in the ListView is activated
You need AutoItObject by the AutoItObject team and APIConstants.au3 and WinAPIEx.au3 version 3.6 or 3.7 by Yashied.

If the script fails for some reason and you are left with an hourglass cursor, then run HourglassOff.au3 to get the normal cursor back.

Run EnumDesktopGUI.au3 in the zipfile.

Testet on XP SP3.

The zip-files can be opened with 7-Zip.

2012-04-19: Implemented with AutoItObject


Update #2 2012-08-20: Implemented with ObjCreateInterface() (no need for AutoItObject, not included)



Edited by LarsJ
Link to post
Share on other sites

Nice peace of code - I like it!

Is it running properly on Win7 x64!

Well done!



Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to post
Share on other sites

Lilbert, Thank you for pointing that out.

Now I realize that UEZ was reporting an error (propably the same). I misunderstood. Sorry for that.

I have no access to Win7 X64 for the moment. Could someone with access to Win7 X64 test the following 2 lines of code for me in the script below.

$r = $IShellFolder.GetDisplayNameOf( Number($pidlRel), 0, Number(DllStructGetPtr($tSTRRET)) )   ; <<<<<<<<<<<<<<<<<<<
;$r = $IShellFolder.GetDisplayNameOf( $pidlRel, 0, DllStructGetPtr( $tSTRRET ) )                ; <<<<<<<<<<<<<<<<<<<

Both of these lines works on XP. Probably only the first line works on Win7 X64. I would like to get that confirmed.

In my project I consistently uses the second version of the lines, so that could be the reason for the errors.

The script displays the name of the running au3-file in a MsgBox.

#include <AutoItObject.au3>

Global Const $sIID_IShellFolder = "{000214E6-0000-0000-C000-000000000046}"
Global Const $dtagIShellFolder = $dtagIUnknown & _
  "ParseDisplayName hresult(hwnd;ptr;wstr;dword*;ptr*;dword*);" & _
  "EnumObjects hresult(hwnd;dword;ptr*);" & _
  "BindToObject hresult(ptr;ptr;ptr;ptr*);" & _
  "BindToStorage hresult(ptr;ptr;ptr;ptr*);" & _
  "CompareIDs hresult(lparam;ptr;ptr);" & _
  "CreateViewObject hresult(hwnd;ptr;ptr*);" & _
  "GetAttributesOf hresult(UINT;ptr;ulong*);" & _
  "GetUIObjectOf hresult(hwnd;uint;ptr;ptr;uint*;ptr*);" & _
  "GetDisplayNameOf hresult(ptr;uint;ptr);" & _
  "SetNameOf hresult(hwnd;ptr;wstr;dword;ptr*);"


Local $pidl = _ILCreateFromPath( @ScriptFullPath )
Func _ILCreateFromPath($sPath)
  Local $aRes = DllCall("shell32.dll", "ptr", "ILCreateFromPathW", "wstr", $sPath)
  If @error Then Return SetError(1, 0, 0)
  Return $aRes[0]

Local $pIShellFolder, $pidlRel, $r
Local $tRIID = _AutoItObject_CLSIDFromString( $sIID_IShellFolder )
;$r = _SHBindToParent( $pidl, DllStructGetPtr( $tRIID ), $pIShellFolder, $pidlRel )
$r = _SHBindToParent( Number($pidl), Number(DllStructGetPtr($tRIID)), $pIShellFolder, $pidlRel )
Func _SHBindToParent( $pidl, $riid, ByRef $pIShellFolder, ByRef $pidlRel )
  Local $aRes = DllCall("Shell32.dll", "long", "SHBindToParent", "ptr", $pidl, "ptr", $riid, "ptr*", 0, "ptr*", 0)
  If @error Then Return SetError(1, 0, 0)
  $pIShellFolder = $aRes[3]
  $pidlRel = $aRes[4]
  Return $aRes[0]

Local $tSTRRET = DllStructCreate("uint uType;ptr data;")
Local $IShellFolder = _AutoItObject_WrapperCreate( $pIShellFolder, $dtagIShellFolder )
$r = $IShellFolder.GetDisplayNameOf( Number($pidlRel), 0, Number(DllStructGetPtr($tSTRRET)) )   ; <<<<<<<<<<<<<<<<<<<
;$r = $IShellFolder.GetDisplayNameOf( $pidlRel, 0, DllStructGetPtr( $tSTRRET ) )                  ; <<<<<<<<<<<<<<<<<<<

Local $sName
;_StrRetToBuf(DllStructGetPtr($tSTRRET), 0, $sName, 512)
_StrRetToBuf(Number(DllStructGetPtr($tSTRRET)), 0, $sName, 512)
Func _StrRetToBuf($pSTRRET, $pidl, ByRef $sName, $iLength = 512)
  Local $aRes = DllCall("shlwapi.dll", "long", "StrRetToBufW", "ptr", $pSTRRET, "ptr", $pidl, "wstr", $sName, "uint", $iLength)
  If @error Then Return SetError(1, 0, 0)
  $sName = $aRes[3]
  Return $aRes[0]

MsgBox( 0, "Name of file", $sName )

$IShellFolder = 0
$tSTRRET = 0
Edited by LarsJ
Link to post
Share on other sites

Both lines work fine for me on Win7 64bit, running Autoit as 32bit as well as 64bit.

Edit: And your example "EnumDesktopGUI.au3" works fine for me too, running Autoit as 32bit as well as 64bit. Nice program ;)!

Edited by KaFu
Link to post
Share on other sites

trancexx, Thank you. I'll hope you'll continue developing this function, ObjCreateInterface().

Lilbert, Are you running the scripts on a server? That could give some problems. When EnumDesktopGUI.au3 starts up, it expands the Desktop and "My Computer" folder to fill out a few items in the TreeView and to fill out the ListView. If there is an item in one of these folders that needs special treatment because it's a server, that could be the problem.

Whether it's a server or not, I think it's an item in the Desktop or "My Computer" folder that's the reason for the error. We have to figure out which item it is.

Will you take a look at the next post and then open and run FileListDesktop.au3 and FileListSystemFolder.au3 in Scite. One of the scripts or may be both will probably fail. But before they fails they will print some of the files and folders in the console. If you open Windows Explorer and compares the contents of the Desktop folder and "My Computer" folder with the output in the console, it should be possible to figure out which item is the reason for the error.

When I run FileListDesktop.au3 and FileListSystemFolder.au3 on my XP the output looks like this:

C:\Documents and Settings\LJ\Desktop
    Internet Explorer
    Command Prompt
    Date and Time
    Internet Explorer
    Media Player Classic
    Microsoft Office Access 2003
    Microsoft Office Outlook 2003
    Microsoft Visual FoxPro 9.0
    Microsoft Visual Studio 2008
    Windows Explorer
    Windows Media Player
    Windows PowerShell
    DAEMON Tools
My Computer
My Network Places
My Documents
Recycle Bin

My Computer\
    Control Panel\
    My Documents\
    D:\My Documents\
    Shared Documents\
    C:\Documents and Settings\All Users\Documents\
    SYSTEM (C:)\
    DATA (D:)\
    PROGRAMS (E:)\
    BACKUP (G:)\
    DVD-RAM Drive (H:)\
    DVD Drive (M:)\
Edited by LarsJ
Link to post
Share on other sites

The zipfile below contains 5 small au3-files to print the contents of the Desktop folder, a system folder (e.g. "My Computer" or "Control Panel") and a directory (filesystem folder) in the Scite console.

  • FileListConsts.au3 - declarations
  • FileListFuncs.au3 - functions
  • FileListDesktop.au3 - print the contents of the Desktop folder
  • FileListSystemFolder.au3 - print the contents of a system folder
  • FileListDirectory.au3 - print the contents of a directory
FileListSystemFolder.au3 and FileListDirectory.au3 uses recursion to print the contents of subfolders. You can set the recursion level to specify how many recursions you want to make. The default is 1 which means that the contents of the specified folder is printed, but no subfolders.


Edit: Added indentation for subfolders.

Edited by LarsJ
Link to post
Share on other sites
  • 3 months later...

Hello LarsJ, first of all the script is excellent, I learn a lot from it.

I´ve got a question, perhaps you can help me. Is it possible to sort the list view?

I was trying to sort the ListView by different methods but all failed.

I tried to register a callback function at the begining with: GUICtrlRegisterListViewSort(-1, "LVSort") but for some reason it doesn´t work.

Then I tied to trap the columns header clicked with WM_NOTIFY but I also failed. Follow the code:

Case $LVN_COLUMNCLICK ;ListView column clicked
     Local $tInfo = DllStructCreate($tagNMLISTVIEW, $ilParam)
     Local $iCol = DllStructGetData($tInfo, "SubItem") ;Column clicked starting from 0

     Local $tNMLVCUSTOMDRAW = DllStructCreate($tagNMLVCUSTOMDRAW, $ilParam)
     Local $dwItemSpec = DllStructGetData($tNMLVCUSTOMDRAW, "dwItemSpec") ; Item index
     Local $iSubItem   = DllStructGetData($tNMLVCUSTOMDRAW, "iSubItem")  ; Subitem index
   LVSort($hLV, $dwItemSpec, $dwItemSpec, $iCol)

Just in case you can help me I attach the sort function I get from the forum:

Func LVSort($hWnd, $nItem1, $nItem2, $nColumn)
Local $val1, $val2, $nResult
; Switch the sorting direction
If $nColumn = $nCurCol Then
  If Not $bSet Then
   $nSortDir = $nSortDir * - 1
   $bSet = 1
  $nSortDir = 1
$nCol = $nColumn
$val1 = GetSubItemText($hWnd, $nItem1, $nColumn)
$val2 = GetSubItemText($hWnd, $nItem2, $nColumn)
; If it is the 3rd colum (column starts with 0) then compare the dates
If $nColumn = 2 Then
  $val1 = StringRight($val1, 4) & StringMid($val1, 4, 2) & StringLeft($val1, 2)
  $val2 = StringRight($val2, 4) & StringMid($val2, 4, 2) & StringLeft($val2, 2)
$nResult = 0 ; No change of item1 and item2 positions
If $val1 < $val2 Then
  $nResult = -1 ; Put item2 before item1
ElseIf $val1 > $val2 Then
  $nResult = 1 ; Put item2 behind item1
$nResult = $nResult * $nSortDir
Return $nResult
EndFunc   ;==>LVSort

; Retrieve the text of a listview item in a specified column
Func GetSubItemText($nCtrlID, $nItemID, $nColumn)
Local $stLvfi = DllStructCreate("uint;ptr;int;int[2];int")
Local $nIndex, $stBuffer, $stLvi, $sItemText
DllStructSetData($stLvfi, 1, $LVFI_PARAM)
DllStructSetData($stLvfi, 3, $nItemID)
$stBuffer = DllStructCreate("char[260]")
$nIndex = GUICtrlSendMsg($nCtrlID, $LVM_FINDITEM, -1, DllStructGetPtr($stLvfi));
$stLvi = DllStructCreate("uint;int;int;uint;uint;ptr;int;int;int;int")
DllStructSetData($stLvi, 1, $LVIF_TEXT)
DllStructSetData($stLvi, 2, $nIndex)
DllStructSetData($stLvi, 3, $nColumn)
DllStructSetData($stLvi, 6, DllStructGetPtr($stBuffer))
DllStructSetData($stLvi, 7, 260)
GUICtrlSendMsg($nCtrlID, $LVM_GETITEMA, 0, DllStructGetPtr($stLvi));
$sItemText = DllStructGetData($stBuffer, 1)
$stLvi = 0
$stLvfi = 0
$stBuffer = 0
Return $sItemText
EndFunc   ;==>GetSubItemText

Thanks a lot and regards.

Link to post
Share on other sites
Link to post
Share on other sites

Hi LarsJ, I added a drag bar to your script. It works perfectly right until I resize the main window. I attach the drag routine I created.

;Drag Bar------------------------------------------------------
Global $DragBarWhidth=5, $DragBarLimit=70
Global $LPos=ControlGetPos($hGui, "", $idTV)
Global $RPos=ControlGetPos($hGui, "", $idLV)
Global $WPos = WinGetPos($hGui)
Global $DBLeftLimit=$Lpos[0]+$DragBarLimit,$DBRightLimit=$WPos[2]-$DragBarLimit
Global $DragBar=GUICtrlCreateLabel("",$LPos[2]-($DragBarWhidth/2),$LPos[1],$DragBarWhidth,$LPos[3], -1, 0x00000001) ;separator rezising bar $WS_EX_DLGMODALFRAME
GUICtrlSetCursor(-1, 13)
Global $Pos=GUIGetCursorInfo($hGui)
Global $DPos=ControlGetPos($hGui,"",$DragBar);position of the control relative to its parent window
ControlMove($hGui,"",$idLV,$DPos[0]+$DragBarWhidth,$RPos[1], $WPos[2]-$DPos[0], $RPos[3])
;Drag Bar end------------------------------------------------------

;In the main Loop
   Case $DragBar
     $LPos=ControlGetPos($hGui, "", $idTV)
     $RPos=ControlGetPos($hGui, "", $idLV)
     $WPos = WinGetPos($hGui)
      If $Pos[0]>$DBLeftLimit And $Pos[0]<$DBRightLimit Then ControlMove($hGui,"",$DragBar,$Pos[0],$DPos[1],$DPos[2],$DPos[3])
     Until $Pos[2]=0 ;$pos[2]=Primary down (1 if pressed, 0 if not pressed)
     If $Pos[0]<$DBLeftLimit Then
      ControlMove($hGui, "",$idTV,$LPos[0],$LPos[1],$DPos[0]-$DragBarWhidth,$LPos[3])
      If $Pos[0]>$DBRightLimit Then
       ControlMove($hGui,"",$idLV,$Pos[0]+$DragBarWhidth,$RPos[1], $WPos[2]-$Pos[0], $RPos[3])
     GUISetState(@SW_ENABLE, $hGui)
     GUICtrlSetState($DragBar, $GUI_FOCUS)

Another thing I noticed, the ListView scroll bar is hidden.

Perhaps you can help me.

I appreciate it.


Link to post
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
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Tippex
      I have a problem trying to use _IEAttach to recover control after a Windows Explorer reset (e.g. on Windows 7), since all the 
      InternetExplorer.Application objects appear to be destroyed by this event. An example of this is when a PC has some kind of fault causing a pop-up error message "Windows Explorer has stopped working". I can simulate this event by using Windows Task Manager to end the Explorer.exe process then to file, new task (run...) Explorer.exe
      After this event, _IEAttach gives an @error of: 
      7 ($_IEStatus_NoMatch) - No Match
      However, using WinList() followed by WinGetText() I can recover the URL for an IE session that has lost its 
      InternetExplorer.Application object which could be used to _IECreate a new session (the $iTryAttach fails) and WinKill() the original session but the result is a if a refresh had been done (any form entries are lost) . Unfortunately form entries do not appear as text or hidden text to Windows so I'm looking for the best way to read the contents of an Internet Explorer screen (even if just text without structure). 
      When there is no InternetExplorer.Application object (because of some Windows fault) but still with an Internet Explorer session running, I tried to read its contents by a "Select All" & "Copy to Clipboard" but its form entries are blanked and combo boxes choices are all displayed with no way of finding out which ones were selected.
      Local $ClipBoardSave = _ClipBoard_GetData() ;not ClipGet() which is text only ClipPut("") ;Clear User's Clipboard Local $sText = "" Local $hWnd = WinActivate("Window Title", "") If IsHWnd($hWnd) Then Local $bStatusCtrlSend = ControlSend("Window Title", "", "", "^a^c") If $bStatusCtrlSend Then Local $bStatusCtrlClick = ControlClick("Window Title", "", "") If $bStatusCtrlClick Then $sText = ClipGet() _ClipBoard_SetData($ClipBoardSave) ;not ClipPut($ClipBoardSave) which is text only  
      Is  there a solution to this please?
  • Create New...