Jump to content

Automating File/Windows Explorer and Desktop


LarsJ
 Share

Recommended Posts

This is an update of the old Automating Windows Explorer example. The update includes Desktop automation. However, Windows XP code has been removed.
 

Threads
Other threads related to File/Windows Explorer:

Some of these threads are very old. I'm considering updating some of the examples: Remove Windows XP code. Implement some of the code in other ways.

Enumerating and Browsing the Desktop is important to me personally because it was the first time I used the ObjCreateInterface() function. The first version of the example was based on _AutoItObject_WrapperCreate() from the AutoItObject UDF. Then I was told that you can use ObjCreateInterface() instead. Of course I had to try.
 

Automating File/Windows Explorer and Desktop
Automating File/Windows Explorer
The old example contains a description of the techniques for automating File/Windows Explorer.

The techniques are based on COM interfaces. Initially, it's about getting an IShellBrowser interface based on a File/Windows Explorer window handle.

An IDispatch interface for the window is important for creating the IShellBrowser interface.

Through the IShellBrowser interface, you can generate a large number of interfaces that can be used to implement the automation functions.
 

Automating Desktop
Bilgus figured out how to get an IDispatch interface for the Desktop in this post:

$oIShellWindows.FindWindowSW( Null, Null, $SWC_DESKTOP, $hWnd, $SWFO_NEEDDISPATCH, $pIDispatch )

This is the part of the old code in the GetIShellBrowser() function that needs to be updated to include the Desktop:

; Get an IWebBrowserApp object for each window
; This is done in two steps:
; 1. Get an IDispatch object for the window
; 2. Get the IWebBrowserApp interface
; Check if it's the right window
Local $pIDispatch, $oIDispatch
Local $pIWebBrowserApp, $oIWebBrowserApp, $hWnd
For $i = 0 To $iWindows - 1
  $oIShellWindows.Item( $i, $pIDispatch )
  If $pIDispatch Then
    $oIDispatch = ObjCreateInterface( $pIDispatch, $sIID_IDispatch, $dtag_IDispatch )
    $oIDispatch.QueryInterface( $tRIID_IWebBrowserApp, $pIWebBrowserApp )
    If $pIWebBrowserApp Then
      $oIWebBrowserApp = ObjCreateInterface( $pIWebBrowserApp, $sIID_IWebBrowserApp, $dtag_IWebBrowserApp )
      $oIWebBrowserApp.get_HWND( $hWnd )
      If $hWnd = $hExplorer Then ExitLoop
    EndIf
  EndIf
Next

And here the code to include the Desktop is added:

; Get an IWebBrowserApp object for each window
; This is done in two steps:
; 1. Get an IDispatch object for the window
; 2. Get the IWebBrowserApp interface
; Check if it's the right window
Local $pIDispatch, $oIDispatch, $hRes
Local $pIWebBrowserApp, $oIWebBrowserApp, $hWnd
For $i = 0 To $iWindows
  $hRes = $i < $iWindows ? $oIShellWindows.Item( $i, $pIDispatch ) _
                         : $oIShellWindows.FindWindowSW( Null, Null, $SWC_DESKTOP, $hWnd, $SWFO_NEEDDISPATCH, $pIDispatch )
  If $pIDispatch Then
    $oIDispatch = ObjCreateInterface( $pIDispatch, $sIID_IDispatch, $dtag_IDispatch )
    $oIDispatch.QueryInterface( $tRIID_IWebBrowserApp, $pIWebBrowserApp )
    If $pIWebBrowserApp Then
      $oIWebBrowserApp = ObjCreateInterface( $pIWebBrowserApp, $sIID_IWebBrowserApp, $dtag_IWebBrowserApp )
      $oIWebBrowserApp.get_HWND( $hWnd )
      If $hWnd = $hExplorer Then ExitLoop
    EndIf
  EndIf
Next

The For loop runs an extra round if a File/Explorer Window has not been identified. In this last loop, the FindWindowSW() method returns a window corresponding to the Desktop. Here, the method always returns the Program Manager window.

a consequence of this implementation is that if you specify a non-existent window as a parameter to the GetIShellBrowser() function, then the function will return the Program Manager window. Thus, the Program Manager window is the default window for the function.

The IDispatch interface is the important thing in terms of automating the Desktop. Then all the functions used in connection with a File/Windows Explorer window can also be used in connection with the Desktop. Except for a few functions that are not relevant for the Desktop.
 

Functions
The automation functions are coded in FileExplorer.au3. The functions are implemented using a number of Shell API functions and Shell COM interfaces coded in ShellFunctions.au3 and ShellInterfaces.au3.

The old example contains a list of implemented functions.

New functions

Examples
The 7z-file contains examples for automating the Desktop and a File/Windows Explorer window. These are the same examples as in this post. Note that the examples GetFiles.au3 and GetFolders.au3 also show how to make a list of selected files and folders.

Note that the GetSetIconView.au3 example can change the order of icons on the Desktop. If you don't want this, run this example only in a File/Windows Explorer window.

The GUI application that was used to demonstrate the features in the old version is not included. The small examples seems to be much more useful.

This post contains new examples.

In both the old and the new post, the examples are shown for a File/Windows Explorer window. But the 7z-file contains similar examples for the Desktop.
 

Forum examples
This is a list of the most interesting examples in the old thread:

 

7z-file
The 7z-file contains source code for the UDF and examples.

You need AutoIt 3.3.12 or later. Tested on Windows 7 and Windows 10.

Comments are welcome. Let me know if there are any issues.

FileExplorerAndDesktop.7z

Edited by LarsJ
Updates and new 7z-file
Link to comment
Share on other sites

Added more information to first post. Minor updates of the examples. New 7z-file at bottom of first post.

Link to comment
Share on other sites

  • 2 weeks later...

GetSortColumns()
Added a new function to show which column(s) a File/Windows Explorer or Desktop view is sorted by:

Func GetSortColumns()
  Local $iColumns ; Number of sort columns
  $oIFolderView2.GetSortColumnCount( $iColumns )

  ; Get sort columns
  Local $tagSORTCOLUMNS
  For $i = 0 To $iColumns - 1
    $tagSORTCOLUMNS &= $tagSORTCOLUMN & ";"
  Next
  Local $tSortColumns = DllStructCreate( $tagSORTCOLUMNS )
  $oIFolderView2.GetSortColumns( $tSortColumns, $iColumns )

  ; Get data from structure
  Local $tSC, $aSortColumns[2*$iColumns][2]
  For $i = 0 To $iColumns - 1
    $aSortColumns[2*$i+0][0] = "Sort"
    $aSortColumns[2*$i+1][0] = "Column"
    $tSC = DllStructCreate( $tagSORTCOLUMN, DllStructGetPtr( $tSortColumns ) + $i * DllStructGetSize( $tSortColumns ) )
    $aSortColumns[2*$i+0][1] = DllStructGetData( $tSC, "sort" )
    PSGetNameFromPropertyKey( DllStructGetPtr( $tSC ), $aSortColumns[2*$i+1][1] )
    If @error Then PSStringFromPropertyKey( DllStructGetPtr( $tSC ), $aSortColumns[2*$i+1][1] )
  Next

  ; Return sort columns
  Return $aSortColumns
EndFunc

Because the information is extracted from PropertyKeys, column names are displayed this way:

Name          -> System.ItemNameDisplay
Date modified -> System.DateModified
Type          -> System.ItemTypeText
Size          -> System.Size

When I sort files by Type in a Windows Explorer window, I get this information about the sort columns:

Sort   -> -1
Column -> System.ItemTypeText
Sort   -> 0
Column -> {EB4F9DAB-0000-0000-91B9-413D76000090} 0

There are two sort columns. The first is the Type column, which is sorted in descending order. The other doesn't immediately provide any recognizable information. But it's probably used by the internal Microsoft code.

Two new examples. One for the Desktop and one for an Explorer window.

Note that the Desktop example on Windows 10 simply shows an empty ArrayDisplay window. This is because the Name column for the Desktop is not provided with a sort. So $iColumns in the code box above gets the value zero. And thus $aSortColumns becomes an empty array. Probably there is no sorting for the other columns either (the columns can be seen in Details view).

Sorting for the Name column can be set with the code in the next post. And then the sort is displayed in the ArrayDisplay window.

Note that setting a sort for the Name column means that the icons on the Desktop are placed in a different order.

Edited by LarsJ
Updates
Link to comment
Share on other sites

SetSortColumns()
The SetSortColumns() function to set the sort for a column (the columns that can be seen in Details view) is coded this way:

Func SetSortColumns( $sGUID, $iPID, $iSort )
  ; Create SORTCOLUMN structure
  Local $tSortColumn = DllStructCreate( $tagSORTCOLUMN )
  _WinAPI_GUIDFromStringEx( $sGUID, $tSortColumn )
  DllStructSetData( $tSortColumn, "pid", $iPID )
  DllStructSetData( $tSortColumn, "sort", $iSort )

  ; Set sort columns
  $oIFolderView2.SetSortColumns( $tSortColumn, 1 )
EndFunc

This is the Microsoft definition of the IFolderView2.SetSortColumns() method:

HRESULT SetSortColumns(
  SORTCOLUMN *rgSortColumns, // Structure
  int        cColumns        // Integer, number of columns
)

// SORTCOLUMN structure
struct tagSORTCOLUMN {
  PROPERTYKEY   propkey;   // Structure
  SORTDIRECTION direction; // Integer, sort direction, 1 or -1
}

// PROPERTYKEY structure
struct tagPROPERTYKEY {
  GUID  fmtid; // Unique identifier of the PROPERTYKEY
  dword pid;   // Property identifier (PID)
}

And these are the AutoIt definitions:

; PROPERTYKEY Structure (GUID & PID)
Global Const $tagPROPERTYKEY = "struct;ulong Data1;ushort Data2;ushort Data3;byte Data4[8];endstruct" & ";dword pid"

; SORTCOLUMN Structure
Global Const $tagSORTCOLUMN = $tagPROPERTYKEY & ";int sort"

The information needed for the SetSortColumns() function to create the SORTCOLUMN structure is therefore $sGUID and $iPID, which defines the PROPERTYKEY structure, and $iSort, which defines the final SORTCOLUMN structure.

To obtain $sGUID and $iPID from a PROPERTYKEY structure, a new function GetSortColumnsEx() has been added, which returns $sGUID and $iPID in addition to the original information from GetSortColumns().

Output from GetSortColumnsEx() for System.ItemNameDisplay PropertyKey:

GUID   -> {B725F130-47EF-101A-A5F1-02608C9EEBAC}
PID    -> 10
Sort   -> 1
Column -> System.ItemNameDisplay

Because the first part of the PROPERTYKEY structure is a GUID, the value of this GUID can be set with _WinAPI_GUIDFromStringEx().

Link to comment
Share on other sites

Examples
The examples are shown for a File/Windows Explorer window. The 7z-file at bottom of first post contains similar examples for the Desktop.

8 ) GetSortColumns.au3

#include "..\..\Includes\FileExplorer.au3"
#include <Array.au3>

Example()

Func Example()
  ; File/Windows Explorer on Windows 7, 8, 10
  Local $hExplorer = WinGetHandle( "[REGEXPCLASS:^(Cabinet|Explore)WClass$]" )
  If Not $hExplorer Then
    MsgBox( 0, "Automating File/Windows Explorer and Desktop", "Could not find window. Terminating." )
    Return
  EndIf

  ; Get an IShellBrowser interface
  GetIShellBrowser( $hExplorer )
  If Not IsObj( $oIShellBrowser ) Then
    MsgBox( 0, "Automating File/Windows Explorer and Desktop", "Could not get an IShellBrowser interface. Terminating." )
    Return
  EndIf

  ; Activate File Explorer
  WinActivate( $hExplorer )
  WinWaitActive( $hExplorer )

  ; Get other interfaces
  GetShellInterfaces()

  ; Get sort columns
  Local $aSortColumns = GetSortColumns()
  _ArrayDisplay( $aSortColumns, "Explorer: $aSortColumns" )
EndFunc

Output:

Sort   -> 1
Column -> System.ItemNameDisplay


9) GetSortColumnsEx.au3

#include "..\..\Includes\FileExplorer.au3"
#include <Array.au3>

Example()

Func Example()
  ; File/Windows Explorer on Windows 7, 8, 10
  Local $hExplorer = WinGetHandle( "[REGEXPCLASS:^(Cabinet|Explore)WClass$]" )
  If Not $hExplorer Then
    MsgBox( 0, "Automating File/Windows Explorer and Desktop", "Could not find window. Terminating." )
    Return
  EndIf

  ; Get an IShellBrowser interface
  GetIShellBrowser( $hExplorer )
  If Not IsObj( $oIShellBrowser ) Then
    MsgBox( 0, "Automating File/Windows Explorer and Desktop", "Could not get an IShellBrowser interface. Terminating." )
    Return
  EndIf

  ; Activate File Explorer
  WinActivate( $hExplorer )
  WinWaitActive( $hExplorer )

  ; Get other interfaces
  GetShellInterfaces()

  ; Get sort columns
  Local $aSortColumns = GetSortColumnsEx()
  _ArrayDisplay( $aSortColumns, "Explorer: $aSortColumns" )
EndFunc

Output:

GUID   -> {B725F130-47EF-101A-A5F1-02608C9EEBAC}
PID    -> 10
Sort   -> 1
Column -> System.ItemNameDisplay


10) SetSortColumns.au3

#include "..\..\Includes\FileExplorer.au3"

Example()

Func Example()
  ; File/Windows Explorer on Windows 7, 8, 10
  Local $hExplorer = WinGetHandle( "[REGEXPCLASS:^(Cabinet|Explore)WClass$]" )
  If Not $hExplorer Then
    MsgBox( 0, "Automating File/Windows Explorer and Desktop", "Could not find window. Terminating." )
    Return
  EndIf

  ; Get an IShellBrowser interface
  GetIShellBrowser( $hExplorer )
  If Not IsObj( $oIShellBrowser ) Then
    MsgBox( 0, "Automating File/Windows Explorer and Desktop", "Could not get an IShellBrowser interface. Terminating." )
    Return
  EndIf

  ; Activate File Explorer
  WinActivate( $hExplorer )
  WinWaitActive( $hExplorer )

  ; Get other interfaces
  GetShellInterfaces()

  ; Set sort columns
  SetSortColumns( "{B725F130-47EF-101A-A5F1-02608C9EEBAC}", 10, -1 ) ; System.ItemNameDisplay, Name
EndFunc

Desktop: Note that setting a sort for the Name column on Desktop means that the icons are placed in a different order.

 

First post and GetSortColumns() post are updated. This post and previous post have been added. New 7z-file at bottom of first post.

Edited by LarsJ
Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...