SHMultiFileProperties

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

SHMultiFileProperties

12 Apr 2017, 15:39

Hello!

Pondering how to get the summarize total size property box for multi-selected files via the Run, properties command.
It does work for the ScriptDir if using Null as target file. Trying to get the same property box for multiple target files fails each time. Should be possible to do this, but how?

Code: Select all

Run, properties "" ; Gives a summarize total size property box for the ScriptDir 
Run, properties "G:\Burn.mp3"|"G:\Chuck.mp3"
Last edited by zcooler on 14 Apr 2017, 04:45, edited 1 time in total.
User avatar
JoeWinograd
Posts: 2165
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: summarize total size property box

12 Apr 2017, 16:04

According to the Run documentation, it does it only for "the indicated file". There's no mention of wildcarding or multiple files. But it works fine if you do one at a time, such as:

Code: Select all

Run, properties "G:\Burn.mp3"
Run, properties "G:\Chuck.mp3"
The Properties dialogs will be on top of each other, but they're there — just move 'em! Regards, Joe
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: summarize total size property box

12 Apr 2017, 16:18

Yeah, im forced to do it like that today. Its meaningless running and manually closing +20 Properties dialogs when its the combined size Im after. This AHK properties implementation is limited to say the least. Is it possible to do it via dll call?
User avatar
JoeWinograd
Posts: 2165
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: summarize total size property box

12 Apr 2017, 16:30

its the combined size Im after
If your goal is to get the combined size of files, there are much better ways to do it — you certainly don't want to use Properties dialog boxes for that. One simple way is to use the FileGetSize command. Regards, Joe
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: summarize total size property box

12 Apr 2017, 16:38

My goal is to use the explorer properties dialog box only. So I wanted to know if anyone knew about a solution to my problem before seeking it elsewhere.

Regards
zcooler
User avatar
JoeWinograd
Posts: 2165
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: summarize total size property box

12 Apr 2017, 17:10

If you don't want to use the FileGetSize command, you may like the COM calls ("Shell.Application") in kon's excellent FileGetProperties. But I don't know how to do what you want by utilizing multiple Explorer Properties dialog boxes — and definitely don't understand why you want to do it that way. Perhaps someone else will jump in. Regards, Joe
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: summarize total size property box

13 Apr 2017, 08:16

JoeWinograd wrote:But I don't know how to do what you want by utilizing multiple Explorer Properties dialog boxes — and definitely don't understand why you want to do it that way.
Well, Im simply trying to copy the shell/explorer behaviour for a shell/explorer similar application section and that includes "display a merged property sheet for a set of files (or folders) (if multi-selected). Property values common to all the files are shown while those that differ display the string (multiple values)". There is an AHK request for this kind of feature since 2008, but no luck so far. I did found a solution, but its in C#. Any takers who might be interested in converting it to AHK? If the solution is good it might end up with improving/completing the AHK Run, properties command :)

More info here:
https://stackoverflow.com/questions/356 ... in-c-sharp

Code: Select all

public class Properties
{
    #region Import Methods

    [DllImport("shell32.dll", SetLastError = true)]
    static extern int SHMultiFileProperties(IDataObject pdtobj, int flags);

    [DllImport("shell32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr ILCreateFromPath(string path);

    [DllImport("shell32.dll", CharSet = CharSet.None)]
    public static extern void ILFree(IntPtr pidl);

    [DllImport("shell32.dll", CharSet = CharSet.None)]
    public static extern int ILGetSize(IntPtr pidl);

    #endregion

    #region Static Methods

    #region Private

    private static MemoryStream CreateShellIDList(StringCollection filenames)
    {
        // first convert all files into pidls list
        int pos = 0;
        byte[][] pidls = new byte[filenames.Count][];
        foreach (var filename in filenames)
        {
            // Get pidl based on name
            IntPtr pidl = ILCreateFromPath(filename);
            int pidlSize = ILGetSize(pidl);
            // Copy over to our managed array
            pidls[pos] = new byte[pidlSize];
            Marshal.Copy(pidl, pidls[pos++], 0, pidlSize);
            ILFree(pidl);
        }

        // Determine where in CIDL we will start pumping PIDLs
        int pidlOffset = 4 * (filenames.Count + 2);
        // Start the CIDL stream
        var memStream = new MemoryStream();
        var sw = new BinaryWriter(memStream);
        // Initialize CIDL witha count of files
        sw.Write(filenames.Count);
        // Calcualte and write relative offsets of every pidl starting with root
        sw.Write(pidlOffset);
        pidlOffset += 4; // root is 4 bytes
        foreach (var pidl in pidls)
        {
            sw.Write(pidlOffset);
            pidlOffset += pidl.Length;
        }

        // Write the root pidl (0) followed by all pidls
        sw.Write(0);
        foreach (var pidl in pidls) sw.Write(pidl);
        // stream now contains the CIDL
        return memStream;
    }

    #endregion

    #region Public 

    public static int Show(IEnumerable<string> Filenames)
    {
        StringCollection Files = new StringCollection();
        foreach (string s in Filenames) Files.Add(s);
        var data = new DataObject();
        data.SetFileDropList(Files);
        data.SetData("Preferred DropEffect", new MemoryStream(new byte[] { 5, 0, 0, 0 }), true);
        data.SetData("Shell IDList Array", Properties.CreateShellIDList(Files), true);
        return SHMultiFileProperties(data, 0);
    }

    public static int Show(params string[] Filenames)
    {
        return Properties.Show(Filenames as IEnumerable<string>);
    }

    #endregion

    #endregion
}
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: summarize total size property box

28 Apr 2017, 12:25

Hello,
zcooler wrote:I did found a solution, but its in C#.
Here's a conversion of the code found on http://stackoverflow.com/a/34551988:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

MultiFileProperties(filenames*) {
	static IID_IDataObject
	if (!VarSetCapacity(IID_IDataObject))
		VarSetCapacity(IID_IDataObject, 16), DllCall("ole32\CLSIDFromString", "WStr", "{0000010e-0000-0000-C000-000000000046}", "Ptr", &IID_IDataObject)

	ret := False
	if (!filenames.MaxIndex())
		return ret

    DllCall("shell32\SHGetDesktopFolder", "Ptr*", pDesktop)
    if (!pDesktop)
		return ret

	VarSetCapacity(pidl_list, filenames.MaxIndex() * A_PtrSize)
	filenameCount := 0, ParseDisplayName := NumGet(NumGet(pDesktop+0)+3*A_PtrSize)
	for _, filename in filenames
		filenameCount += DllCall(ParseDisplayName, "Ptr", pDesktop, "Ptr", 0, Ptr, 0, "WStr", filename, "Ptr", 0, "Ptr", &pidl_list+(filenameCount * A_PtrSize), "Ptr", 0) == 0

	if (filenameCount && DllCall(NumGet(NumGet(pDesktop+0)+10*A_PtrSize), "Ptr", pDesktop, "Ptr", 0, "UInt", filenameCount, "Ptr", &pidl_list, "Ptr", &IID_IDataObject, "Ptr", 0, "Ptr*", pDataObject) == 0) { ; GetUIObjectOf
		ret := DllCall("shell32\SHMultiFileProperties", "Ptr", pDataObject, "UInt", 0) == 0
		ObjRelease(pDataObject)
	}

	loop %filenameCount% {
		if ((pidl := NumGet(pidl_list, (A_Index - 1) * A_PtrSize, "Ptr")))
			DllCall("ole32\CoTaskMemFree", "Ptr", pidl)
	}
	ObjRelease(pDesktop)
	
	return ret
}

if (MultiFileProperties(A_ProgramFiles . "\Internet Explorer\iexplore.exe", A_WinDir . "\Explorer.exe", A_ScriptFullPath))
	MsgBox
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: SHMultiFileProperties

28 Apr 2017, 12:55

GOD DAMIT..YOU ARE GOOD :shock:
This fine piece of coding finalizes the Run, properties command. Hope lexikos is watching :D
As the Americans say: You're the Man...QWERTY12 :mrgreen:
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: SHMultiFileProperties

28 Apr 2017, 13:36

@qwerty12
Do you think it might be possible to call it without the msgbox (and avoid the error) eventually?

Code: Select all

msgbox % MultiFileProperties("F:\ErrorlogSC.txt","F:\Edited Recordings\20170425_20-59-02_TV6_Man On A Ledge - ★★★★★★★☆☆☆.ts")
Tried to work out why the msgbox prevents it from throwing the error on the stackoverflow site, but this stuff is so over my head :oops:
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: SHMultiFileProperties

28 Apr 2017, 14:00

Hey zcooler,
zcooler wrote:Do you think it might be possible to call it without the msgbox (and avoid the error) eventually?
SHMultiFileProperties apparently does its work out-of-process; if you use Process Hacker/Explorer on the properties window, you'll see that the window displayed belongs to dllhost.exe and whatever COM component it's hosting as opposed to AutoHotkey. To be honest, I'm not sure why it errors out if AutoHotkey exits before the properties are displayed given that the script (is supposed to) free(s) everything right after SHMultiFileProperties is called.
The Run page lists a workaround for this using WinWait instead of the dirty MsgBox. I don't know know if it's relevant, but if you plan on using MultiFileProperties in a script that is #Persistent (and/or maybe showing a GUI of its own anyway) then you don't need to workaround anything...
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: SHMultiFileProperties

28 Apr 2017, 14:09

I thought this wasn't working, but then it was just that I was closing the MsgBox too quickly.

This shows a Properties dialog that closes when you close the MsgBox (if the script is not #Persistent). Tested on Windows 7.

One query is if it's possible to specify the (x,y) coordinates for the dialog.

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

@zcooler
You can use #Persistent and ExitApp. I'd be interested in any other methods that make a script 'temporarily persistent'
[EDIT:] I found a way to achieve 'temporary persistence':
GUIs via DllCall: MsgBox - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 14#p163414

@qwerty12. Nicely done. Thanks so much for this.

Btw have you looked into shell extensions much?

[I was thinking I might read over this this weekend as it happens:]
The Complete Idiot's Guide to Writing Shell Extensions - Part I - CodeProject
https://www.codeproject.com/Articles/44 ... ell-Extens

[And I mention some other AutoHotkey requests re. shell extensions here:]
DllCall - Assign icon for “Copy/Cut/Paste/Delete” Windows default context menu items - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=30377

I was mainly interested in applying an action to multiple selected files in Explorer, via the right-click context menu e.g. '7-Zip, Add to archive...'.

Cheers.

[EDIT:] The guide to shell extensions mentions getting a list from an hDrop. My JEE_DropGetPaths function might be useful for that.
GUI COMMANDS: COMPLETE RETHINK - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 42#p144342

[EDIT:] Might be interesting:
ahk-libs/JumpList at master · rshipp/ahk-libs · GitHub
https://github.com/rshipp/ahk-libs/tree/master/JumpList
JumpList library for Windows 7 - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/7648 ... windows-7/
Last edited by jeeswg on 28 Aug 2017, 16:04, 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
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: SHMultiFileProperties

28 Apr 2017, 15:00

qwerty12 wrote:Hey zcooler,
zcooler wrote:Do you think it might be possible to call it without the msgbox (and avoid the error) eventually?
SHMultiFileProperties apparently does its work out-of-process; if you use Process Hacker/Explorer on the properties window, you'll see that the window displayed belongs to dllhost.exe and whatever COM component it's hosting as opposed to AutoHotkey. To be honest, I'm not sure why it errors out if AutoHotkey exits before the properties are displayed given that the script (is supposed to) free(s) everything right after SHMultiFileProperties is called.
The Run page lists a workaround for this using WinWait instead of the dirty MsgBox. I don't know know if it's relevant, but if you plan on using MultiFileProperties in a script that is #Persistent (and/or maybe showing a GUI of its own anyway) then you don't need to workaround anything...
Hey qwerty12,
Heck yes, its going to be in a GUI, so that problem averted :) It works inside the GUI :dance: However passing the concatenated string via a variable does not :( No multi properties dialog showing, no error or anything.

Code: Select all

Properties: ; <<<<<Step3.8 - changed
Gui, Gui2:Default
GuiControl, -g, TVVar
For Each, ItemID In TVSel.Selection
  ;Run, % "properties " . TVCache[ItemID, "Path"], , UseErrorLevel
  GetFilesProperties .= (A_Index > 1 ? "," : "") . """" TVCache[ItemID, "Path"] """"
;msgbox % GetFilesProperties
MultiFileProperties(GetFilesProperties) ; Works not- same string as in call below
;MultiFileProperties("F:\TV\Seen Recordings\20170427_11-30-14_TV8_Knife Fight - S03E04.ts","F:\TV\Seen Recordings\22waaaaa.log") ; This works- same string as in call above
GuiControl, +gTVLabel, TVVar
Return
Last edited by zcooler on 28 Apr 2017, 15:07, edited 1 time in total.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: SHMultiFileProperties

28 Apr 2017, 15:05

If you change
MultiFileProperties(filenames*) {
to
MultiFileProperties(filenames) {
I.e. remove the *, then you can just pass an array, e.g.:

Code: Select all

q::
filenames := []
filenames.1 := A_ProgramFiles . "\Internet Explorer\iexplore.exe"
filenames.2 := A_WinDir . "\Explorer.exe"
filenames.3 := A_ScriptFullPath
if MultiFileProperties2(filenames)
	MsgBox
return
==================================================

[EDIT:] Based on what qwerty12 said below. Actually what you can do instead is keep the function as it is, but add an * after the array name, when you use it as a parameter, e.g.:

Code: Select all

q::
filenames := []
filenames.1 := A_ProgramFiles . "\Internet Explorer\iexplore.exe"
filenames.2 := A_WinDir . "\Explorer.exe"
filenames.3 := A_ScriptFullPath
if MultiFileProperties(filenames*)
	MsgBox
return
Last edited by jeeswg on 28 Apr 2017, 15:24, 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
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: SHMultiFileProperties

28 Apr 2017, 15:16

jeeswg wrote:One query is if it's possible to specify the (x,y) coordinates for the dialog.
Not until after it's displayed, AFAICT sorry. SHMultiFileProperties doesn't seem to offer much in the way of choice...
Btw have you looked into shell extensions much?
Not really, the standard way of writing them seems to be with C++ and ATL. I can't write C++ code to save my life and, honestly, committing seppuku seems like the more pleasing option to me. There's Delphi, too, but I don't know Pascal. C# etc. is an option but even if Raymond Chen didn't advise against it, I despise .Net background processes anyway (a holdover from having a computer for many, many years with 128 MB RAM when the .Net framework was getting popular :)).
I did find a rather good tutorial on writing a simple shell extension in C on a Windows-totally-not-a-warez-forum but I just can't think of any reason for me to even try (and probably fail).
[And I mention some other AutoHotkey requests re. shell extensions here:]
DllCall - Assign icon for “Copy/Cut/Paste/Delete” Windows default context menu items - AutoHotkey Community
Oh, IMHO, a waste of time trying that in AutoHotkey. As you note, you need to load the images in the context of the Explorer process (which, as far as I can tell, is not trivial) and even then, that's a no go for the open/save file dialogs displayed by other processes, unless you then do the same there too. And then find a good way to free the image handles in each process when done. If you write a shell extension, then the responsible component of Windows will load the extension DLL into compatible processes for you at the right time, which honestly seems easier to me.
I was mainly interested in applying an action to multiple selected files in Explorer, via the right-click context menu e.g. '7-Zip, Add to archive...'.
You probably don't need a shell extension for that. If you write a script and add it to the context menu (I think there's steps in the thread you linked to), your script will get the filenames and then from there, you can do something like (I'm not knowledgable on COM, especially shell stuff, so this might all be rubbish) create an IContextMenu representing the multiple files and from there you can invoke whatever verb on them. Mind, I don't know if that will actually work, given 7-Zip's a shell extension itself - I don't know how well it lends itself to being progmatically manipulated. I don't really know how to do it, but find Deo's Unicode x64 port of the ShellContextMenu AutoHotkey library and read Raymond Chen's posts on IContextMenu and you might be able to whip something up.
zcooler wrote:Heck yes, its going to be in a GUI, so that problem averted :) It works inside the GUI :dance:
Good to hear :)
No multi properties dialog showing, no error or anything.
Hmm, MultiFileProperties is a varadic function so you can actually pass it an "exploded" array as described in Variadic Function Calls. I don't know enough about the AutoHotkey language to tell you if there's an easier way than something like this:

Code: Select all

GetFilesProperties := []
For Each, ItemID In TVSel.Selection
	GetFilesProperties.Push(TVCache[ItemID, "Path"])
MultiFileProperties(GetFilesProperties*)
(untested, but unless I messed it up, I hope the gist is clear)
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: SHMultiFileProperties

28 Apr 2017, 15:33

qwerty12 wrote:Hmm, MultiFileProperties is a varadic function so you can actually pass it an "exploded" array as described in Variadic Function Calls. I don't know enough about the AutoHotkey language to tell you if there's an easier way than something like this:

Code: Select all

GetFilesProperties := []
For Each, ItemID In TVSel.Selection
	GetFilesProperties.Push(TVCache[ItemID, "Path"])
MultiFileProperties(GetFilesProperties*)
(untested, but unless I messed it up, I hope the gist is clear)
OHHH YEAHH, that works :mrgreen: Should have figured out that one myself :facepalm: I guess its too many beers already ;)
Many thanks for a great job done, qwerty12 :beer:
Its fantastic!

Best regards
zcooler
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: SHMultiFileProperties

28 Aug 2017, 16:00

Re. shell extensions:
You can add items to 'Send to' in the Explorer context menu, to in effect achieve shell extensions.
Explorer context menu shell extensions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=32152

Re. the properties dialog for multiple files:
This example waits for the dialog to be shown and closed, without using a MsgBox.

Code: Select all

vPath1 := A_ProgramFiles "\Internet Explorer\iexplore.exe"
vPath2 := A_WinDir "\Explorer.exe"
vPath3 := A_ScriptFullPath
MultiFileProperties(vPath1, vPath2, vPath3)
WinWait,, % "ahk_class #32770 ahk_exe " A_AhkPath
WinWaitClose, % "ahk_class #32770 ahk_exe " A_AhkPath
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: morkovka18 and 207 guests