Getting CLSID or SpecialFolderConstant of an Explorer object

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Getting CLSID or SpecialFolderConstant of an Explorer object

16 Oct 2014, 16:25

Hi,

Considering this code:

Code: Select all

For pExplorer in ComObjCreate("Shell.Application").Windows ; see http://msdn.microsoft.com/en-us/library/windows/desktop/aa752084(v=vs.85).aspx
{
	strType := ""
	try
		strType := pExplorer.Type ; Gets the user type name of the contained document object. "Document HTML" for IE windows. Should be empty for file Explorer windows.
	if !StrLen(strType)
		MsgBox, % "a"
			. "pExplorer.LocationURL: !" . pExplorer.LocationURL . "!`n"
			. "pExplorer.Type: !" . strType . "!`n"
			. "pExplorer.LocationName: !" . pExplorer.LocationName . "!`n"
			. "pExplorer.HWND: !" . pExplorer.HWND . "!"
}
If an Explorer contains one of the special folders identified by a CLSID string (like "{20d04fe0-3aea-1069-a2d8-08002b30309d}" for My Computer), the LocationURL proterty of the object returned by the ComObjCreate("Shell.Application").Windows command is empty.

How could I retrieve the CLSID of this special folder?

Or, alternatively, how could I retrieve the ShellSpecialFolderConstantsthat can be used with the Shell.Explore command?

Thanks!
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

21 Oct 2014, 12:45

JnLlnd wrote:How could I retrieve the CLSID of this special folder?
From what I've read on MSDN, you can find a special folder's CLSID in the Registry,
specifically in the HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID key.

Special folder keys contain:
- a REG_SZ type with the folder name as the Default sub key.
- one or more REG_EXPAND_SZ sub key's with the value: @%SystemRoot%\system32\shell32.dll,-*somenumber*
Search 1st key I posted for the folder name and additional sub key info as your criteria should get you the CLSID.
htms
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

22 Oct 2014, 04:10

Not sure if I understand what you want, but e.g. "My Computer" is a virtual folder without a file system path.

You might want to have a look at KNOWNFOLDERID.
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

22 Oct 2014, 08:37

Let me explain what I want to to: I want to save info about the group of Explorer instances (screen position/size and current folder) in order to allow the user to re-open this group (restoring Explorer windows position and content).

Saving position is easy. Saving the current path works well with the .LocationURL property of the .Windows object.

The problem I have is with special folders (My Computer, etc.). With the .Windows object normally returns the current path of the Explorer window in the .LocationURL property. But for special folders, this property is empty. If it was containing the CLSID' I could use "Run, ::{CLSID}" to restore this Explorer instance. But it is empty...

The only info available is the .LocationName. But this value is localized. This mean that I could not use it as-is to lookup the CLSID info in the registry (or another technique with KNONWFOLDERID). Since my app is running on a large number of localized Windows version, this value is of little help as-is.

Thanks to TLM, I found in the Registry entries the command to get the localized string based of the CLSID. What I'm working on is to build (at script initialization) a lookup table (using Object) containing "localized name -> CLSID". Then, when I later save an instance of Explorer containing a special folder, I can use this lookup table to find the CLSID based on the localized name.

Now I have to look at KnownFolders. At first view, I can't find how to get localized names based on a KnownFolderID.

All this being said, maybe there is another war to get the current folder of an Explorer? I'm excluding the option of sending keys to the GUI and using the clipboard, which is too slow and risky, specially with localized versions of Windows).

Thank you to both of you for your input.
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

22 Oct 2014, 20:36

Here is a script doing what I described earlier. Not sure this is the most efficient solution but it "does the jod". Based on CLSID (not KNONWFOLDERID).

Code: Select all


; objClassIdByLocalizedName["SpecialFolderLocalizedName"] returns the CLSID of this special folder like "{20d04fe0-3aea-1069-a2d8-08002b30309d}"

global objClassIdByLocalizedName := Object()
Gosub, InitClassIdByLocalizedName

; for the demo, open these special folders
Run, ::{20d04fe0-3aea-1069-a2d8-08002b30309d} ; My Computer / Ordinateur (in French)
Run, ::{450d8fba-ad25-11d0-98a8-0800361b1103} ; My Documents / Mes documents (in French)
Run, ::{031E4825-7B94-4dc3-B131-E946B44C8DD5} ; Libraries / Bibliothèques (in French)

Sleep, 500

strListLocationNames := ""
strListHandles := ""

; scan the open Explorer
; see http://msdn.microsoft.com/en-us/library/windows/desktop/aa752084(v=vs.85).aspx
; pExplorer := ComObjCreate("Shell.Application").Windows[1]
For pExplorer in ComObjCreate("Shell.Application").Windows
{
	strType := ""
	; Gets the user type name of the contained document object. "Document HTML" for IE windows.
	try
		strType := pExplorer.Type
	; .Type is empty for file Explorer windows
	if !StrLen(strType)
	{
		MsgBox, % ""
			. "pExplorer.LocationURL: " . pExplorer.LocationURL . "`n"
			. "pExplorer.LocationName: " . pExplorer.LocationName
		
		; save the strSpecialFolderLocation to test how this work 
		strListLocationNames .= pExplorer.LocationName . "|"
		strListHandles .= pExplorer.HWND . "|"
	}
}
StringTrimRight, strListLocationNames, strListLocationNames, 1 ; remove extra delimiter
StringTrimRight, strListHandles, strListHandles, 1 ; remove extra delimiter

; To check than we can reopen these special folders, close them here.
Loop, Parse, strListHandles, |
	WinClose, ahk_id %A_LoopField%
Sleep, 500

; Now check if we can reopen these special folders by their localized name

Loop, Parse, strListLocationNames, |
{
	strLocationName := A_LoopField
	
	if InStr(strLocationName, "(") ; strip the "(workgroup name)" added to "Network" location name
		StringLeft, strLocationName, strLocationName, % InStr(strLocationName, "(") - 2
	
	; lookup the CLSID based on the localised location name
	strSpecialFolderLocation := objClassIdByLocalizedName[strLocationName]
	MsgBox, % "REOPENING`n"
		. "strLocationName: " . strLocationName . "`n"
		. "strSpecialFolderLocation: " . strSpecialFolderLocation
		
	; reopen it using the strLocationURL (if we have) or the strSpecialFolderLocation
	Run, explorer.exe shell:::%strSpecialFolderLocation%
	Sleep, 500
}

return


;------------------------------------------------------------
InitClassIdByLocalizedName:
;------------------------------------------------------------

; with folder in .LocationURL
InitClassId("{450d8fba-ad25-11d0-98a8-0800361b1103}") ;	My Documents / Mes documents
InitClassId("{4336a54d-038b-4685-ab02-99bb52d3fb8b}") ; Public Folder / Public
InitClassId("{59031a47-3f72-44a7-89c5-5595fe6b30ee}") ; (User files) <User name> / <User name>

; with only localized name in .LocationName
InitClassId("{20d04fe0-3aea-1069-a2d8-08002b30309d}") ; My Computer / Ordinateur
InitClassId("{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}") ;	Computer and devices / Réseau
InitClassId("{645ff040-5081-101b-9f08-00aa002f954e}") ;	Recycle Bin / Corbeille
InitClassId("{031E4825-7B94-4dc3-B131-E946B44C8DD5}") ; Libraries / Bibliothèques
InitClassId("{22877a6d-37a1-461a-91b0-dbda5aaebc99}") ;	Recent Places / Emplacements récents -> cannot be open by Run, needs "explorer.exe shell:::{CLSID}"
InitClassId("{21EC2020-3AEA-1069-A2DD-08002B30309D}") ;	All Control Panel items / Tous les Panneaux de configuration
InitClassId("{26EE0668-A00A-44D7-9371-BEB064C98683}") ;	Control Panel / Panneau de configuration
InitClassId("{208d2c60-3aea-1069-a2d7-08002b30309d}") ;	My Network Places / Réseau (WORKGROUP)
InitClassId("{992CFFA0-F557-101A-88EC-00DD010CCC48}") ;	Network Connections / Connexions réseau ### XL + ME
InitClassId("{7007acc7-3202-11d1-aad2-00805fc1270e}") ;	Network Connections / Connexions réseau ### XP
InitClassId("{E7DE9B1A-7533-4556-9484-B26FB486475E}") ;	Network Map / Mappage réseau
InitClassId("{2227a280-3aea-1069-a2de-08002b30309d}") ;	Printers / Imprimantes

return
;------------------------------------------------------------


;------------------------------------------------------------
InitClassId(strClassId)
;------------------------------------------------------------
{
    strLocalizedName := GetLocalizedNameForClassId(strClassId)
    objClassIdByLocalizedName.Insert(strLocalizedName, strClassId)
}
;------------------------------------------------------------


;------------------------------------------------------------
GetLocalizedNameForClassId(strClassId)
; get the command in the registry to retrieve the localized name
;------------------------------------------------------------
{
    /*
        Question : What's the best Registry key for this to work on Win XP, 7, 8+ ?
    
        HKEY_CLASSES_ROOT\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}
        HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}
        HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}
        HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}
        HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}
    */
    RegRead, strLocalizedString, HKEY_CLASSES_ROOT, CLSID\%strClassId%, LocalizedString
    ; strLocalizedString example: "@%SystemRoot%\system32\shell32.dll,-9216"

    StringSplit, arrLocalizedString, strLocalizedString, `,
    intDllNameStart := InStr(arrLocalizedString1, "\", , 0) ; search for \ from the end
    StringRight, strDllFile, arrLocalizedString1, % StrLen(arrLocalizedString1) - intDllNameStart
    strDllIndex := arrLocalizedString2
    
    return TranslateMUI(strDllFile, Abs(strDllIndex))
}
;------------------------------------------------------------


;------------------------------------------------------------
TranslateMUI(resDll, resID)
; source: 7plus (https://github.com/7plus/7plus/blob/master/MiscFunctions.ahk)
;------------------------------------------------------------
{
	VarSetCapacity(buf, 256) 
	hDll := DllCall("LoadLibrary", "str", resDll, "Ptr") 
	Result := DllCall("LoadString", "Ptr", hDll, "uint", resID, "str", buf, "int", 128)
	return buf
}
;------------------------------------------------------------


;------------------------------------------------------------
DecodeLocationURL(strLocationURL)
;------------------------------------------------------------
{
    strDecoded :=  UriDecode(strLocationURL)
	StringReplace, strDecoded, strDecoded, file:///
	StringReplace, strDecoded, strDecoded, /, \, A
    
    return strDecoded
}
;------------------------------------------------------------


;------------------------------------------------
UriDecode(str)
; by polyethene
; http://www.autohotkey.com/board/topic/17367-url-encoding-and-decoding-of-special-characters/?p=112822
;------------------------------------------------
{
	Loop
		If RegExMatch(str, "i)(?<=%)[\da-f]{1,2}", hex)
			StringReplace, str, str, `%%hex%, % Chr("0x" . hex), All
		Else
			Break
	return str
}
;------------------------------------------------
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

23 Oct 2014, 01:45

Code: Select all

for window in ComObjCreate("Shell.Application").Windows
	try MsgBox % window.Document.Folder.Self.Path
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

23 Oct 2014, 04:21

[OT]
I'm loosing my personal fights with COM objects rather often. Apparently I don't find the proper documentation. Out of couriosity, I played around wit this example and got the following code working like I understand it should:

Code: Select all

#NoEnv
^+w::
Count := 0
For Window in ComObjCreate("Shell.Application").Windows ; see http://msdn.microsoft.com/en-us/library/windows/desktop/aa752084(v=vs.85).aspx
{
   If (Folder := Window.Document.Folder.Self) ; && Window.Visible
      MsgBox, 0, Shell Windows
            , % ++Count . "`n"
              . "Window.HWND: !"    . Window.HWND . "!`n"
              . "Window.Visible: !" . Window.Visible . "!`n"
              . "Folder.Path: !"    . Folder.Path . "!`n"
              . "Folder.Name: !"    . Folder.Name . "!`n"
              . "Folder.Type: !"    . Folder.Type . "!"
}
Return
Esc::ExitApp
But I again couldn't find the places where the properties Window.HWND and Window.Document.Folder are documented.

JnLlnd & lexikos, would you shed some light on this, please?
[/OT]
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

23 Oct 2014, 04:24

lexikos wrote:

Code: Select all

for window in ComObjCreate("Shell.Application").Windows
	try MsgBox % window.Document.Folder.Self.Path
very nice :)
just me wrote: But I again couldn't find the places where the properties Window.HWND and Window.Document.Folder are documented.
JnLlnd & lexikos, would you shed some light on this, please?
I'm with you on this.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

23 Oct 2014, 10:03

just me wrote:But I again couldn't find the places where the properties Window.HWND and Window.Document.Folder are documented.
For Window.Document.Folder:
If I understand correctly, the Windows property returns a ShellWindows object and each item(Window) is an InternetExplorer object. As per documentation of the Document property:
Document object wrote:When the active document is an HTML page, this property provides access to the contents of the HTML Document Object Model (DOM).
OTHERWISE
When other document types are active, such as a Microsoft Word document, this property returns the default IDispatch dispatch interface (dispinterface) pointer for the hosted document object.
So in this case, I assume since it's not a HTML document, the Document property returned the default IDispatch dispatch interface. The Folder must be a Folder OR a Folder2 object. For HWND, it must be this HWND since Window is an InternetExplorer object.

I may be wrong though....
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

23 Oct 2014, 14:20

Interesting, thanks Coco. That does explain a lot.
There should probably some reference to this in the Shell object properties documentation.
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

23 Oct 2014, 21:24

ComObjType can tell you the default interface name, from which you can usually find the documentation for most of the methods/properties.

Code: Select all

for window in ComObjCreate("Shell.Application").Windows
{
   MsgBox % ComObjType(window, "Name")  ; IWebBrowser2
   MsgBox % ComObjType(window.Document, "Name")  ; IShellFolderViewDual2
   MsgBox % ComObjType(window.Document.Folder, "Name")  ; Folder3
   MsgBox % ComObjType(window.Document.Folder.Self, "Name")  ; FolderItem2
}
The ComObjQuery help page has an example for retrieving the class name via the IProvideClassInfo interface.
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

24 Oct 2014, 01:46

Thanks, lexikos and coco!
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

24 Oct 2014, 11:04

lexikos wrote:ComObjType can tell you the default interface name, from which you can usually find the documentation for most of the methods/properties.
The ComObjQuery help page has an example for retrieving the class name via the IProvideClassInfo interface.
Perfect!
This will definitely be used.

thanks :)
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

09 Nov 2014, 19:23

lexikos wrote:

Code: Select all

for window in ComObjCreate("Shell.Application").Windows
	try MsgBox % window.Document.Folder.Self.Path
Hummm... I'll save quite a bunch of lines in my code with those only two lines :-)

Thanks lexikos, just me and coco for these infos.

[OT]
I missed email alerts for these updates. I checked and I'm correctly subscribed to this topic. Am I alone not receiving alertsd from this forum?
[/OT]
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

16 Nov 2014, 19:30

lexikos wrote:

Code: Select all

for window in ComObjCreate("Shell.Application").Windows
	try MsgBox % window.Document.Folder.Self.Path
Hi lexikos,

What is the reason for using try? Or what's the risk of not using it?

FYI, in my code, I check if window.Type is not empty to make sure window is a file Explorer (not an Internet Explorer) before reading the Document.Folder.Self.Path value.

Thanks.
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

16 Nov 2014, 20:48

The ShellWindows collection includes Internet Explorer windows, for which 'Folder' is not a valid property. The try statement allows those to be skipped without having to check what type of window it is.
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Getting CLSID or SpecialFolderConstant of an Explorer ob

16 Nov 2014, 22:36

lexikos wrote:The ShellWindows collection includes Internet Explorer windows, for which 'Folder' is not a valid property. The try statement allows those to be skipped without having to check what type of window it is.
Good. That way, my code is safe because I read the Path only for the correct Type. Thanks.
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: haomingchen1998 and 241 guests