Beej126 / SingleInstanceAccumulator

Useful for multi-file shell context menu actions
10 stars 2 forks source link

# SingleInstanceAccumulator

Purpose

See typical stack-o's expressing the need, e.g. this one.

The gist is it comes in handy as a bridge between selecting multiple files or folders in custom Windows File Explorer context menus and sending all the selections to various scripts or 3rd party tools.

Usage

"-c:command line" (default: cmd /c echo $files && pause)
  $files will be replace with aggregated list

-f = output each item on separate line to new tempfile
  $files will be replaced by the tempfile path
  quote will default to nothing

-d:delimiter (default: ,)
-q:quote character around each accumulated argument (default: ") - see examples below
-t:timeout millisecs (default: 200)
-w = hidden launch (hides console window for cmd.exe & powershell.exe)
-v = debug output


Command Line Examples

Heads up about registry entries:

examples are all formatted to be executed from WITHIN A BATCH FILE

example 0 - feed files to a 3rd party exe

reg add "HKEY_CURRENT_USER\Software\Classes\*\shell\MyCommand\command" /f /ve /t REG_EXPAND_SZ /d "\"^%%bin^%%\SingleInstanceAccumulator\" -w \"-c:myprogram.exe $files\" \"%%1\""

example 1 - put selected files onto clipboard

reg add "HKEY_CURRENT_USER\Software\Classes\*\shell\Path2Clip\command" /f /ve /t REG_EXPAND_SZ /d "\"^%%bin^%%\SingleInstanceAccumulator\" -w -d:\" ^^^& echo \" \"-c:cmd /c (echo $files) ^^^| clip\" \"%%1\""

when run from windows explorer context menu the above reg entry will execute a command line that looks like this:

"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy bypass C:\bin\transcode.ps1 -listFilePath 'C:\Users\Beej\AppData\Local\Temp\tmp5EEF.tmp'

example 2 - PowerShell script - using temp file approach

reg add "HKEY_CURRENT_USER\Software\Classes\*\shell\MyCommand\command" /f /ve /t REG_EXPAND_SZ /d "\"^%%bin^%%\SingleInstanceAccumulator\" -w -f \"-c:powershell -ExecutionPolicy bypass \"^%%bin^%%\test.ps1 -listFilePath '$files'\" \"%%1\""

note: -listFilePath command line argument corresponds to test.ps1's $listFilePath param shown next below

corresponding test.ps1 sample:

param(
  [String]$listFilePath
)

gc $listFilePath | % { $_ }

# erase $listFilePath # you probably want this in your final script as good cleanup, commenting out for debug
pause


side note if interested, i use this format to drive a transcode.ps1 script which leverages handbrake.exe command line to bulk process .mov files (from point and shoot cameras) into mp4's.
customizing windows explorer with context menus like this starts to turn it into an efficient workflow space... further on that note, i like to use a left-right double explorer tool
here is another example, setting up zip files with an auto-extract script: https://beej126.github.io/windows-file-explorer-auto-extract-zips-like-mac-finder/

example 3 - files list passed directly to powershell script on command line as a string array

reg add "HKEY_CURRENT_USER\Software\Classes\*\shell\MyCommand\command" /f /ve /t REG_EXPAND_SZ /d "\"^%%bin^%%\SingleInstanceAccumulator\" -w -q:' \"-c:powershell -ExecutionPolicy bypass ^%%bin^%%\test.ps1 -filesArray $files\" \"%%1\""

will execute like this:

powershell test.ps1 -filesArray 'filepath1', 'filepath2', 'filepath3'

corresponding test.ps1 sample:

param(
  [String[]]$filesArray
)

$filesArray | % { $_ }

pause

General Tips

  1. use the -v flag to see what will be executed so you can see how all the quoting and escaping works and fine tune from there
  2. By default Explorer will only show context menu's when less than 15 files are selected - https://www.tenforums.com/tutorials/94513-fix-context-menu-items-missing-when-more-than-15-selected-windows.html ... here's the reg setting to bump it to 100: reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer" /f /v MultipleInvokePromptMinimum /t REG_DWORD /d 100
  3. you should probably only interactively test manual SingleInstanceAccumulator.exe calls via cmd.exe (i.e. not at a powershell command line)... if you must test at powershell cli, you then also have to escape the single tick after -q (e.g. -q:`') because otherwise your interactive powershell cli will get caught on the single tick as an open string... remember these SingleInstanceAccumulator command lines are primarily intended to be fired from shell > command registry entries and that initial command line execution context is not powershell, it is traditional cmd.exe syntax rules, so the -q:' is fine there
  4. can be super helpful to use "-NoExit" arg on powershell.exe command lines so those windows stay open to see errors
  5. powershell treats spaces as separate args on the command line, so if you're passing any args with spaces you'll need to escape them with back ticks (i.e. the ` character)
    1. one tricky place this can happen is if you're using a variable that winds up with spaces in it, e.g. a variable that contains the path to a command
    2. along those lines, i've found it convenient to create "setup.cmd" batch file scripts that populate my desired shell\blah\command registry entries ... so here's some helpful code in traditional batch syntax:
      :: get path where this script is started from
      set cwd=%~dp0
      ::replace spaces with powershell escapes
      set cwd=%cwd: =` %
    3. -OR- instead of escaping spacing you can surround powershell arguments with double quotes which requires nested escaping for the quotes to pass through to that execution context... example here in another repo of mine
  6. I haven't generally needed this yet but certain explorer shell verbs may not accept multiple arguments by default and here's the fix:
    :: crucial multi-file handling property - https://learn.microsoft.com/en-us/windows/win32/shell/context-menu-handlers?redirectedfrom=MSDN#employing-the-verb-selection-model
    reg add "HKEY_CLASSES_ROOT\FileType\shell\YourNewContextMenu" /f /v "MultiSelectModel" /d "Player"
  7. in this general file explorer customization space you might also want to start creating new file extensions that have their own icon and associated shell commands... the easiest way i've come to manage those are via commands built into cmd.exe, here's a quick example:
    assoc .bth=ElevatorHiddenBatch
    ftype ElevatorHiddenBatch=c:\bin\elevator.exe -hide -c \"%1\"

    (fyi, Elevator.exe is another tool i've cobbled together to provide some more handy stuff in this space =)