Explorer column interaction (get/set: which appear, width, ascending/descending order)

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Explorer column interaction (get/set: which appear, width, ascending/descending order)

14 Jun 2017, 20:23

The IColumnManager and IFolderView2 interfaces offer methods to interact with columns in Explorer folder windows.

==================================================

3 example scripts:
Sort folder's contents by date - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 34#p174634
Toggle specific column in File Explorer (then set its order) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 84#p185684
[show 2 columns only: date modified and file type]
Question for jeeswg - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 01#p190801

==================================================

IColumnManager interface (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
IFolderView2 interface (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx

These functions make it possible to: count/list and add/remove columns programmatically, to get/set which columns are used to sort the files, and to get/set whether the order is ascending/descending.

The JEE_ExpGetInterfaces function for getting access to the interfaces is based on code by and mainly thanks to qwerty12:
Explorer: jump to first file/folder - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 13#p153513

I obtained the offset for each method, for both interfaces by using:
C:\Program Files (x86)\Windows Kits\8.1\Include\um\ShObjIdl.h
which came with Visual Studio.

It seems that with some objects/interfaces you can specify '.MethodName()' but with others you need to use DllCall and NumGet to refer to the methods. There are some details here:
COM interface tutorial
https://maul-esel.github.io/tutorials/C ... faces.html

I also made some comments about retrieving the list of methods for an interface at 'INTERFACES: GET METHOD LIST' at:
jeeswg's objects tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=29232

Note some information can be retrieved, and interaction done, e.g. listing and simulating clicking column headers, via the Acc library, there is an e.g. lower down:
Acc library (MSAA) and AccViewer download links - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=26201

Btw it appears that to uniquely identify a column header you need a CLSID and a property identifier, (these are shown in example lower down,) and that some column headers share the same CLSID.

==================================================

[Links: (IColumnManager)]
How to obtain the list of visible columns in a shell view – Windows SDK Support Team Blog
https://blogs.msdn.microsoft.com/winsdk ... hell-view/
windows - Copying file details from Explorer as tabular text - Stack Overflow
https://stackoverflow.com/questions/266 ... bular-text

[Links: (StringFromCLSID)]
DllCall to get idle time until computer sleeps - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=25935
[AHK_L] Custom AutoCompletion for edit control, with drop down list - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/9612 ... down-list/

==================================================

[EDIT:][some code for changing for the view mode in Explorer]
Reading Win 10 File Explorer View Mode - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 46#p132846
Last edited by jeeswg on 12 Apr 2018, 22:15, edited 2 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

15 Jun 2017, 23:21

Note: the functions are finished except for JEE_ICMGetColumnWidth which isn't working (if anyone can help identify what the problem is), although I do provide a method below to retrieve a column width via the Acc library.

Code: Select all

;q:: ;list visible/all columns
WinGet, hWnd, ID, A
WinGetClass, vWinClass, % "ahk_id " hWnd
if !(vWinClass = "CabinetWClass") && !(vWinClass = "ExploreWClass")
	return
oWin := JEE_ExpWinGetObj(hWnd)
JEE_ExpGetInterfaces(oWin, isp, isb, isv, ifv2, icm)

vOutput := "visible columns: " JEE_ICMGetColumnCount(icm)
vOutput .=  "`r`n" "all columns: " JEE_ICMGetColumnCount(icm, "a")
MsgBox, % vOutput

vOutput := "visible columns:"
vOutput .= "`r`n`r`n" JEE_ICMGetColumns(icm, "`n")
MsgBox, % vOutput

vOutput := "all columns:"
vOutput .= "`r`n`r`n" JEE_ICMGetColumns(icm, "`n", "cna")
Clipboard := vOutput
MsgBox, % vOutput

isp := isb := isv := ifv2 := icm := ""
return

;==================================================

;q:: ;set which columns appear, and get/set width of Name column
WinGet, hWnd, ID, A
oWin := JEE_ExpWinGetObj(hWnd)
JEE_ExpGetInterfaces(oWin, isp, isb, isv, ifv2, icm)
vListAbbrev := "nam,mod,typ,siz,len"
vList := JEE_ExpColAbbrevToName(vListAbbrev, ",")
MsgBox, % StrReplace(vList, ",", "`n")
JEE_ICMSetColumns(icm, vList, ",")

;vWidth := JEE_ICMGetColumnWidth(icm, "System.ItemNameDisplay", vWidthIdeal) ;not working
;MsgBox, % vWidth " " vWidthIdeal
;JEE_ICMSetColumnWidth(icm, "System.ItemNameDisplay", 10)

isp := isb := isv := ifv2 := icm := ""
return

;==================================================

;q:: ;list/set column(s) that files are sorted by
WinGet, hWnd, ID, A
oWin := JEE_ExpWinGetObj(hWnd)
JEE_ExpGetInterfaces(oWin, isp, isb, isv, ifv2, icm)

MsgBox, % "sort column count:`r`n`r`n" JEE_IFV2GetSortColumnCount(ifv2)
MsgBox, % JEE_IFV2GetSortColumns(ifv2)

Sleep 800
vList := "System.ItemNameDisplay -1" ;descending
JEE_IFV2SetSortColumns(ifv2, vList)
Sleep 800
vList := "System.ItemNameDisplay 1" ;ascending
JEE_IFV2SetSortColumns(ifv2, vList)

isp := isb := isv := ifv2 := icm := ""
return

;==================================================

JEE_ExpWinGetObj(hWnd)
{
	for oWin in ComObjCreate("Shell.Application").Windows
		if (oWin.HWND == hWnd)
			break
	return oWin
}

;==================================================

;e.g. JEE_ExpGetInterfaces(oWin, isp, isb, isv, ifv2, icm)
;e.g. isp := isb := isv := ifv2 := icm := ""
JEE_ExpGetInterfaces(oWin, ByRef isp="", ByRef isb="", ByRef isv="", ByRef ifv2="", ByRef icm="")
{
	isp := ComObjQuery(oWin, "{6d5140c1-7436-11ce-8034-00aa006009fa}")
	, isb := ComObjQuery(isp, "{4C96BE40-915C-11CF-99D3-00AA004AE837}", "{000214E2-0000-0000-C000-000000000046}")
	if (DllCall(NumGet(NumGet(isb+0)+15*A_PtrSize), Ptr,isb, PtrP,isv) < 0) ;QueryActiveShellView
		return
	ifv2 := ComObjQuery(isv, "{1af3a467-214f-4298-908e-06b03e0b39f9}")
	icm := ComObjQuery(ifv2, "{d8ec27bb-3f3b-4042-b10a-4acfd924d453}")
}

;==================================================

;custom abbreviation to canonical property name
JEE_ExpColAbbrevToName(vList, vDelim="`n")
{
	oArray := {nam:"System.ItemNameDisplay"
	,siz:"System.Size"
	,typ:"System.ItemTypeText"
	,mod:"System.DateModified"
	,cre:"System.DateCreated"
	,acc:"System.DateAccessed"
	,dat:"System.ItemDate"
	,dur:"System.Calendar.Duration"
	,dim:"System.Image.Dimensions"
	,len:"System.Media.Duration"}

	vOutput := ""
	Loop, Parse, vList, % vDelim
		vOutput .= ((A_Index = 1) ? "" : vDelim) oArray[A_LoopField]
	return vOutput
}

;==================================================

;IColumnManager interface (Windows)
;https://msdn.microsoft.com/en-us/library/windows/desktop/bb776149(v=vs.85).aspx
;methods (8): C:\Program Files (x86)\Windows Kits\8.1\Include\um\ShObjIdl.h

;==================================================

JEE_ICMGetColumnCount(icm, vMode="")
{
	vFlags := InStr(vMode, "a") ? 0x1 : 0x2
	;CM_ENUM_VISIBLE := 0x2 ;CM_ENUM_ALL := 0x1
	DllCall(NumGet(NumGet(icm+0)+5*A_PtrSize), Ptr,icm, UInt,vFlags, UIntP,vCountCol) ;GetColumnCount
	return vCountCol
}

;==================================================

;mode: n (get name), c (get CLSID and property identifier), a (get all)
JEE_ICMGetColumns(icm, vSep="`n", vMode="n")
{
	vFlags := InStr(vMode, "a") ? 0x1 : 0x2
	DllCall(NumGet(NumGet(icm+0)+5*A_PtrSize), Ptr,icm, UInt,vFlags, UIntP,vCountCol) ;GetColumnCount
	vOutput := ""
	VarSetCapacity(vOutput, vCountCol*100*2)
	vOutput := ""
	;CM_ENUM_VISIBLE := 0x2 ;CM_ENUM_ALL := 0x1
	vArrayPROPERTYKEY := ""
	VarSetCapacity(vArrayPROPERTYKEY, vCountCol*20, 0)
	DllCall(NumGet(NumGet(icm+0)+6*A_PtrSize), Ptr,icm, UInt,vFlags, Ptr,&vArrayPROPERTYKEY, UInt,vCountCol) ;GetColumns
	Loop, % vCountCol
	{
		vOffset := (A_Index-1)*20
		if InStr(vMode, "c")
		{
			DllCall("ole32\StringFromCLSID", Ptr,&vArrayPROPERTYKEY+vOffset, PtrP,vAddrCLSID)
			vCLSID := StrGet(vAddrCLSID, "UTF-16")
			vNum := NumGet(vArrayPROPERTYKEY, vOffset+16, "UInt")
			vOutput .= vCLSID " " vNum
		}
		if InStr(vMode, "n")
		{
			if InStr(vMode, "c")
				vOutput .= "`t"
			DllCall("propsys\PSGetNameFromPropertyKey", Ptr,&vArrayPROPERTYKEY+vOffset, PtrP,vAddrName)
			vName := StrGet(vAddrName, "UTF-16")
			vOutput .= vName
		}
		vOutput .= vSep
	}
	vOutput := SubStr(vOutput, 1, -StrLen(vSep))
	return vOutput
}

;==================================================

JEE_ICMSetColumns(icm, vList, vSep="`n")
{
	DllCall(NumGet(NumGet(icm+0)+5*A_PtrSize), Ptr,icm, UInt,vFlags, UIntP,vCountCol) ;GetColumnCount
	vList := StrReplace(vList, vSep, vSep, vCountCol)
	vCountCol++
	vArrayPROPERTYKEY := ""
	VarSetCapacity(vArrayPROPERTYKEY, vCountCol*20, 0)
	Loop, Parse, vList, % vSep
	{
		vOffset := (A_Index-1)*20
		DllCall("propsys\PSGetPropertyKeyFromName", Str,A_LoopField, Ptr,&vArrayPROPERTYKEY+vOffset)
	}
	DllCall(NumGet(NumGet(icm+0)+7*A_PtrSize), Ptr,icm, Ptr,&vArrayPROPERTYKEY, UInt,vCountCol) ;SetColumns
	return
}

;==================================================

;not working (error 0x8000FFFF)
;is 'ideal' width, the autosize width?
JEE_ICMGetColumnWidth(icm, vName, ByRef vWidthIdeal="")
{
	VarSetCapacity(PROPERTYKEY, 20, 0)
	DllCall("propsys\PSGetPropertyKeyFromName", Ptr,&vName, Ptr,&PROPERTYKEY)
	VarSetCapacity(CM_COLUMNINFO, 184, 0)
	NumPut(184, CM_COLUMNINFO, 0, "UInt") ;cbSize
	;CM_MASK_WIDTH := 0x1 ;CM_MASK_IDEALWIDTH := 0x4
	NumPut(0x5, CM_COLUMNINFO, 4, "UInt") ;dwMask
	DllCall(NumGet(NumGet(icm+0)+4*A_PtrSize), Ptr,icm, Ptr,&PROPERTYKEY, Ptr,&CM_COLUMNINFO) ;GetColumnInfo
	vWidthIdeal := NumGet(CM_COLUMNINFO, 20, "UInt") ;uIdealWidth
	return NumGet(CM_COLUMNINFO, 12, "UInt") ;uWidth
}

;==================================================

JEE_ICMSetColumnWidth(icm, vName, vWidth)
{
	VarSetCapacity(PROPERTYKEY, 20, 0)
	DllCall("propsys\PSGetPropertyKeyFromName", Ptr,&vName, Ptr,&PROPERTYKEY)
	VarSetCapacity(CM_COLUMNINFO, 184, 0)
	NumPut(184, CM_COLUMNINFO, 0, "UInt") ;cbSize
	;CM_MASK_WIDTH := 0x1
	NumPut(0x1, CM_COLUMNINFO, 4, "UInt") ;dwMask
	NumPut(vWidth, CM_COLUMNINFO, 12, "UInt") ;dwMask
	DllCall(NumGet(NumGet(icm+0)+3*A_PtrSize), Ptr,icm, Ptr,&PROPERTYKEY, Ptr,&CM_COLUMNINFO) ;SetColumnInfo
}

;==================================================

;IFolderView2 interface (Windows)
;https://msdn.microsoft.com/en-us/library/windows/desktop/bb775541(v=vs.85).aspx
;methods (42): C:\Program Files (x86)\Windows Kits\8.1\Include\um\ShObjIdl.h

;==================================================

JEE_IFV2GetSortColumnCount(ifv2)
{
	DllCall(NumGet(NumGet(ifv2+0)+26*A_PtrSize), Ptr,ifv2, IntP,vCountCol) ;GetSortColumnCount
	return vCountCol
}

;==================================================

JEE_IFV2GetSortColumns(ifv2, vSep="`n", vMode="n")
{
	DllCall(NumGet(NumGet(ifv2+0)+26*A_PtrSize), Ptr,ifv2, IntP,vCountCol) ;GetSortColumnCount
	vOutput := ""
	vArraySORTCOLUMN := ""
	VarSetCapacity(vArraySORTCOLUMN, vCountCol*24, 0)
	DllCall(NumGet(NumGet(ifv2+0)+28*A_PtrSize), Ptr,ifv2, Ptr,&vArraySORTCOLUMN, Int,vCountCol) ;GetSortColumns
	Loop, % vCountCol
	{
		vOffset := (A_Index-1)*24
		if (vMode = "n")
		{
			DllCall("propsys\PSGetNameFromPropertyKey", Ptr,&vArraySORTCOLUMN+vOffset, PtrP,vAddrName)
			vName := StrGet(vAddrName, "UTF-16")
			;SORT_ASCENDING := 1 ;SORT_DESCENDING := -1
			vDirection := NumGet(vArraySORTCOLUMN, vOffset+20, "Int")
			vOutput .= vName " " vDirection vSep
		}
		else if (vMode = "c")
		{
			DllCall("ole32\StringFromCLSID", Ptr,&vArraySORTCOLUMN+vOffset, PtrP,vAddrCLSID)
			vCLSID := StrGet(vAddrCLSID, "UTF-16")
			vNum := NumGet(vArraySORTCOLUMN, vOffset+16, "UInt")
			vDirection := NumGet(vArraySORTCOLUMN, vOffset+20, "Int")
			vOutput .= vCLSID " " vNum " " vDirection vSep
		}
	}
	vOutput := SubStr(vOutput, 1, -StrLen(vSep))
	return vOutput
}

;==================================================

JEE_IFV2SetSortColumns(ifv2, vList, vSep="`n")
{
	vList := StrReplace(vList, vSep, vSep, vCountCol)
	vCountCol++
	vArraySORTCOLUMN := ""
	VarSetCapacity(vArraySORTCOLUMN, vCountCol*24, 0)
	Loop, Parse, vList, % vSep
	{
		vOffset := (A_Index-1)*24
		vPos := InStr(A_LoopField, " ", 0, -1)
		vName := SubStr(A_LoopField, 1, vPos-1)
		vDirection := SubStr(A_LoopField, vPos+1)
		DllCall("propsys\PSGetPropertyKeyFromName", Str,vName, Ptr,&vArraySORTCOLUMN+vOffset)
		;SORT_ASCENDING := 1 ;SORT_DESCENDING := -1
		NumPut(vDirection, vArraySORTCOLUMN, vOffset+20, "Int")
	}
	DllCall(NumGet(NumGet(ifv2+0)+27*A_PtrSize), Ptr,ifv2, Ptr,&vArraySORTCOLUMN, Int,vCountCol) ;SetSortColumns
}

;==================================================
Additional code that doesn't use the functions:

Code: Select all

;q:: ;CLSID as string/binary data
vCLSID := "{000214E6-0000-0000-C000-000000000046}"
MsgBox, % vCLSID
VarSetCapacity(IID_IShellFolder, 16)
DllCall("ole32\CLSIDFromString", WStr,vCLSID, Ptr,&IID_IShellFolder)

DllCall("ole32\StringFromCLSID", Ptr,&IID_IShellFolder, PtrP,vAddrCLSID)
vCLSID := StrGet(vAddrCLSID, "UTF-16")
MsgBox, % vCLSID
return

;==================================================

q:: ;Acc - list Explorer column names and widths, click Explorer column header (invoke sort)
ControlGet, hCtl, Hwnd,, DirectUIHWND3, A
oAcc := Acc_Get("Object", "4", 0, "ahk_id " hCtl)
;e.g. header list may be child 1/2/3 depending on how many scrollbars there are
for _, oAcc in Acc_Children(oAcc)
	if (oAcc.accRole(0) = 33) ;header list
		break
vOutput := ""
for _, oChild in Acc_Children(oAcc)
	if IsObject(oChild)
		vOutput .= Acc_Location(oChild).w "`t" oChild.accName(0) "`r`n"

Acc_Children(oAcc).1.accDoDefaultAction(0)
Sleep 500

;doesn't seem to work if do it twice unless 'refresh' it
;e.g. by doing Acc_Get again
oAcc := Acc_Get("Object", "4.1", 0, "ahk_id " hCtl)
Acc_Children(oAcc).1.accDoDefaultAction(0)
Sleep 500
oAcc := oChild := ""

MsgBox, % vOutput
return

;==================================================

;q:: ;explorer (folders) - set view to details view
PostMessage, 0x111, 28747, 0, SHELLDLL_DefView1, A ;e.g. Windows 7
;PostMessage, 0x111, 28716, 0,, A ;e.g. Windows XP
return

;==================================================

;Script for Automatically Sizing Columns in Windows 7 - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/77196-script-for-automatically-sizing-columns-in-windows-7/

;q:: ;explorer (folders) - resize all columns (Size All Columns to Fit) (tested on Windows 7)
;{LCtrl Up} is used in case the hotkey itself uses Ctrl
ControlSend, DirectUIHWND3, {LCtrl Up}{LCtrl Down}{NumpadAdd}{LCtrl Up}, A
return
Last edited by jeeswg on 16 Jun 2017, 04:40, edited 2 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

16 Jun 2017, 01:46

Edit: I tried your version now, and this is working here:

Code: Select all

vWidth := JEE_ICMGetColumnWidth(icm, "System.ItemNameDisplay", vWidthIdeal) ;not working
MsgBox, % vWidth " " vWidthIdeal
;...
;...
;...
JEE_ICMGetColumnWidth(icm, vName, ByRef vWidthIdeal="")
{
	VarSetCapacity(PROPERTYKEY, 20, 0)
	DllCall("propsys\PSGetPropertyKeyFromName", Ptr,&vName, Ptr,&PROPERTYKEY)
	VarSetCapacity(CM_COLUMNINFO, 184, 0)
	NumPut(184, CM_COLUMNINFO, 0, "UInt") ;cbSize
	;CM_MASK_WIDTH := 0x1 ;CM_MASK_IDEALWIDTH := 0x4
	NumPut(0x1F, CM_COLUMNINFO, 4, "UInt") ;dwMask - changed mask to get all fields
	DllCall(NumGet(NumGet(icm+0)+4*A_PtrSize), Ptr,icm, Ptr,&PROPERTYKEY, Ptr,&CM_COLUMNINFO) ;GetColumnInfo
	MsgBox, 0, Name:, % StrGet(&CM_COLUMNINFO + 24, "UTF-16") ; added for testing
	vWidthIdeal := NumGet(CM_COLUMNINFO, 20, "UInt") ; changed offset
	return NumGet(CM_COLUMNINFO, 16, "UInt") ; changed offset
}
I didn't try, but I'm pretty sure that you have to pass the PROPERTYKEY returned by GetColumns to GetColumnWidth.
Also:
PSGetNameFromPropertyKey wrote:It is the responsibility of the calling application to use CoTaskMemFree to release the string referred to by ppszCanonicalName when it is no longer needed.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

16 Jun 2017, 09:58

Thanks just me. I tried your version of the function, but it still doesn't seem to work, unless you also made some other edits. I'm using Windows 7, and I tried the latest AHK v1.1 x32 and x64.

I will check up on CoTaskMemFree, btw are there are any other common dll functions used for freeing up memory? What does AHK use for freeing up variables/memory/objects? Cheers. [EDIT: I will start this as a new thread, after I finish collecting some of the dll functions I already know about.]
Last edited by jeeswg on 16 Jun 2017, 21:13, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
wyagd
Posts: 2
Joined: 01 Jun 2017, 09:59

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

16 Jun 2017, 21:08

@just me
your code I tested work in windows 10 32bit.but can't work in windows 7 32bit.
my mistake.

just me's version I change

Code: Select all

return NumGet(CM_COLUMNINFO, 12, "UInt")
just me's version and jeeswg's version of JEE_ICMGetColumnWidth both works for me on Windows 10, The two methods are not working properly on Windows 7.

in other words,The following code both can work on windows 10 ,but can't work on windows 7.

Code: Select all

NumPut(0x5, CM_COLUMNINFO, 4, "UInt")

Code: Select all

NumPut(0x1F, CM_COLUMNINFO, 4, "UInt")

Code: Select all

return NumGet(CM_COLUMNINFO, 12, "UInt")
Last edited by wyagd on 17 Jun 2017, 01:12, edited 1 time in total.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

16 Jun 2017, 21:16

Thanks for your response wyagd. So just me's version of JEE_ICMGetColumnWidth works for you on Windows 10, but not on Windows 7? Do let me know if you have any problems with any other bits of code or functions above.

Btw do you think these functions might be useful to you? Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

17 Jun 2017, 05:04

wyagd wrote:just me's version I change ...
Yes, I confused the offsets. Sorry!


@jeeswg:
Did you try other combinations of the CM_MASK enumeration? Are all failing on Win 7?
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

17 Jun 2017, 05:57

@wyagd Thanks again for the clarification.

@just me Yes I tried that, and I tried your version of the function. I'm getting blanks on NumGet and StrGet, and I think it's because the DllCall is failing.

If you check the return value from DllCall, it suggests an error. When the DllCall works, for other ICM/IFV2 functions, it's usually 0, but in this case I got 0x8000FFFF.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

17 Jun 2017, 12:47

jeeswg wrote:If you check the return value from DllCall, it suggests an error. When the DllCall works, for other ICM/IFV2 functions, it's usually 0, but in this case I got 0x8000FFFF.
just me's code also works for me on Windows 10 x64. The code from the link you provided also works to show the column widths of all the windows open on my 10 laptop (well, after adding a call to GetColumnInfo).

With Windows 7, however, the only way I was able to get a window's column widths was by taking the code from that link, compiling it into a DLL, making said code run on the same thread as the window and injecting the DLL into Explorer. I guess Windows 8 onwards does some marshalling or something.
(Although, not knowing much about COM, I wonder why GetColumns succeeds outside of Explorer to get the property key names of open windows on Windows 7...)

If this is really that important to you, you could adapt the "get opened common file dialogs' selected files" script I wrote that uses LibTCC, but IMO, unless someone figures out another way, I don't think it's worth it (going to such extremes to find out the width of columns on an OS that has its support cut off in ~3 years isn't worth anybody's time IMO).
Last edited by qwerty12 on 16 Aug 2017, 12:17, edited 1 time in total.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

17 Jun 2017, 14:46

Cheers, well I'll test getting column widths on Windows 10, and possibly improve the function slightly, when I get the chance.

I'll also try and develop a workaround function, to retrieve column widths, that uses the Acc library, see above an for Acc library example. @qwerty12: I'll try Acc, rather than inject a dll, but that is a great suggestion and cheers for testing that approach.

Further points:
- What is the 'ideal' width? Is it possible to retrieve a column's 'Size Column to Fit' width without applying it.
- Is it possible to invoke 'Size Column to Fit'/'Size All Columns to Fit', via a window message e.g. WM_COMMAND, or via objects. I was able to apply 'Size All Columns to Fit', above, via ControlSend, and I did try some tests with WM_COMMAND.
- Is it possible to convert descriptions like 'System.ItemNameDisplay' to their localised name e.g. 'Name'? Preferably without using another interface.
- Does IColumnManager have to be retrieved through IFolderView2, or can it be retrieved more directly from the Window object?
- I found that sometimes DoRename worked when applied directly to a Window object, without having to use any other interfaces. Any explanation of why this sometimes works, would be quite interesting. Although, I don't really need DoRename because you can use SelectItem and SVSI_EDIT.

Thanks for the responses, it would be interesting to know if these functions work on Windows XP/Vista/8. You can use a Window object on Windows XP, but on MSDN it says that the minimum supported client for both IFolderView2 and IColumnManager is Windows Vista.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

17 Jun 2017, 16:32

jeeswg wrote:@qwerty12: I'll try Acc, rather than inject a dll, but that is a great suggestion and cheers for testing that approach.
I think that would be the more prudent (and far less time consuming!) approach.
- Is it possible to invoke 'Size Column to Fit'/'Size All Columns to Fit', via a window message e.g. WM_COMMAND, or via objects. I was able to apply 'Size All Columns to Fit', above, via ControlSend, and I did try some tests with WM_COMMAND.
I didn't go all the way testing it, but the only way I could think of that might work once again involves running code on the same thread as the window through injection to be able to get the CDefView. My knowledge of the shell API isn't great; perhaps there is a documented way somewhere on MSDN... But I don't think WM_COMMAND will work as the size columns etc. menu is created then and there when you right-click a column. TrackPopupMenu is called only then.
- Is it possible to convert descriptions like 'System.ItemNameDisplay' to their localised name e.g. 'Name'? Preferably without using another interface.
The code provided in this thread already does that (call GetColumnInfo with CM_MASK_NAME set and read out wszName), but I can also see that might not be an option on Windows 7... I haven't tested it, but SHGetNameFromPropertyKey might work.
- Does IColumnManager have to be retrieved through IFolderView2, or can it be retrieved more directly from the Window object?
The earliest I got it was from the IShellView.
Thanks for the responses, it would be interesting to know if these functions work on Windows XP/Vista/8. You can use a Window object on Windows XP, but on MSDN it says that the minimum supported client for both IFolderView2 and IColumnManager is Windows Vista.
https://stackoverflow.com/a/35463918
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

17 Jun 2017, 16:41

Cheers re.:

c++ - WTSGetActiveConsoleSessionId - Minimum supported client / server incorrect? - Stack Overflow
https://stackoverflow.com/questions/354 ... 8#35463918
Windows XP and Windows Server 2003 are no longer supported, so the minimum supported client/server is Windows Vista and Windows Server 2008, respectively. A common mistake is, that developers read the information to mean "introduced in". This is not the case.
I did expect that 'supported' would literally mean 'supported' rather than 'introduced', the only problem with that though, is presumably all MSDN articles should thus say 'minimum supported client' as Windows Vista at a minimum, but for example this states Windows 2000 as the minimum supported client, unless lots of articles are just 'out-of-date' or unless Windows 2000 is still 'semi-supported' in some way:

WideCharToMultiByte function (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx

Then again, 'introduced' dates would be far more useful, if available somewhere.

==================================================

SHGetNameFromPropertyKey appears to give the same result as PSGetNameFromPropertyKey, giving the canonical name.

Btw there is PSGetPropertyKeyFromName, but there doesn't appear to be a SHGetPropertyKeyFromName.

Note: I tried to implement CoTaskMemFree, but it crashed AHK, although I appear to be using it in the same way that other people have in other scripts.

Code: Select all

;q:: ;test SHGetNameFromPropertyKey
vName := "System.ItemNameDisplay"
PROPERTYKEY := ""
VarSetCapacity(PROPERTYKEY, 20, 0)
DllCall("propsys\PSGetPropertyKeyFromName", Str,vName, Ptr,&PROPERTYKEY)

DllCall("propsys\PSGetNameFromPropertyKey", Ptr,&PROPERTYKEY, PtrP,vAddrName)
vName := StrGet(vAddrName, "UTF-16")
MsgBox, % vName ;System.ItemNameDisplay
;DllCall("ole32\CoTaskMemFree", Ptr,vAddrName)

DllCall("shell32\SHGetNameFromPropertyKey", Ptr,&PROPERTYKEY, PtrP,vAddrName)
vName := StrGet(vAddrName, "UTF-16")
MsgBox, % vName ;System.ItemNameDisplay
;DllCall("ole32\CoTaskMemFree", Ptr,vAddrName)
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

17 Jun 2017, 19:50

jeeswg wrote:I did expect that 'supported' would literally mean 'supported' rather than 'introduced', the only problem with that though, is presumably all MSDN articles should thus say 'minimum supported client' as Windows Vista at a minimum, but for example this states Windows 2000 as the minimum supported client, unless lots of articles are just 'out-of-date' or unless Windows 2000 is still 'semi-supported' in some way:
I thought the same as you, but given in the inconsistency across the pages wrt. the last supported version, In all honesty I'm more inclined to believing changing the versions are done when there's nothing else to do. For example, the current page for ConvertSidToStringSid says the minimum supported version is XP, but if you go back to the 2011 version, it says Windows 2000. But then at the same time, there's the example you linked.
SHGetNameFromPropertyKey appears to give the same result as PSGetNameFromPropertyKey, giving the canonical name.
Oops. Didn't realise what canonical name meant. Try this:

Code: Select all

VarSetCapacity(IID_IPropertyDescription, 16)
DllCall("ole32\CLSIDFromString", "WStr", "{6f79d558-3e96-4549-a1d1-7d75d2288814}", "Ptr", &IID_IPropertyDescription)

; use PSGetPropertyDescription for the same from an already-initialised PROPERTYKEY
if (!DllCall("propsys\PSGetPropertyDescriptionByName", "WStr", "System.ItemNameDisplay", "Ptr", &IID_IPropertyDescription, "Ptr*", iprop)) { 
	if (!DllCall(NumGet(NumGet(iprop+0)+6*A_PtrSize), "Ptr", iprop, "Ptr*", pszDisplayName)) { ; IPropertyDescription::GetDisplayName
		MsgBox % StrGet(pszDisplayName, "UTF-16")
		DllCall("ole32\CoTaskMemFree", "Ptr", pszDisplayName)
	}
	ObjRelease(iprop)
}
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

18 Jun 2017, 05:16

Though somewhat off-topic:

SHGetNameFromPropertyKey doesn't seem to be included in /exported by the shell32.dll of the latest Win 10 version (ErrorLevel = -4). Can anyone confirm this? Thanks!
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

18 Jun 2017, 09:29

just me wrote:SHGetNameFromPropertyKey doesn't seem to be included in /exported by the shell32.dll of the latest Win 10 version (ErrorLevel = -4). Can anyone confirm this? Thanks!
Yes, I can confirm this. In addition, in the Windows 8.1 and 10.0.15063.0 SDKs, SHGetNameFromPropertyKey isn't present in any header files.

(Also, glad to see you around :-))
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

19 Jun 2017, 03:01

qwerty12 wrote:Yes, I can confirm this.
Weird, I didn't expect to find documented functions on the MSDN which have been removed silently.

(Also, the same to you :))
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

03 Jan 2018, 11:22

- I went back to the script above, 'test SHGetNameFromPropertyKey'.
- PSGetNameFromPropertyKey and CoTaskMemFree were working fine.
- SHGetNameFromPropertyKey and CoTaskMemFree was causing a crash.
- It appears that SHGetNameFromPropertyKey doesn't exist on Windows 7 in shell32.dll (neither in x64 nor x32 versions of the dll). And it appears that it doesn't exist on Windows 10 either, based on posts above.
- I will add CoTaskMemFree, to the library functions that require it, when I produce a slightly improved version of the library. I will also test/finish that function re. column widths on Windows 10, before sharing the updated library.
- (I had thought that perhaps I had misunderstood something about object reference counts, but the problem was a far more simple one, the dll function wasn't found.)

Code: Select all

q:: ;test SHGetNameFromPropertyKey
vName := "System.ItemNameDisplay"
PROPERTYKEY := ""
VarSetCapacity(PROPERTYKEY, 20, 0)
DllCall("propsys\PSGetPropertyKeyFromName", Str,vName, Ptr,&PROPERTYKEY)

;this works:
MsgBox, % DllCall("propsys\PSGetNameFromPropertyKey", Ptr,&PROPERTYKEY, PtrP,vAddrName)
vName := StrGet(vAddrName, "UTF-16")
MsgBox, % vName ;System.ItemNameDisplay
DllCall("ole32\CoTaskMemFree", Ptr,vAddrName)

;this doesn't work:
MsgBox, % DllCall("shell32\SHGetNameFromPropertyKey", Ptr,&PROPERTYKEY, PtrP,vAddrName)
vName := StrGet(vAddrName, "UTF-16")
MsgBox, % vName ;System.ItemNameDisplay
;DllCall("ole32\CoTaskMemFree", Ptr,vAddrName)
return

Code: Select all

;DllListExports() - List of Function exports of a DLL - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=4563

w:: ;list a dll's functions (check if SHGetNameFromPropertyKey is in function list)
vText := StrReplace(DllListExports("shell32"), "`n", "`r`n") "`r`n"
Clipboard := vText
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
joefiesta
Posts: 494
Joined: 24 Jan 2016, 13:54
Location: Pa., USA

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

12 Apr 2018, 08:48

@jeeswg: I like that you have taken the time to share your code, but, I can't figure it out and therefore can't modify to suit my own needs. I am talking about the following, which shows what columns exist, what all the columns are and sorts a column:

Code: Select all

q:: ;Acc - list Explorer column names and widths, click Explorer column header (invoke sort)
ControlGet, hCtl, Hwnd,, DirectUIHWND3, A
oAcc := Acc_Get("Object", "4", 0, "ahk_id " hCtl)
;e.g. header list may be child 1/2/3 depending on how many scrollbars there are
for _, oAcc in Acc_Children(oAcc)
	if (oAcc.accRole(0) = 33) ;header list
		break
vOutput := ""
for _, oChild in Acc_Children(oAcc)
	if IsObject(oChild)
		vOutput .= Acc_Location(oChild).w "`t" oChild.accName(0) "`r`n"

Acc_Children(oAcc).1.accDoDefaultAction(0)
Sleep 500

;doesn't seem to work if do it twice unless 'refresh' it
;e.g. by doing Acc_Get again
oAcc := Acc_Get("Object", "4.1", 0, "ahk_id " hCtl)
Acc_Children(oAcc).1.accDoDefaultAction(0)
Sleep 500
oAcc := oChild := ""

MsgBox, % vOutput
return
Specificially, I have the following questions:

1. You say it "click Explorer column header (invoke sort)". Which column header is clicked?

2. What part of the code actually does the clicking?

3. Can I make it click whichever column I want?

I realize much of your code (especially the ACC stuff) has been copied. The ACC stuff is totally baffling--mostly because it is virtually undocumented. Can you supply some small amouth of what the ACC stuff does? I havent' even been able to figure out what ACC stands for.

thanks
Joe P.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

12 Apr 2018, 14:38

- To sort a column by name, you click on the 'Name' header. In Windows 7, some common column headers are: Name, Size, Item type, Date modified.
- Whenever Acc 'does something', i.e. 'sends a click', these things are usually achieved by using accDoDefaultAction, which is generally equivalent to clicking on a particular GUI element. AFAIK there is not usually a way to programmatically invoke a right-click using Acc.
- Microsoft Active Accessibility. I use Acc to retrieve information about windows, e.g. I manually use AccViewer, or I write a short script to do some of the same things. Sometimes I use Acc to *do* something, via accDoDefaultAction.
Acc library (MSAA) and AccViewer download links - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=26201
AccViewer Basic - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=32039
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
joefiesta
Posts: 494
Joined: 24 Jan 2016, 13:54
Location: Pa., USA

Re: Explorer column interaction (get/set: which appear, width, ascending/descending order)

12 Apr 2018, 21:51

1. I didn't ask how to sort in file explorer. I know how to do that.

I run your code and a column get sorted (it looks likes it's the NAME column, but it's not. The FIRST column is getting sorted and what all depends on how I have arranged columns. I asked what part of your code does the clicking (or, if your code doesn't actually perform a click, what part of your code causes a column to be sorted?)

2. Can your code be modified to click the 2nd or 3rd .... column?

3. I have the acc library, so your code runs. But, I can't not figure it out. One simple example:

Acc_Children(oAcc).1.accDoDefaultAction(0)

I have NO IDEA what the above line of code does. I thought maybe "accdodefaultaction(0)" was a function call. But, no. it's not. Nor can I find "accdodefaultaction" anywhere in the acc library.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: tiska and 162 guests