get/set File Explorer column width (IColumnManager interface)

Post your working scripts, libraries and tools.
neogna2
Posts: 586
Joined: 15 Sep 2016, 15:44

get/set File Explorer column width (IColumnManager interface)

Post by neogna2 » 17 Nov 2022, 16:51

I converted parts from jeeswg's v1 Explorer column interaction code to get/set column widths in v2.
Some oneliners are split for clarity and the "JEE_" prefix was removed from function names.
Please check the code and let me know any mistakes or what could be improved. Tested in Windows 10.

Code: Select all

#Requires AutoHotkey v2.0-beta.14
;example script
;open File Explorer, run the script and press Tab
#HotIf WinActive("ahk_class CabinetWClass")
Tab::
{
    oWin := ExpWinGetObj( WinExist("A") )
    ExpGetInterfaces(oWin, &isp, &isb, &isv, &ifv2, &icm)
    ;get current width for column "Name"
    MsgBox "width: " ICMGetColumnWidth(icm, "System.ItemNameDisplay")
    ;change width to 200 px
    ICMSetColumnWidth(icm, "System.ItemNameDisplay", 200)
    ;check changed width
    MsgBox "width: " ICMGetColumnWidth(icm, "System.ItemNameDisplay")
    ExitApp
}
#HotIf

ExpWinGetObj(hWnd)
{
    for oWin in ComObject("Shell.Application").Windows
        if oWin.HWND = hWnd
            return oWin
}

; isp   = IServiceProvider
; isb   = IShellBrowser
; isv   = IShellView
; ifv2  = IFolderView2
; icm   = IColumnManager
; https://learn.microsoft.com/en-us/windows/win32/api/servprov/nn-servprov-iserviceprovider
; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ishellbrowser
; shobjidl_core.h
; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellbrowser-queryactiveshellview
; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifolderview2
; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-icolumnmanager
ExpGetInterfaces(oWin, &isp?, &isb?, &isv?, &ifv2?, &icm?)
{
    isp := ComObjQuery(oWin, "{6d5140c1-7436-11ce-8034-00aa006009fa}")
    isb := ComObjQuery(isp, "{4C96BE40-915C-11CF-99D3-00AA004AE837}"
                          , "{000214E2-0000-0000-C000-000000000046}")
    ;QueryActiveShellView (15 in isb vtable)
    qasv := NumGet(NumGet(isb.Ptr, "UPtr") + 15*A_PtrSize, "UPtr")
    HResult := DllCall(qasv, "Ptr" , isb.Ptr, "PtrP", &isv := 0, "Ptr")
    if HResult < 0  ;S_OK (0) if successful otherwise COM-defined error value
        return
    ifv2 := ComObjQuery(isv, "{1af3a467-214f-4298-908e-06b03e0b39f9}")
    icm := ComObjQuery(ifv2, "{d8ec27bb-3f3b-4042-b10a-4acfd924d453}")
}

; IColumnManager interface (Windows)
; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-icolumnmanager
; shobjidl_core.h
; https://learn.microsoft.com/en-us/windows/win32/api/propsys/nf-propsys-psgetpropertykeyfromname
; https://learn.microsoft.com/en-us/windows/win32/api/wtypes/ns-wtypes-propertykey
; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icolumnmanager-getcolumninfo
; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ns-shobjidl_core-cm_columninfo
ICMGetColumnWidth(icm, ColName, &IdealWidth := "")
{
    PROPERTYKEY := Buffer(20, 0)
    DllCall("propsys\PSGetPropertyKeyFromName", "WStr", ColName, "Ptr", PROPERTYKEY)
    CM_COLUMNINFO := Buffer(184, 0)
    NumPut("UInt", 184, CM_COLUMNINFO, 0) ;cbSize
    ;CM_MASK_WIDTH := 0x1 ;CM_MASK_IDEALWIDTH := 0x4
    NumPut("UInt", 0x5, CM_COLUMNINFO, 4) ;dwMask
    ;GetColumnInfo (4 in icm vtable)
    gci := NumGet(NumGet(icm.Ptr, "UPtr") + 4*A_PtrSize, "UPtr")
    DllCall(gci, "Ptr", icm.Ptr, "Ptr", PROPERTYKEY, "Ptr", CM_COLUMNINFO)
    IdealWidth := NumGet(CM_COLUMNINFO, 20, "UInt") ;uIdealWidth
    return NumGet(CM_COLUMNINFO, 12, "UInt") ;uWidth
}

; https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icolumnmanager-setcolumninfo
ICMSetColumnWidth(icm, ColName, Width)
{
    PROPERTYKEY := Buffer(20, 0)
    DllCall("propsys\PSGetPropertyKeyFromName", "WStr", ColName, "Ptr", PROPERTYKEY)
    CM_COLUMNINFO := Buffer(184, 0)
    NumPut("UInt", 184, CM_COLUMNINFO, 0) ;cbSize
    ;CM_MASK_WIDTH := 0x1
    NumPut("UInt", 0x1, CM_COLUMNINFO, 4) ;dwMask
    NumPut("UInt", Width, CM_COLUMNINFO, 12)
    ;SetColumnInfo (3 in icm vtable)
    sci := NumGet(NumGet(icm.Ptr, "UPtr") + 3*A_PtrSize, "UPtr")
    DllCall(sci, "Ptr", icm.Ptr, "Ptr", PROPERTYKEY, "Ptr", CM_COLUMNINFO)
}

ntepa
Posts: 406
Joined: 19 Oct 2022, 20:52

Re: get/set File Explorer column width (IColumnManager interface)

Post by ntepa » 28 Nov 2022, 22:15

Try using ComCall instead.

Code: Select all

; QueryActiveShellView (15 in isb vtable)
; qasv := NumGet(NumGet(isb.Ptr, "UPtr") + 15*A_PtrSize, "UPtr")
; HResult := DllCall(qasv, "Ptr" , isb.Ptr, "PtrP", &isv := 0, "Ptr")
HResult := ComCall(15, isb, "PtrP", &isv := 0, "Ptr")

; gci := NumGet(NumGet(icm.Ptr, "UPtr") + 4*A_PtrSize, "UPtr")
; DllCall(gci, "Ptr", icm.Ptr, "Ptr", PROPERTYKEY, "Ptr", CM_COLUMNINFO)
ComCall(4, icm, "Ptr", PROPERTYKEY, "Ptr", CM_COLUMNINFO)

; sci := NumGet(NumGet(icm.Ptr, "UPtr") + 3*A_PtrSize, "UPtr")
; DllCall(sci, "Ptr", icm.Ptr, "Ptr", PROPERTYKEY, "Ptr", CM_COLUMNINFO)
ComCall(3, icm, "Ptr", PROPERTYKEY, "Ptr", CM_COLUMNINFO)

neogna2
Posts: 586
Joined: 15 Sep 2016, 15:44

Re: get/set File Explorer column width (IColumnManager interface)

Post by neogna2 » 01 Dec 2022, 15:31

ntepa wrote:
28 Nov 2022, 22:15
Try using ComCall instead.
Thank you :thumbup: I have a lot to learn about COM. I will update the code when I convert some more of jeeswg's IColumnManager code.

A question for any reader who knows. The ComCall documentation says Calls a native COM interface method by index. I wonder about the specifier "native" there. What's an example of a non-native COM interface that ComCall cannot handle?

Post Reply

Return to “Scripts and Functions (v2)”