Jump to content

Recommended Posts

Posted (edited)

I needed to be able to retrieve the folder currently being explored by an instance of Windows Explorer. To that end, I had to write a function to do this which I've copied out below. Feel free to re-use this if you find it useful.

#include-once

; ==================================================================================================
; <_GetWindowsExplorerPath.au3>
;
; Functions:
;   _GetWindowsExplorerPath()
;   _GetWindowsExplorerPaths()
;
; Author: WideBoyDixon
; ==================================================================================================

; ==================================================================================================
; Func _GetWindowsExplorerPath($hWnd)
;
; Function to get the path currently being explored by a Windows Explorer window
;
; $hWnd = Handle to the Windows Explorer window
;
; Returns:
;   Success: String - Path being explored by this window
;   Failure: "" empty string, with @error set:
;      @error = 1 = This is not a valid explorer window
;      @error = 2 = DLL call error, use _WinAPI_GetLastError()
;
; Author: WideBoyDixon
; ==================================================================================================
Func _GetWindowsExplorerPath($hWnd)
    Local $pv, $pidl, $return = "", $ret, $hMem, $pid, $folderPath = DllStructCreate("char[260]"), $className
    Local $bPIDL = False
    Local Const $CWM_GETPATH = 0x400 + 12;

    ; Check the classname of the window first
    $className = DllCall("user32.dll", "int", "GetClassName", "hwnd", $hWnd, "str", "", "int", 4096)
    If @error Then Return SetError(2, 0, "")
    If ($className[2] <> "ExploreWClass" And $className[2] <> "CabinetWClass") Then Return SetError(1, 0, "")
    
    ; Retrieve the process ID for our process
    $pid = DllCall("kernel32.dll", "int", "GetCurrentProcessId")
    If @error Then Return SetError(2, 0, "")

    ; Send the CWM_GETPATH message to the window
    $hMem = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hWnd, "int", $CWM_GETPATH, "wparam", $pid[0], "lparam", 0) 
    If @error Then Return SetError(2, 0, "")
    If $hMem[0] = 0 Then Return SetError(1, 0, "")
    
    ; Lock the shared memory
    $pv = DllCall("shell32.dll", "ptr", "SHLockShared", "uint", $hMem[0], "uint", $pid[0])
    If @error Then Return SetError(2, 0, "")
    If $pv[0] Then
        $pidl = DllCall("shell32.dll", "ptr", "ILClone", "uint", $pv[0]) ; Clone the PIDL
        If @error Then Return SetError(2, 0, "")
        $bPIDL = True
        DllCall("shell32.dll", "int", "SHUnlockShared", "uint", $pv) ; Unlock the shared memory
    EndIf
    DllCall("shell32.dll", "int", "SHFreeShared", "uint", $hMem, "uint", $pid) ; Free the shared memory
    
    If $bPIDL Then
        ; Retrieve the path from the PIDL
        $ret = DllCall("shell32.dll", "int", "SHGetPathFromIDList", "ptr", $pidl[0], "ptr", DllStructGetPtr($folderPath))
        If (@error = 0) And ($ret[0] <> 0) Then $return = DllStructGetData($folderPath, 1) ; Retrieve the value
        DllCall("shell32.dll", "none", "ILFree", "ptr", $pidl[0]) ; Free up the PIDL that we cloned
        Return SetError(0, 0, $return) ; Success
    EndIf
    
    Return SetError(2, 0, "") ; Failed a WinAPI call
EndFunc

; ==================================================================================================
; Func _GetWindowsExplorerPaths()
;
; Function to get a list of all paths currently being explored by a Windows Explorer window
;
; Returns:
;   Array
;       [0][0] - Number of items in the Array
;       [$i][0] - Window handle of the explorer window
;       [$i][1] - Folder being explored by this window
;
; Author: WideBoyDixon
; ==================================================================================================
Func _GetWindowsExplorerPaths()
    Local $nCount = 0 ; The number of explorer windows we found
    Local $aPaths[1][2] ; Our return array
    Local $aWindows = WinList() ; Look at all the windows we can find
    Local $nI, $folderPath ; Loop variable and folder path string

    ; Loop through all windows
    For $nI = 1 To $aWindows[0][0]
        ; Try to get the folder path for this window
        $folderPath = _GetWindowsExplorerPath($aWindows[$nI][1])
        If @error = 0 Then
            ; Found the path ... store it
            $nCount += 1 ; Increment the number of windows we've found
            ReDim $aPaths[$nCount + 1][2] ; Make room for this one in the array
            $aPaths[$nCount][0] = $aWindows[$nI][1] ; Store the window handle
            $aPaths[$nCount][1] = $folderPath ; Store the path that's being explored
            $aPaths[0][0] = $nCount ; Update the number of items in the array
        EndIf
    Next

    Return $aPaths
EndFunc

Regards,

WBD

Edited by WideBoyDixon
Posted

Nice example, will sure come handy at some point :).

$var = WinList("[CLASS:ExploreWClass;]")
for $i = 1 to $var[0][0]
    ConsoleWrite(_GetWindowsExplorerPath($var[$i][1]) & @crlf)
Next

Cheers

Posted

Would this work for a FileOpen Dialog? If not, can it be adapted?

I thought I might be able to use something like this to tackle a

problem I have, how to detect uploads from Firefox.

Posted

Would this work for a FileOpen Dialog? If not, can it be adapted?

Unfortunately, "No" and "No". In order to get the current folder in a FileOpen dialog you need to delve in to the depths of IShellBrowser and other shell interfaces and I'm not sure AutoIt can do that without creating an external DLL. The basics are:

[1] Use SendMessage to the dialog using WM_GETISHELLBROWSER (=WM_USER + 7) to get a pointer to an IShellBrowser

[2] Use the QueryActiveShellView method of IShellBrowser to get the active shell view which is a pointer to an IShellView

[3] Use the QueryInterface method of the IShellView to get a handle to a folder view IFolderView

[4] Use the GetFolder method of the IFolderView to get a handle to IPersistFolder2

[5] Call GetCurFolder on the IPersistFolder2 to get a PIDL for the current folder

[6] Call SHGetPathFromIDList to get the path from the PIDL

[7] Call CoTaskMemFree to free up the PIDL

The C++ code would look something like this:

bool ShellViewGetCurrentFolder(HWND hwnd, LPTSTR path)
{
    path[0] = 0;

    IShellBrowser *psb = (IShellBrowser*) ::SendMessage( hwnd, WM_GETISHELLBROWSER, 0, 0 );
    if (!psb)
        return false;
        
    CComPtr<IShellView> psv;
    if (FAILED(psb->QueryActiveShellView(&psv)))
        return false;
        
    CComPtr<IFolderView> pfv;
    if (FAILED(psv->QueryInterface(IID_IFolderView, (void**)&pfv)))
        return false;
         
    CComPtr<IPersistFolder2> ppf2;
    if (FAILED(pfv->GetFolder(IID_IPersistFolder2, (void**)&ppf2)))
        return false;
        
    LPITEMIDLIST pidlFolder;
    if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder))) 
    {
        bool res = false;
        if (::SHGetPathFromIDList(pidlFolder, path))
            res = true;
        ::CoTaskMemFree(pidlFolder);
        return res;
    }
    
    return false;
}

Hope this helps.

WBD

Posted

Very nice!

@Kafu- you example didn't seem to work for me?

I can see this comming in useful with an up and comming project...

Just some issues at the moment.

Using this code:

$aWindow = _GetWindowsExplorerPaths()

For $i = 1 To $aWindow[0][0]
    MsgBox(0, $i, "HWND = " & $aWindow[$i][0] & @CRLF & _
            "Folder = " & $aWindow[$i][1])
Next

With 2 explorer windows open (one is My Computer and the other is D:\My Documents), it doesn't display anything as the folder for My Computer. Any idea why?

Cheers,

Brett

Posted

@Kafu- you example didn't seem to work for me?

Strange, just rechecked posted code and it works for me (this is even another machine then i wrote the example on), writes info to console as intended.

Just realized what you mentioned, if the "file focus" of an explorer is set on "My Computer" it doesn't return anything. But that's okay, because "My Computer" isn't a location in the file-system :).

Posted

Strange.. This works for me... :)

$var = WinList("[CLASS:CabinetWClass;]")
for $i = 1 to $var[0][0]
    MsgBox (0, "", _GetWindowsExplorerPath($var[$i][1]))
Next
Posted

I needed to be able to retrieve the folder currently being explored by an instance of Windows Explorer. To that end, I had to write a function to do this which I've copied out below. Feel free to re-use this if you find it useful.

#include-once

; ==================================================================================================
; <_GetWindowsExplorerPath.au3>
;
; Functions:
;   _GetWindowsExplorerPath()
;   _GetWindowsExplorerPaths()
;
; Author: WideBoyDixon
; ==================================================================================================

; ==================================================================================================
; Func _GetWindowsExplorerPath($hWnd)
;
; Function to get the path currently being explored by a Windows Explorer window
;
; $hWnd = Handle to the Windows Explorer window
;
; Returns:
;   Success: String - Path being explored by this window
;   Failure: "" empty string, with @error set:
;      @error = 1 = This is not a valid explorer window
;      @error = 2 = DLL call error, use _WinAPI_GetLastError()
;
; Author: WideBoyDixon
; ==================================================================================================
Func _GetWindowsExplorerPath($hWnd)
    Local $pv, $pidl, $return = "", $ret, $hMem, $pid, $folderPath = DllStructCreate("char[260]"), $className
    Local $bPIDL = False
    Local Const $CWM_GETPATH = 0x400 + 12;

    ; Check the classname of the window first
    $className = DllCall("user32.dll", "int", "GetClassName", "hwnd", $hWnd, "str", "", "int", 4096)
    If @error Then Return SetError(2, 0, "")
    If ($className[2] <> "ExploreWClass" And $className[2] <> "CabinetWClass") Then Return SetError(1, 0, "")
    
    ; Retrieve the process ID for our process
    $pid = DllCall("kernel32.dll", "int", "GetCurrentProcessId")
    If @error Then Return SetError(2, 0, "")

    ; Send the CWM_GETPATH message to the window
    $hMem = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hWnd, "int", $CWM_GETPATH, "wparam", $pid[0], "lparam", 0) 
    If @error Then Return SetError(2, 0, "")
    If $hMem[0] = 0 Then Return SetError(1, 0, "")
    
    ; Lock the shared memory
    $pv = DllCall("shell32.dll", "ptr", "SHLockShared", "uint", $hMem[0], "uint", $pid[0])
    If @error Then Return SetError(2, 0, "")
    If $pv[0] Then
        $pidl = DllCall("shell32.dll", "ptr", "ILClone", "uint", $pv[0]) ; Clone the PIDL
        If @error Then Return SetError(2, 0, "")
        $bPIDL = True
        DllCall("shell32.dll", "int", "SHUnlockShared", "uint", $pv) ; Unlock the shared memory
    EndIf
    DllCall("shell32.dll", "int", "SHFreeShared", "uint", $hMem, "uint", $pid) ; Free the shared memory
    
    If $bPIDL Then
        ; Retrieve the path from the PIDL
        $ret = DllCall("shell32.dll", "int", "SHGetPathFromIDList", "ptr", $pidl[0], "ptr", DllStructGetPtr($folderPath))
        If (@error = 0) And ($ret[0] <> 0) Then $return = DllStructGetData($folderPath, 1) ; Retrieve the value
        DllCall("shell32.dll", "none", "ILFree", "ptr", $pidl[0]) ; Free up the PIDL that we cloned
        Return SetError(0, 0, $return) ; Success
    EndIf
    
    Return SetError(2, 0, "") ; Failed a WinAPI call
EndFunc

; ==================================================================================================
; Func _GetWindowsExplorerPaths()
;
; Function to get a list of all paths currently being explored by a Windows Explorer window
;
; Returns:
;   Array
;       [0][0] - Number of items in the Array
;       [$i][0] - Window handle of the explorer window
;       [$i][1] - Folder being explored by this window
;
; Author: WideBoyDixon
; ==================================================================================================
Func _GetWindowsExplorerPaths()
    Local $nCount = 0 ; The number of explorer windows we found
    Local $aPaths[1][2] ; Our return array
    Local $aWindows = WinList() ; Look at all the windows we can find
    Local $nI, $folderPath ; Loop variable and folder path string

    ; Loop through all windows
    For $nI = 1 To $aWindows[0][0]
        ; Try to get the folder path for this window
        $folderPath = _GetWindowsExplorerPath($aWindows[$nI][1])
        If @error = 0 Then
            ; Found the path ... store it
            $nCount += 1 ; Increment the number of windows we've found
            ReDim $aPaths[$nCount + 1][2] ; Make room for this one in the array
            $aPaths[$nCount][0] = $aWindows[$nI][1] ; Store the window handle
            $aPaths[$nCount][1] = $folderPath ; Store the path that's being explored
            $aPaths[0][0] = $nCount ; Update the number of items in the array
        EndIf
    Next

    Return $aPaths
EndFunc

Regards,

WBD

Thats really awesome script. i want to know 1 more thing, isn't it possible to get the currently selected file in explorer?

73 108 111 118 101 65 117 116 111 105 116

Posted

Thats really awesome script. i want to know 1 more thing, isn't it possible to get the currently selected file in explorer?

Technically it's possible but of course there might be more than one selected file (or there might be zero). Again you'd have to delve deep in to the Shell32.dll functions and I think the one you'd need is SHShellFolderView_Message along with SFVM_GETSELECTEDOBJECTS

I'm not sure quite how to program an array of PIDLs in AutoIt so I'm going to shy away from this one for now.

WBD

Posted

@Digisoul

Explorer is just a listview control. Look at the _GuiCtrlListView_*** functions to do what you need.

Ya i know but it never work well,

main problems

1. you have to find the file with,

2. if the file has same name & but different extensions, you cant decide it correctly.

73 108 111 118 101 65 117 116 111 105 116

Posted

Hi

Maybe I'm missing something, but I think there is an easier way to get this information. I use this method to get the current folder and selected file. Once obtained, this information is used to edit the file differently based on the type. Hope the below is clear. It is a function of a much larger script.

HotKeySet("^q", "quickview")

func quickview()

if WinActive("[CLASS:ExploreWClass]","") Then

$sSelected_Path = ControlGetText("[CLASS:ExploreWClass]", "", "Edit1")

$CLVItem = ControlListView("[CLASS:ExploreWClass]", "", "[CLASS:SysListView32; INSTANCE:1]", "GetSelected")

$Item = ControlListView("[CLASS:ExploreWClass]", "", "[CLASS:SysListView32; INSTANCE:1]", "GetText", $CLVItem)

$Item2=StringRight($Item,3)

if $Item2="au3" Then

ShellExecute("F:\applications\autoit\install\SciTe\SciTE.exe", $sSelected_Path & "\" & $Item)

ElseIf $Item2="vbs" or $Item2="hta" or $Item2="bat" Then

ShellExecute("C:\Mshell\programs\notepad2.exe", $sSelected_Path & "\" & $Item)

EndIf

ElseIf WinActive("[CLASS:CabinetWClass]","") Then

$sSelected_Path = ControlGetText("[CLASS:CabinetWClass]", "", "Edit1")

$CLVItem = ControlListView("[CLASS:CabinetWClass]", "", "[CLASS:SysListView32; INSTANCE:1]", "GetSelected")

$Item = ControlListView("[CLASS:CabinetWClass]", "", "[CLASS:SysListView32; INSTANCE:1]", "GetText", $CLVItem)

$Item2=StringRight($Item,3)

if $Item2="au3" Then

ShellExecute("F:\applications\autoit\install\SciTe\SciTE.exe", $sSelected_Path & "\" & $Item)

ElseIf $Item2="vbs" or $Item2="hta" or $Item2="bat" Then

ShellExecute("C:\Mshell\programs\notepad2.exe", $sSelected_Path & "\" & $Item)

EndIf

else

SEND("!q")

EndIf

EndFunc

Posted

It doesn't work if you have the address bar hidden. That's the kicker.

WBD

Posted

It doesn't work if you have the address bar hidden. That's the kicker.

WBD

An issue I had to try and deal with in my New Folders and Context Options programs.

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Posted

@TheSaint

What was your solution? Especially since this topic's method doesn't work in Vista, SendMessage() returns 0.

Someone just had to ask that ... now I have to go and work out what I did ... I had to say something didn't I ... and I'm not even 100% sure I did solve it? I remember my Open Projects program also giving me some grief, because the Drive windows opened by TrueCrypt didn't always display a drive letter in the address bar ... but I do remember solving that somehow. Have you or anyone else tried my New Folders program in Vista ... especially without the address bar being active ... I don't have easy access to Vista at this point?

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Posted (edited)

In the Hot Folders script I dealt with this using the following:

; Force address bar to be visible so that we can get the handle to the edit control
_SendMessage($hwndActive, $WM_COMMAND, 41477, 0)

Perhaps that would work for you too?

WBD

Edited by WideBoyDixon
  • 1 year later...
Posted

Has anyone succeeded in getting this script to work in Windows Vista or 7? I don't understand why it wouldn't work in 7 -- the class for an explorer window is still CabinetWClass.

Any suggestions that might help get this working?

Posted

This works for me on Win7...

MsgBox(0, "", _GetWindowsExplorerPath("[CLASS:CabinetWClass]"))

Func _GetWindowsExplorerPath($hWnd)
    If @OSVersion = "WIN_7" Then Return WinGetTitle($hWnd,"")
EndFunc   ;==>_GetWindowsExplorerPath

Just add the line If @OSVersion... below the Return SetError(1, 0, "") line of the original function.

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.
×
×
  • Create New...