Drag and Drop to a Powershell script

I thought I had an answer to this, but the more I play with it, the more I see it as a design flaw of Powershell.

I would like to drag and drop (or use the Send-To mechanism) to pass multiple files and/or folders as a array to a Powershell script.

Test Script

#Test.ps1
param ( [string[]] $Paths, [string] $ExampleParameter )
"Paths"
$Paths
"args"
$args

Attempt #1

I created a shortcut with the following command line and dragged some files on to it. The files come across as individual parameters which first match the script parameters positionally, with the remainder being placed in the $args array.

Shortcut for Attempt #1

powershell.exe -noprofile -noexit -file c:\Test.ps1

Wrapper Script

I found that I can do this with a wrapper script...

#TestWrapper.ps1
& .\Test.ps1 -Paths $args

Shortcut for Wrapper Script

powershell.exe -noprofile -noexit -file c:\TestWrapper.ps1

Batch File Wrapper Script

And it works through a batch file wrapper script...

REM TestWrapper.bat
SET args='%1'
:More
SHIFT
IF '%1' == '' GOTO Done
SET args=%args%,'%1'
GOTO More
:Done
Powershell.exe -noprofile -noexit -command "& {c:\test.ps1 %args%}"

Attempted Answers

Keith Hill made the excellent suggestion to use the following shortcut command line, however it did not pass the arguments correctly. Paths with spaces were split apart when they arrived at the Test.ps1 script.

powershell.exe -noprofile -noexit -command "& {c:\test1.ps1 $args}"

Has anyone found a way to do this without the extra script?

Answers


I realize this question is a couple of years old now, but since it's still the top result for a lot of Google queries relating to running PowerShell via an Explorer drag-and-drop I figured I'd post what I came up with today in the hopes it helps others.

I was wanting to be able to drag-and-drop files onto PowerShell (ps1) scripts and couldn't find any good solutions (nothing that didn't involve an extra script or shortcut). I started poking around the file associates in the Registry, and I came up with something that seems to work perfectly.

First we need to add a DropHandler entry for .PS1 files so that Explorer knows PS1 files should accept drag-drop operations. Do that with this Registry change. You'll probably have to create the ShellEx and DropHandler subkeys.

HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\ShellEx\DropHandler\
(Default) = {60254CA5-953B-11CF-8C96-00AA00B8708C}

This drop handler is the one used by Windows Scripting Host. It's my first choice because it supports long filenames. If you run into trouble with spaces or something else, you can try the standard Shell32 executable (.exe, .bat, etc) drop handler: {86C86720-42A0-1069-A2E8-08002B30309D}.

Both of these drop handlers work by simply invoking the default verb (what happens when you double-click a file) for the file type (seen as the (Default) value of the Shell key). In the case of PS1 files, this is Open. By default this verb displays the PowerShell script in Notepad -- not very helpful.

Change this behavior by modifying the Open verb for PS1 files:

HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\Open\Command\
(Default) = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoExit -File "%1" %*

This will run the script in PowerShell when opened as well as pass it all the parameters (dropped files). I have it set to stay open after the script completes, but you can change this by removing the -NoExit option.

That's it. I haven't done any extensive testing, but so far it seems to be working very well. I can drop single files/folders as well as groups. The order of the files in the parameter list isn't always what you'd expect (a quirk of how Explorer orders selected files), but other than that it seems ideal. You can also create a shortcut to a PS1 file in Shell:Sendto, allowing you to pass files using the Send To menu.

Here's both changes in REG file format:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\ShellEx\DropHandler]
@="{60254CA5-953B-11CF-8C96-00AA00B8708C}"

[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\Open\Command]
@="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit -File \"%1\" %*"

Notes:

  • As a result of these changes double-clicking a PS1 file in Explorer will execute the script; to edit instead of open you'll have to use the right-click menu. And just as a suggestion (sadly learned from bitter experience :), you might consider a confirmation/sanity-check guard if you have scripts which take damaging actions.

  • Scripts run via drag-drop actions will have a default starting working directory of C:\Windows\system32\. Another reason to be careful.

  • Remember you will need to change your Execution Policy (Set-ExecutionPolicy) unless you're using signed scripts.

  • If you have adjusted handling of .PS1 files on Windows 10, you need to delete the registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice which will override the default handling.


The easiest way to pass files or folders to a Powershell script is a wrapper script like the following:

@echo off
rem the name of the script is drive path name of the Parameter %0
rem (= the batch file) but with the extension ".ps1"
set PSScript=%~dpn0.ps1
set args=%1
:More
shift
if '%1'=='' goto Done
set args=%args%, %1
goto More
:Done
powershell.exe -NoExit -Command "& '%PSScript%' '%args%'"

All you have to do is

make a copy of the .bat File

give it the same name as the script file but with the extension .bat

For example "hello.ps1" <--> "hello.bat"

Drop the files/folders onto the batch file and it will pass them to the script.

A sample script code may look like this:

"hello world"
$args
$args.Gettype().Fullname

I am not sure why I haven't thought of this sooner...

The secret all this time, since it was made available, has been the parameter attribute "ValueFromRemainingArguments".

Shortcut

powershell.exe -noexit -file c:\Test.ps1

Test.ps1

param (
    [Parameter(ValueFromRemainingArguments=$true)]
    $Path
)

'Paths'
$Path

Change your shortcut to this and try it:

powershell.exe -noprofile -noexit -command "& {c:\test1.ps1 $args}"

I have filed a bug report on the Powershell Microsoft Connect site.


Unfortunately, $args will split your arguments on spaces, thus working incorrectly on filenames containing them. However, you still can process $args inside your .ps1 without an external wrapper script!

Since everything you drag-n-drop on a shortcut (or select for "Send To") will be passed to your script as space-joined list of absolute paths, you can parse $args for all absolute path occurrences using regex and pipe it to foreach:

$pattern = '([a-zA-Z]:\\(((?![<>:"/\\|?*]).)+((?<![ .])\\)?)*)(\s|$)'

Select-String "$pattern" -input "$args" -AllMatches | Foreach {$_.matches} | ForEach-Object { 
    $path = "$($_.Groups.Groups[1].Value)"
    # ...
} 

Where each $path is exactly one absolute path (including those with spaces inside)

The absolute file pattern is taken from here.

I wrapped it in parentheses (to match the first group with the actual path) and added a (\s|$) group at the end, which is "whitespace or end of string", needed because shortcut arguments will be joined with single spaces; $ is required to match the last path occurrence in the argument list.


Need Your Help

Which Perl module would you recommend for sending and getting e-mail?

perl email smtp module imap

I am searching a Perl module to write (SMTP) and read (IMAP) e-mails. Which module out of the bunch would you suggest?

How can I block further input in textarea using maxlength

javascript jquery maxlength

I have a textarea that I want to block input on if the entered characters reaches a max-length.