Adding If @OSVersion = "WIN_7" Then Return WinGetTitle($hWnd,"") below that line gets me the window title of the explorer window, it doesn't return the path like I want it to. Any luck getting the path like it does in XP?
Thanks
Edit:
After thinking about this some more, I have something that will work. *Note: I haven't tested on Vista yet, only 7.
;;;This is the code that would go in your code, does the string analysis to get the path
for $i = 1 to $var[0][0]
If @OSVersion = "WIN_7" Then
$string = _GetWindowsExplorerPath($var[$i][1])
$string = StringReplace($string, "Address: ", "")
$string = StringSplit($string, @CRLF)
MsgBox(0,"", $string[1])
ElseIf @OSVersion = "WIN_VISTA" Then
$string = _GetWindowsExplorerPath($var[$i][1])
$string = StringReplace($string, "Address: ", "")
$string = StringSplit($string, @CRLF)
MsgBox(0,"", $string[1])
Else
MsgBox (0, "", _GetWindowsExplorerPath($var[$i][1]))
EndIf
Next
;;This gets added to the function
;;It goes below this line:
;;If ($className[2] <> "ExploreWClass" And $className[2] <> "CabinetWClass") Then Return SetError(1, 0, "")
If @OSVersion = "WIN_7" Then Return WinGetText($hWnd,"")
If @OSVersion = "WIN_Vista" Then Return WinGetText($hWnd,"")
Hopefully there's a less messy solution out there, but for now this seems like it could work.
Hopefully there's a less messy solution out there, but for now this seems like it could work.
I am wondering why _GetWindowsExplorerPath could not be realized using Shell.Application:
$SHA= ObjCreate("Shell.Application")
$SHW= $SHA.Windows()
while 1
for $WIN in $SHW
if $WIN.hwnd = $w then exitloop 2
next
return ""
wend
$n= $WIN.document.SelectedItems.Count
$SEL= $WIN.document.SelectedItems
$s= $WIN.LocationURL & ","
for $ITM in $SEL
$s &= $ITM.path & ","
next
Works for me on XP/SP3. Any drawbacks or caveats known for this?
Since the 'LocationURL' string returns values like:
'file:///C:/%5BABracketedFolder%5B'
I wrote the following simple function to decode the string into a usable path. If you know of a better way, let me know:
AutoIt
; ===================================================================================================================; Func _URLFilePathDecode($sURL);; Function to decode a URL-encoded filepath (one in which there are '%xx' values, '/'s,; and a possible 'file:///' prefix.;; Example: file:///C:/%5BABracketedFolder%5B; becomes: C:\[ABracketedFolder];; Returns: String stripped of URL encoded data;; Author: Ascend4nt; ===================================================================================================================Func_URLFilePathDecode($sURL)Local$aHexCodes,$sFilePath=$sURL; Grab all the '%xx' values into an array so that we can replace them in left-to-right order$aHexCodes=StringRegExp($sURL,'%([[:xdigit:]]{2})',3)IfNot@errorThen; Change all the '%xx' values into ';'s (invalid path character)$sFilePath=StringRegExpReplace($sURL,'%([[:xdigit:]]{2})',';'); Then go through 1 by 1 and replace each ';' in left-to-right order (same as array order)For$i=0ToUBound($aHexCodes)-1$sFilePath=StringReplace($sFilePath,';',ChrW('0x'&$aHexCodes[$i]),1); only the 1st occurrence for each rep!NextEndIf$sFilePath=StringReplace($sFilePath,'file:///','')$sFilePath=StringReplace($sFilePath,'/','\')Return$sFilePathEndFunc
*edit - Revised function to work correctly in case there are '%xx's in the resultant path
#include <Array.au3>$aWinList=WinList("[REGEXPCLASS:(Explore|Cabinet)WClass]")For$i=1ToUBound($aWinList)-1$aSelection=_WindowsExplorer_ExtractSelectedDirAndFiles($aWinList[$i][1])_ArrayDisplay($aSelection,"Explorer Instance #"&$i&" / "&WinGetTitle($aWinList[$i][1]))Next; ===================================================================================================================; Name...........: _WindowsExplorer_ExtractSelectedDirAndFiles; Description....: Function to extract LocationURL and selected files from a "Windows Explorer" Window; Syntax.........: _WindowsExplorer_ExtractSelectedDirAndFiles($hWnd); Parameters.....: $hWnd - Windows handle of an "Windows Explorer" Window; AutoIt Version.: 3.3.6.1; Return values..: Success - Array; Array[0] = Number of files selected in right-hand Listview; Array[1] = LocationURL - selected in left-hand Treeview or Window Rebar; Array[2] - Array[n] = URL of selected files in right-hand Listview; Failure - Array; Array[0] = 0; Array[1] = ""; Sets the @error flag to non-zero.; @error = 1 - $hwnd is not a valid window handle; @error = 2 - $hwnd is not a window handle for an "Windows Explorer" Window; @error = 3 - "Shell.Application" object could not be created; @error = 4 - "$oShellApp.Windows()" object could not be created; Author.........: Ascend4nt, KaFu, klaus.s; ===================================================================================================================Func_WindowsExplorer_ExtractSelectedDirAndFiles($hWnd)Local$aExplorerSelectedFiles[2]=[0,""]IfNotIsHWnd($hWnd)ThenReturnSetError(1,0,$aExplorerSelectedFiles)Local$aWinList=WinList("[REGEXPCLASS:(Explore|Cabinet)WClass]")While1For$i=1ToUBound($aWinList)-1If$hWnd=$aWinList[$i][1]ThenExitLoop2NextReturnSetError(2,0,$aExplorerSelectedFiles)WEndLocal$oShellApp=ObjCreate("Shell.Application")IfNotIsObj($oShellApp)ThenReturnSetError(3,0,$aExplorerSelectedFiles)Local$oShellApp_Windows=$oShellApp.Windows()IfNotIsObj($oShellApp_Windows)ThenReturnSetError(4,0,$aExplorerSelectedFiles)For$oShellApp_InstIn$oShellApp_WindowsIf$oShellApp_Inst.hwnd=$hWndThenExitLoopNextLocal$oShellApp_Inst_SelectedItems_Count=$oShellApp_Inst.document.SelectedItems.CountLocal$oShellApp_Inst_LocationURL=$oShellApp_Inst.LocationURLLocal$aHexCodes=StringRegExp($oShellApp_Inst_LocationURL,'%([[:xdigit:]]{2})',3)IfNot@errorThen; Change all the '%xx' values into ';'s (invalid path character)$oShellApp_Inst_LocationURL=StringRegExpReplace($oShellApp_Inst_LocationURL,'%([[:xdigit:]]{2})',';'); Then go through 1 by 1 and replace each ';' in left-to-right order (same as array order)For$i=0ToUBound($aHexCodes)-1$oShellApp_Inst_LocationURL=StringReplace($oShellApp_Inst_LocationURL,';',ChrW('0x'&$aHexCodes[$i]),1); only the 1st occurrence for each rep!NextEndIf$oShellApp_Inst_LocationURL=StringReplace($oShellApp_Inst_LocationURL,'file:///','')$aExplorerSelectedFiles[0]=$oShellApp_Inst_SelectedItems_Count$aExplorerSelectedFiles[1]=StringReplace($oShellApp_Inst_LocationURL,'/','\')ReDim$aExplorerSelectedFiles[$aExplorerSelectedFiles[0]+2]$oShellApp_Inst_SelectedItems=$oShellApp_Inst.document.SelectedItemsLocal$iCounter=2For$oShellApp_Inst_SelectedItemIn$oShellApp_Inst_SelectedItems$aExplorerSelectedFiles[$iCounter]=$oShellApp_Inst_SelectedItem.path$iCounter+=1Next$oShellApp=0$oShellApp_Windows=0Return$aExplorerSelectedFilesEndFunc;==>_WindowsExplorer_ExtractSelectedDirAndFiles
Hey, an even better way than my PCRE method is a simple API function I just came across today:
Nice find ...
(KaFu uses '$oShellApp_Inst_LocationURL' - don't ask me why an $o prefix was given to a string)
Because nobody really reviewed the snippet I posted ?
AutoIt
#include <Array.au3>$aWinList=WinList("[REGEXPCLASS:(Explore|Cabinet)WClass]")For$i=1ToUBound($aWinList)-1$aSelection=_WindowsExplorer_ExtractSelectedDirAndFiles($aWinList[$i][1])_ArrayDisplay($aSelection,"Explorer Instance #"&$i&" / "&WinGetTitle($aWinList[$i][1]))Next; ===================================================================================================================; Name...........: _WindowsExplorer_ExtractSelectedDirAndFiles; Description....: Function to extract LocationURL and selected files from a "Windows Explorer" Window; Syntax.........: _WindowsExplorer_ExtractSelectedDirAndFiles($hWnd); Parameters.....: $hWnd - Windows handle of an "Windows Explorer" Window; AutoIt Version.: 3.3.6.1; Return values..: Success - Array; Array[0] = Number of files selected in right-hand Listview; Array[1] = LocationURL - selected in left-hand Treeview or Window Rebar; Array[2] - Array[n] = URL of selected files in right-hand Listview; Failure - Array; Array[0] = 0; Array[1] = ""; Sets the @error flag to non-zero.; @error = 1 - $hwnd is not a valid window handle; @error = 2 - $hwnd is not a window handle for an "Windows Explorer" Window; @error = 3 - "Shell.Application" object could not be created; @error = 4 - "$oShellApp.Windows()" object could not be created; Author.........: Ascend4nt, KaFu, klaus.s; ===================================================================================================================Func_WindowsExplorer_ExtractSelectedDirAndFiles($hWnd)Local$aExplorerSelectedFiles[2]=[0,""]IfNotIsHWnd($hWnd)ThenReturnSetError(1,0,$aExplorerSelectedFiles)Local$aWinList=WinList("[REGEXPCLASS:(Explore|Cabinet)WClass]")While1For$i=1ToUBound($aWinList)-1If$hWnd=$aWinList[$i][1]ThenExitLoop2NextReturnSetError(2,0,$aExplorerSelectedFiles)WEndLocal$oShellApp=ObjCreate("Shell.Application")IfNotIsObj($oShellApp)ThenReturnSetError(3,0,$aExplorerSelectedFiles)Local$oShellApp_Windows=$oShellApp.Windows()IfNotIsObj($oShellApp_Windows)ThenReturnSetError(4,0,$aExplorerSelectedFiles)For$oShellApp_InstIn$oShellApp_WindowsIf$oShellApp_Inst.hwnd=$hWndThenExitLoopNextLocal$iShellApp_Inst_SelectedItems_Count=$oShellApp_Inst.document.SelectedItems.CountLocal$sShellApp_Inst_LocationURL=$oShellApp_Inst.LocationURLLocal$aRet=DllCall('shlwapi.dll','long','PathCreateFromUrlW','wstr',$sShellApp_Inst_LocationURL,'wstr','','dword*',65534,'dword',0)IfNot@errorAnd$aRet[0]=0Then$sShellApp_Inst_LocationURL=$aRet[2]$aExplorerSelectedFiles[0]=$iShellApp_Inst_SelectedItems_Count$aExplorerSelectedFiles[1]=$sShellApp_Inst_LocationURLReDim$aExplorerSelectedFiles[$aExplorerSelectedFiles[0]+2]$oShellApp_Inst_SelectedItems=$oShellApp_Inst.document.SelectedItemsLocal$iCounter=2For$oShellApp_Inst_SelectedItemIn$oShellApp_Inst_SelectedItems$aExplorerSelectedFiles[$iCounter]=$oShellApp_Inst_SelectedItem.path$iCounter+=1Next$oShellApp=0$oShellApp_Windows=0$oShellApp_Inst=0$oShellApp_Inst_SelectedItems=0$oShellApp_Inst_SelectedItem=0Return$aExplorerSelectedFilesEndFunc;==>_WindowsExplorer_ExtractSelectedDirAndFiles
Because nobody really reviewed the snippet I posted ?
AutoIt
#include <Array.au3>$aWinList=WinList("[REGEXPCLASS:(Explore|Cabinet)WClass]")For$i=1ToUBound($aWinList)-1$aSelection=_WindowsExplorer_ExtractSelectedDirAndFiles($aWinList[$i][1])_ArrayDisplay($aSelection,"Explorer Instance #"&$i&" / "&WinGetTitle($aWinList[$i][1]))Next; ===================================================================================================================; Name...........: _WindowsExplorer_ExtractSelectedDirAndFiles; Description....: Function to extract LocationURL and selected files from a "Windows Explorer" Window; Syntax.........: _WindowsExplorer_ExtractSelectedDirAndFiles($hWnd); Parameters.....: $hWnd - Windows handle of an "Windows Explorer" Window; AutoIt Version.: 3.3.6.1; Return values..: Success - Array; Array[0] = Number of files selected in right-hand Listview; Array[1] = LocationURL - selected in left-hand Treeview or Window Rebar; Array[2] - Array[n] = URL of selected files in right-hand Listview; Failure - Array; Array[0] = 0; Array[1] = ""; Sets the @error flag to non-zero.; @error = 1 - $hwnd is not a valid window handle; @error = 2 - $hwnd is not a window handle for an "Windows Explorer" Window; @error = 3 - "Shell.Application" object could not be created; @error = 4 - "$oShellApp.Windows()" object could not be created; Author.........: Ascend4nt, KaFu, klaus.s; ===================================================================================================================Func_WindowsExplorer_ExtractSelectedDirAndFiles($hWnd)Local$aExplorerSelectedFiles[2]=[0,""]IfNotIsHWnd($hWnd)ThenReturnSetError(1,0,$aExplorerSelectedFiles)Local$aWinList=WinList("[REGEXPCLASS:(Explore|Cabinet)WClass]")While1For$i=1ToUBound($aWinList)-1If$hWnd=$aWinList[$i][1]ThenExitLoop2NextReturnSetError(2,0,$aExplorerSelectedFiles)WEndLocal$oShellApp=ObjCreate("Shell.Application")IfNotIsObj($oShellApp)ThenReturnSetError(3,0,$aExplorerSelectedFiles)Local$oShellApp_Windows=$oShellApp.Windows()IfNotIsObj($oShellApp_Windows)ThenReturnSetError(4,0,$aExplorerSelectedFiles)For$oShellApp_InstIn$oShellApp_WindowsIf$oShellApp_Inst.hwnd=$hWndThenExitLoopNextLocal$iShellApp_Inst_SelectedItems_Count=$oShellApp_Inst.document.SelectedItems.CountLocal$sShellApp_Inst_LocationURL=$oShellApp_Inst.LocationURLLocal$aRet=DllCall('shlwapi.dll','long','PathCreateFromUrlW','wstr',$sShellApp_Inst_LocationURL,'wstr','','dword*',65534,'dword',0)IfNot@errorAnd$aRet[0]=0Then$sShellApp_Inst_LocationURL=$aRet[2]$aExplorerSelectedFiles[0]=$iShellApp_Inst_SelectedItems_Count$aExplorerSelectedFiles[1]=$sShellApp_Inst_LocationURLReDim$aExplorerSelectedFiles[$aExplorerSelectedFiles[0]+2]$oShellApp_Inst_SelectedItems=$oShellApp_Inst.document.SelectedItemsLocal$iCounter=2For$oShellApp_Inst_SelectedItemIn$oShellApp_Inst_SelectedItems$aExplorerSelectedFiles[$iCounter]=$oShellApp_Inst_SelectedItem.path$iCounter+=1Next$oShellApp=0$oShellApp_Windows=0$oShellApp_Inst=0$oShellApp_Inst_SelectedItems=0$oShellApp_Inst_SelectedItem=0Return$aExplorerSelectedFilesEndFunc;==>_WindowsExplorer_ExtractSelectedDirAndFiles
May I do that now? Ok.
Why do you forcibly release those objects. You don't have to do that since they are local. You miss proper COM error handling for that function because you reference different objects on the fly counting on error free situation. Both issues are closely related and can be seen in e.g. this part of the code:
$oShellApp_Inst.document.SelectedItems.Count
How many objects do you see and create and reference there? Let's see...
$oShellApp_Inst - did you check that $oShellApp_Inst is actually object before trying to access it
$oShellApp_Inst.Document - new object is created. Did you check if it's created? This one is local (flyable too). See, it's released when it gets out of scope (at the end of the line), you don't have to do anything.
$oShellApp_Inst.document.SelectedItems - new object is created. But from what? From something that may not be an object. Same remarks as above are here too.
$oShellApp_Inst.document.SelectedItems.Count - accessing some property that may not exist for any of the reasons of some object that may not be.
This is not the way to write good and safe code in AutoIt. Corrected (to some degree and for this particular case) regarding what I just wrote, would be:
Of course you may do, nice . Freeing the Objects (and vars) was quite an overkill, but I was not sure if local objects are automatically released.
Do I get it right and you reference an error back to the function with this line $oError = ObjEvent("AutoIt.Error", "_WindowsExplorer_ExtractSelectedDirAndFiles") , letting the function handle the error, and in this case just silently drop it?
Of course you may do, nice . Freeing the Objects (and vars) was quite an overkill, but I was not sure if local objects are automatically released.
Do I get it right and you reference an error back to the function with this line $oError = ObjEvent("AutoIt.Error", "_WindowsExplorer_ExtractSelectedDirAndFiles") , letting the function handle the error, and in this case just silently drop it?
Yes, it's only because it has convenient intro. Some dummy function would be required otherwise.
Why do you forcibly release those objects. You don't have to do that since they are local. You miss proper COM error handling for that function because you reference different objects on the fly counting on error free situation. Both issues are closely related and can be seen in e.g. this part of the code:
$oShellApp_Inst.Document.SelectedItems.Count
How many objects do you see and create and reference there? Let's see...
$oShellApp_Inst - did you check that $oShellApp_Inst is actually object before trying to access it
$oShellApp_Inst.Document - new object is created. Did you check if it's created? This one is local (flyable too). See, it's released when it gets out of scope (at the end of the line), you don't have to do anything.
$oShellApp_Inst.document.SelectedItems - new object is created. But from what? From something that may not be an object. Same remarks as above are here too.
$oShellApp_Inst.document.SelectedItems.Count - accessing some property that may not exist for any of the reasons of some object that may not be.
This is not the way to write good and safe code in AutoIt. Corrected (to some degree and for this particular case) regarding what I just wrote, would be:
To get a handle to the Desktop is a slightly different problem, especially on Vista+. See a good method for getting that handle in this thread: Desktop Class: WorkerW
After toying around with Shell objects and the like, I've shortened the code even more. Note that you'll need to add code for an AutoIt COM error handler. I removed that because it shouldn't be part of a UDF function in my opinion.
Also, with separating out the code to get the 'ShellFolderView' object, its possible to do other things like selecting, focusing, and other Shell object operations.
AutoIt
; ==========================================================================================================================; Func _ObjectSHFolderViewFromWin($hWnd);; Returns an 'ShellFolderView' Object for the given Window handle;; Author: Ascend4nt, based on code by KaFu, klaus.s; ==========================================================================================================================Func_ObjectSHFolderViewFromWin($hWnd)IfNotIsHWnd($hWnd)ThenReturnSetError(1,0,0)Local$oShell,$oShellWindows,$oIEObject,$oSHFolderView; Shell Object$oShell=ObjCreate("Shell.Application")IfNotIsObj($oShell)ThenReturnSetError(2,0,0); Get a 'ShellWindows Collection' object$oShellWindows=$oShell.Windows()IfNotIsObj($oShellWindows)ThenReturnSetError(3,0,0); Iterate through the collection - each of type 'InternetExplorer' ObjectFor$oIEObjectIn$oShellWindowsIf$oIEObject.HWND=$hWndThen; InternetExplorer->Document = ShellFolderView object$oSHFolderView=$oIEObject.DocumentIfIsObj($oSHFolderView)ThenReturn$oSHFolderViewReturnSetError(4,0,0)EndIfNextReturnSetError(-1,0,0)EndFunc; ==========================================================================================================================; Func _ExplorerWinGetSelectedItems($hWnd);;; Author: klaus.s, KaFu, Ascend4nt (consolidation & cleanup, Path name simplification); ==========================================================================================================================Func_ExplorerWinGetSelectedItems($hWnd)IfNotIsHWnd($hWnd)ThenReturnSetError(1,0,'')Local$oSHFolderViewLocal$iSelectedItems,$iCounter=2,$aSelectedItems[2]=[0,""]$oSHFolderView=_ObjectSHFolderViewFromWin($hWnd)If@errorThenReturnSetError(@error,0,''); SelectedItems = FolderItems Collection object->Count$iSelectedItems=$oSHFolderView.SelectedItems.CountDim$aSelectedItems[$iSelectedItems+2]; 2 extra -> 1 for count [0], 1 for Folder path [1]$aSelectedItems[0]=$iSelectedItems; ShellFolderView->Folder->Self as 'FolderItem'->Path$aSelectedItems[1]=$oSHFolderView.Folder.Self.Path; ShellFolderView->SelectedItems = FolderItems Collection object$oSelectedFolderItems=$oSHFolderView.SelectedItems#cs
; For ALL items in an Explorer window (not just the selected ones):
$oSelectedFolderItems = $oSHFolderView.Folder.Items
ReDim $aSelectedItems[$oSelectedFolderItems.Count+2]
#ceFor$oFolderItemIn$oSelectedFolderItems$aSelectedItems[$iCounter]=$oFolderItem.Path$iCounter+=1NextReturnSetExtended($iCounter-2,$aSelectedItems)EndFunc;==>_ExplorerWinGetSelectedItems; ==========================================================================================================================; ==========================================================================================================================#include <Array.au3>$aWinList=WinList("[REGEXPCLASS:^(Explore|Cabinet)WClass$]")For$i=1To$aWinList[0][0]$aSelection=_ExplorerWinGetSelectedItems($aWinList[$i][1])_ArrayDisplay($aSelection,"Explorer Instance #"&$i&" / "&$aWinList[$i][0])Next
Here's code to select only 1 item (and focus on it), then select all, then deselect all:
$hWin=WinGetHandle('[REGEXPCLASS:^(Explore|Cabinet)WClass$]')$hCtrl=ControlGetHandle($hWin,'','[CLASS:SysListView32; INSTANCE:1]'); Windows 7If$hCtrl=''Then$hCtrl=ControlGetHandle($hWin,'','[CLASS:DirectUIHWND]')$sFileToSelect=''$oSHFolderView=_ObjectSHFolderViewFromWin($hWin)IfNot@errorThen; Comment this out to choose your own file here:$sFileToSelect=$oSHFolderView.FocusedItem.NameConsoleWrite("Focusing on : "&$sFileToSelect&@CRLF)WinActivate($hWin); Select ONLY the item$oSHFolderView.SelectItem($oSHFolderView.Folder.ParseName($sFileToSelect),4+1+8+16)Sleep(1000); Select ALL items (easiest way):ControlSend($hWin,'',$hCtrl,'^a')Sleep(1000); Deselect ALL items, while maintaining 'focused item'$oSHFolderView.SelectItem($oSHFolderView.FocusedItem,4)EndIf
ineedh3lp, Sure, call 'SelectItem' for each item you want to highlight. Note that the 'Name' member of a FolderItem object will often not include the extension (I think shortcuts were particularly a problem), so you'd best select items based on the 'Path' member which would need to be stripped of everything but the filename & extension if it is passed to 'SelectItem' as it was above.
Here's a link to the SelectItem method so you can see what the options are (I used 4+1+8+16 in my example above).
Excellent! Thank you for the script and resources.
This works without problems in Windows Explorer. May I extend the discussion and ask you if it is possible to select items in a different file manager? For example, I am using xplorer2, and I did try everything I could think of for the window and control handles, but I couldn't get it to work. Is your script bound to work with Windows Explorer only? I suspect it has something to do with $oShellWindows = $oShell.Windows(), but maybe you can tell me more.