Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Explorer Windows Manipulations


  • Please log in to reply
99 replies to this topic
fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Also, here's a function for selecting files in explorer (first one in list will be focussed and scrolled to). It's based on one that I found here somewhere which didn't work for me, I adapted it using this approach to find the active window.

SelectFiles(sSelect)
{
	If (WinActive("ahk_class ExploreWClass") || WinActive("ahk_class CabinetWClass"))
	{	
		hWnd:=WinExist("A")
      COM_Init() 
      psh  :=   COM_CreateObject("Shell.Application") 
      psw  :=   COM_Invoke(psh, "Windows") 
      Loop, %   COM_Invoke(psw, "Count") 
         If   COM_Invoke(pwb:=COM_Invoke(psw, "Item", A_Index-1), "HWND") <> hWnd 
            COM_Release(pwb) 
         Else Break
      pfv  :=   COM_Invoke(pwb, "Document") 
      Loop, Parse, sSelect, `n 
				If  A_LoopField <>
				{ 
					if(A_Index=1)
						COM_Invoke(pfv,"SelectItem",pfi:=COM_Invoke(pfv,"Folder.ParseName",A_LoopField),29), COM_Release(pfi) ;http://msdn.microsoft.com/en-us/library/bb774047(VS.85).aspx
					else
				    COM_Invoke(pfv,"SelectItem",pfi:=COM_Invoke(pfv,"Folder.ParseName",A_LoopField),1), COM_Release(pfi)
				}
			COM_Release(pfv) 
			COM_Release(pwb)      
      COM_Release(psi)
      COM_Release(psw) 
      COM_Release(psh) 
			COM_Term()
   } 
}


Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
You can turn off the error message and handle the errors yourself.
<!-- m -->http://www.autohotke...c22923-317.html<!-- m -->

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Could do, but tbh I'd like to add support for special folders too.
ShellFolder() returns them in ::{GUID} format, but the Navigate2 COM function doesn't accept them it seems. MSDN says it accepts PIDL, but how to get it from ::{GUID}?
I was thinking to try FolderIdToCsidl() (see <!-- m -->http://msdn.microsof...ibrary/bb761736<!-- m --> ), but no success yet.
What I tried:
DllCall("FolderIdToCsidl","str",sPath,"uint",csidl)
DllCall("SHGetFolderLocation","uint",0,"uint", *csidl, "uint",0,"uint",ppidl)
...
if(pidl)
      	COM_Invoke(pwb, "Navigate2", *ppidl)
Any ideas?

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
What you saw is the CLSID of a virtual folder. And your mentioned API is about the correspondence between older CSIDL (:integer) and newer KNOWNFOLDERID (:GUID) introduced and recommended over CSIDL from Vista. But, the ultimate one the Windows shell uses to identify a file/folder is PIDL.

I deliberately avoided directly answering your question as I didn't want to go into the labor of explaining all these stuffs, and I still do, so you have to figure them out yourself if you want to use them. I'd like only to mention that it's possible to use PIDL and/or CSIDL in addition to the normal file system path with Navigate2, which is in fact the difference from Navigate.

COM_Invoke(pwb, "Navigate2", 17) ; CSIDL_DRIVES:=17


fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
I've come to some conclusions, but no solution yet.

First, it seems that Navigate2 works with ::{CLSID} notation on XP, but not on my two Win7 systems.

This is the approach I'm currently trying, but it seems I have something wrong about the calling syntax? Maybe some pointer needs to be dereferenced?

ShellNavigate(sPath, hWnd=0) 
{ 
	If   hWnd||(hWnd:=WinExist("ahk_class CabinetWClass"))||(hWnd:=WinExist("ahk_class ExploreWClass")) 
  {
	sa := Com_CreateObject("Shell.Application")
	wins := sa.Windows
	loop % wins.count
		if((window:=wins.Item(A_Index-1)).Hwnd=hWnd) 
		{
			outputdebug found window
			break
		}
    DllCall("shell32\SHParseDisplayName", "Uint", COM_Unicode4Ansi(wPath,sPath) , "Uint", 0, "UintP", pidl, "Uint", 0, "Uint", 0)
    window.Navigate2(pidl)
  	return
  	}
}


Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
Yes, you did it wrong, you can't use pidl like that. OK, some other users may also want to do this.
VarSetCapacity(sa,24,0), NumPut(DllCall("shell32\ILGetSize","Uint",pidl), NumPut(pidl, NumPut(1, NumPut(1,sa)),4))
Window.Navigate2(COM_Parameter(0x2011,&sa)) ; COM_Invoke_(Window,"Navigate2",0x2011,&sa)
DllCall("shell32\ILFree","Uint",pidl)
Now, you know how to use pidl with Navigate2, so, you may try to obtain directly pidl instead of Path from the opened shell windows. Actually this very script used to be in that route, but it appeared cryptic with AHK and had been changed to the current form. So, now I'll leave it as an exercise.

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
This works very good, I'll post the explorer functions I'm using soon, I just want to do some testing on xp.

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
I still see COM errors popping up sometimes when doing stuff in the control panel, but I haven't been able to find a reproducable case yet. This might be caused by the function being called while switching folders or similar.

Anyway, here's the functions I currently use:

Edit: Update to include fix for Steam, thanks to sinkfaze for the idea:

ShellNavigate(sPath, hWnd=0) 
{ 
	If   hWnd||(hWnd:=WinExist("ahk_class CabinetWClass"))||(hWnd:=WinExist("ahk_class ExploreWClass")) 
  {
	sa := Com_CreateObject("Shell.Application")
	wins := sa.Windows
	loop % wins.count
	{
		window:=wins.Item(A_Index-1)
		If Not InStr( window.FullName, "steam.exe" ) ; ensure pwb isn't IE
		if(window.Hwnd=hWnd)
			break
	}	
    DllCall("shell32\SHParseDisplayName", "Uint", COM_Unicode4Ansi(wPath,sPath) , "Uint", 0, "UintP", pidl, "Uint", 0, "Uint", 0)
    VarSetCapacity(sa,24,0), NumPut(DllCall("shell32\ILGetSize","Uint",pidl), NumPut(pidl, NumPut(1, NumPut(1,sa)),4)) 
		Window.Navigate2(COM_Parameter(0x2011,&sa))
		return
  }
}

SelectFiles(sSelect, hWnd=0)
{
	If   hWnd||(hWnd:=WinExist("ahk_class CabinetWClass"))||(hWnd:=WinExist("ahk_class ExploreWClass")) 
  {
      sa := Com_CreateObject("Shell.Application")		
			;Find hwnd window
			wins := sa.Windows
			loop % wins.count
			{
				window:=wins.Item(A_Index-1)
				If Not InStr( window.FullName, "steam.exe" ) ; ensure pwb isn't IE
				if(window.Hwnd=hWnd)
					break
			}
	    doc:=window.Document
      Loop, Parse, sSelect, `n 
				If  A_LoopField <>
				{ 
					item:=COM_Invoke(doc,"Folder.ParseName",A_LoopField)
					;first, deselect all but first item, then add other items to selection
					COM_Invoke(doc,"SelectItem",item,(A_Index=1 ? 29 : 1)) ;http://msdn.microsoft.com/en-us/library/bb774047(VS.85).aspx
				}
   } 
}

ShellFolder(hWnd=0,returntype=0) 
{ 
	If   hWnd||(hWnd:=WinExist("ahk_class CabinetWClass"))||(hWnd:=WinExist("ahk_class ExploreWClass")) 
  {
		sa := Com_CreateObject("Shell.Application")
		
		;Find hwnd window
		wins := sa.Windows
		loop % wins.count
		{
			window:=wins.Item(A_Index-1)
			If Not InStr( window.FullName, "steam.exe" ) ; ensure pwb isn't IE
			if(window.Hwnd=hWnd)
				break
		}	
    doc:=window.Document
    sFolder   := doc.Folder.Self.Path
    
    ;Don't get focussed item and selected files unless requested, because it will cause a COM error when called during/shortly after explorer path change sometimes
    if (returntype=2)
    	sFocus := doc.FocusedItem.Name
    if(returntype=3 || returntype=4)
	    loop % doc.SelectedItems.Count
	    	sSelect .= (returntype=3 ? sFolder "\" COM_Invoke(doc.SelectedItems, "Item", A_Index-1).Name "`n" : COM_Invoke(doc.SelectedItems, "Item", A_Index-1).Name "`n")
	  	
	  ;Remove last `n
    StringTrimRight, sSelect, sSelect, 1
		if (returntype=1)
			Return   sFolder
		else if (returntype=2)
			Return   sFocus
		else if (returntype=3)
			Return   sSelect
		else if (returntype=4)
			Return 	 sSelect
  }
}


Yogui
  • Members
  • 56 posts
  • Last active: Sep 13 2011 07:09 AM
  • Joined: 14 Jun 2008
Hi,

I must be doing something silly but can't work it out.

When I include these last 3 functions in a test I get and error at load

The following variable name contains an illegal character:
"sa.Windows"

Obviously the dot... in (and others)

wins := sa.Windows

I got current COM and autohotkey.exe

I'm Using Win7 64bit

Help appreciated! :oops:
Thanks, Yogui.
_____________________________

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
That code is for Autohotkey_L.
Do something like

doc:=COM_Invoke(window,"Document")

instead of

doc:=window.Document

Or just use Autohotkey_L if it's an option for you.

Yogui
  • Members
  • 56 posts
  • Last active: Sep 13 2011 07:09 AM
  • Joined: 14 Jun 2008
I set up AHK_L (asked question at the AHK_L post) http://www.autohotke...r=asc&start=509

Still having errors I have to play with it. :wink:

Your 3 functions should work in W7x64?
Thanks, Yogui.
_____________________________

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Yes they should.

Did you include COM.ahk ?

Yogui
  • Members
  • 56 posts
  • Last active: Sep 13 2011 07:09 AM
  • Joined: 14 Jun 2008
Hi,

This should work but it does not :cry:

^1 Does Nothing
^2 Gives errors
Function Name: "Folder"
ERROR: No COM Dispatch Object!
()
^3 All four are a Space

Return

#Include %A_ScriptDir%\COM.ahk		;COM L version is in the same dir

;==============================================================================
^1 Up::
KeyWait, Control

ShellNavigate(A_ScriptDir, WinExist("A"))

Return

;==============================================================================
^2 Up::
KeyWait, Control

SelectFiles(A_ScriptFullPath, WinExist("A"))

Return

;==============================================================================
^3 Up::
KeyWait, Control

Type1 := ShellFolder(WinExist("A"),1)
Type2 := ShellFolder(WinExist("A"),2)
Type3 := ShellFolder(WinExist("A"),3)
Type4 := ShellFolder(WinExist("A"),4)

ToolTip, Type1: >%Type1%<`nType2: >%Type2%<`nType3: >%Type3%<`nType4: >%Type4%<

Return

;From http://www.autohotkey.com/forum/topic20701-60.html

ShellNavigate(sPath, hWnd=0)
{
   If   hWnd||(hWnd:=WinExist("ahk_class CabinetWClass"))||(hWnd:=WinExist("ahk_class ExploreWClass"))
  {
   sa := Com_CreateObject("Shell.Application")
   wins := sa.Windows
   loop % wins.count
   {
      window:=wins.Item(A_Index-1)
      If Not InStr( window.FullName, "steam.exe" ) ; ensure pwb isn't IE
      if(window.Hwnd=hWnd)
         break
   }   
    DllCall("shell32\SHParseDisplayName", "Uint", COM_Unicode4Ansi(wPath,sPath) , "Uint", 0, "UintP", pidl, "Uint", 0, "Uint", 0)
    VarSetCapacity(sa,24,0), NumPut(DllCall("shell32\ILGetSize","Uint",pidl), NumPut(pidl, NumPut(1, NumPut(1,sa)),4))
      Window.Navigate2(COM_Parameter(0x2011,&sa))
      return
  }
}

SelectFiles(sSelect, hWnd=0)
{
   If   hWnd||(hWnd:=WinExist("ahk_class CabinetWClass"))||(hWnd:=WinExist("ahk_class ExploreWClass"))
  {
      sa := Com_CreateObject("Shell.Application")      
         ;Find hwnd window
         wins := sa.Windows
         loop % wins.count
         {
            window:=wins.Item(A_Index-1)
            If Not InStr( window.FullName, "steam.exe" ) ; ensure pwb isn't IE
            if(window.Hwnd=hWnd)
               break
         }
       doc:=window.Document
      Loop, Parse, sSelect, `n
            If  A_LoopField <>
            {
               item:=COM_Invoke(doc,"Folder.ParseName",A_LoopField)
               ;first, deselect all but first item, then add other items to selection
               COM_Invoke(doc,"SelectItem",item,(A_Index=1 ? 29 : 1)) ;http://msdn.microsoft.com/en-us/library/bb774047(VS.85).aspx
            }
   }
}


ShellFolder(hWnd=0,returntype=0)
{
   If   hWnd||(hWnd:=WinExist("ahk_class CabinetWClass"))||(hWnd:=WinExist("ahk_class ExploreWClass"))
  {
      sa := Com_CreateObject("Shell.Application")
      
      ;Find hwnd window
      wins := sa.Windows
      loop % wins.count
      {
         window:=wins.Item(A_Index-1)
         If Not InStr( window.FullName, "steam.exe" ) ; ensure pwb isn't IE
         if(window.Hwnd=hWnd)
            break
      }   
    doc:=window.Document
    sFolder   := doc.Folder.Self.Path
   
    ;Don't get focussed item and selected files unless requested, because it will cause a COM error when called during/shortly after explorer path change sometimes
    if (returntype=2)
       sFocus := doc.FocusedItem.Name
    if(returntype=3 || returntype=4)
       loop % doc.SelectedItems.Count
          sSelect .= (returntype=3 ? sFolder "\" COM_Invoke(doc.SelectedItems, "Item", A_Index-1).Name "`n" : COM_Invoke(doc.SelectedItems, "Item", A_Index-1).Name "`n")
        
     ;Remove last `n
    StringTrimRight, sSelect, sSelect, 1
      if (returntype=1)
         Return   sFolder
      else if (returntype=2)
         Return   sFocus
      else if (returntype=3)
         Return   sSelect
      else if (returntype=4)
         Return     sSelect
  }
}

Thanks, Yogui.
_____________________________

Yogui
  • Members
  • 56 posts
  • Last active: Sep 13 2011 07:09 AM
  • Joined: 14 Jun 2008
Sorry I forgot to mention.

I'm not good enough with COM to understand whats wrong. :oops:

If I try modify your functions to run with normal AHK (as you sugested before).

What would it happen with the calls to:
COM_Parameter

Thanks, Yogui.
_____________________________

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Use the edit function ;)

You still have sa.Windows in the script you posted.
I didn't check any further, make sure not to use the dot operator for something like this in vanilla AHK.