0

I created Windows context menu item to execute a powershell command. However, I can't figure out how to pass %1 to my Powershell script with escaped quotes.

Current registry entry:

[HKEY_CLASSES_ROOT\*\shell\Share file on FTP\Command]
@="cmd.exe /C nircmd elevate PowerShell -NoProfile -NoLogo -Command \"& {(dir \"%1\" | Copy-ToFTP -Destination Public.Screenshots).source | clip}\""

Produces the following error when I select a file via the "Share file on FTP" context menu item:

dir : Cannot find path 'D:\Multimedia' because it does not exist.
At line:1 char:5
+ & {(dir D:\Multimedia Files\Pictures\HTPC\20150328_220120.jpg | Copy-ToOnedrive  ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (D:\Multimedia:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

BONUS I'm currently using a 3rd party tool, Nircmd, to elevate the Powershell Prompt (since my powershell command needs it). If possible, I'd like to use native Windows to do this instead (keeping my command code to a single line, if possible).... NOT afterwards, by creating a Powershell PS1 script that elevates itself.

EDIT: Below line of code "should" work.. however, instead of getting a a shortname path, I get an unexpected incorrect result:

I'm copying and pasting the registry key value to make it easier to read (instead of .reg file content)

cmd.exe /c for %%A in (%1) do @set I=%%~sA | PowerShell.exe -NoExit -NoProfile -NoExit -NoLogo -Command "& {(dir %I% | Copy-ToFTP -Destination Public.Screenshots).source | clip}"

Ultimately produces this output in Powershell:

dir : Cannot find path 'D:\Multimedia Files\Pictures\HTPC\:433310880:4624'
because it does not exist.
Stephan
  • 53,940
  • 10
  • 58
  • 91
MKANET
  • 573
  • 6
  • 27
  • 51
  • I think what's really screwing me up is that ***%~s1*** doesn't work under Windows 8.1 command-interpreter, cmd. I've used this shortening technique this dozens of times under Win7 and earlier. Hence, even the most simple batch file with only ***echo %~s1*** returns the full path without any shortening or errors. – MKANET Aug 03 '15 at 21:27
  • Im guessing the complexity of syntax is going to be pretty difficult; or, I'd probably have seen working answers by now – MKANET Aug 03 '15 at 21:29

2 Answers2

1

I finally figured out the problem. Windows native command interpreter, CMD, dumps %1 to Powershell as a raw scriptblock. The Powershell cmdlets I tested with can't automatically cast scriptblock type to a string type (when passed to it. What made it difficult to debug is that "%1" (with quotes around it) DID preserve the full name path; which looks IDENTICAL to a normal string with a full name directory path such as:

%1 = "D:\Multimedia Files\Pictures\HTPC\20150328_220120.jpg"

This can fool most people into presuming this is a string. It's actually treated as a scriptblock!

Below is my solution to my question. Also, it looks like I didn't need to elevate Powershell afterall for this to work. Hopefully, someone in the future may find this post helpful; saving them lots of time.

ShareImageOneDrive.reg

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\shell\Share Image on OneDrive\Command]
@="cmd.exe /c PowerShell.exe -NoProfile -WindowStyle Hidden -NoLogo -Command \"& {param ($I = {%1}) ; (Copy-ToOnedrive -Path $I.ToString() -Destination Public).source | clip}\""

Registry key value (easier to read code)

cmd.exe /c PowerShell.exe -NoProfile -WindowStyle Hidden -NoLogo -Command "& {param ($I = {%1}) ; (Copy-ToOnedrive -Path $I.ToString() -Destination Public).source | clip}"
MKANET
  • 573
  • 6
  • 27
  • 51
  • Could you be a little more clear about the most important part of the suggestions? All I had to do to use your advice was enclose the %1 in curly braces. At any rate, thx for the advice, it did save me a lot of time and headaches! – Kyle Dev-ious Aug 17 '20 at 16:50
0

How I test these is first I create a working batch file. Being sure to escape all the powershell to work properly when double clicking a batch file. Then once I can get the batch file to double click correctly, then I finally copy that into the registry key via the GUI, so that the GUI automatically escapes the double quotes correctly, which occurs when you export the reg settings.

Meaning I don't test directly from the registry because there are multiple layers of escapes at work here.

As a batch file, the below command runs fine when I double click it (assuming I have a folder path saved to my clipboard). It should be all on one line, but for some reason it's being split.

start /wait /b powershell -command "Clear-Host;$CLIPBOARD = PowerShell -NoProfile -STA -Command {[reflection.assembly]::loadwithpartialname('PresentationCore') | Out-Null;[Windows.Clipboard]::GetText()};C:\Windows\Explorer.exe $CLIPBOARD" How I get my registry compliant settings, I simply add cmd /c to the front of the working batch script, and paste directly into the registry key, followed by an export to .reg file. Reason being that regedit.exe has a built in function to escape or double // everything correctly and as long as it's working from the batch file, you can wrap it with 'cmd /c " and it will run. I prefer the start /wait command so it stays live long enough for the types of commands I run, but it might not be necessary in all cases. Here is what I get after exporting to .reg file.

(Found the source of my example)

https://superuser.com/a/705188/281412

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\Open_in_Explorer] "MUIVerb"="Open_in_Explorer_By_KNUCKLE_DRAGGER" "icon"="Explorer.exe" "Position"="Top" "Extended"=""

[HKEY_CLASSES_ROOT\Directory\Background\shell\Open_in_Explorer\command] @="cmd /c start /wait /b powershell -command \"Clear-Host;$CLIPBOARD = PowerShell -NoProfile -STA -Command {[reflection.assembly]::loadwithpartialname('PresentationCore') | Out-Null;[Windows.Clipboard]::GetText()};C:\Windows\Explorer.exe $CLIPBOARD\""

For larger scripts you will want to go base64 encoded command which will avoid 100% of these issues (base64 character set requires no escaping) and allows for multi-line scripts to run from a single row.

https://stackoverflow.com/a/24997890/3093031

Few examples of self-elevation scripts here.

Determine if script is running hidden

For some reason I have no buttons in my gui, cannot add code tags. Someone will have to edit them in.

Are you 100% sure you're not supposed to be calling nircmdc.exe, the command line version of nircmd ???

Community
  • 1
  • 1
Knuckle-Dragger
  • 6,644
  • 4
  • 26
  • 41
  • I need help with my syntax; or at least a working example closer to what I'm trying to do. All my tests are done in batch files first... Including the use of %~s1 (mentioned earlier)/which doesn't work under Win8. – MKANET Aug 04 '15 at 10:12
  • I specifically need help handling ***"%1"*** part of my code. – MKANET Aug 04 '15 at 10:26
  • PS: Also, as far as I know, Windows cmd interpreter doesn't have a native base64 character encoding. In other words, it's too late to base64 encode the already messed up string/variable in Powershell. It has to be done BEFORE its passed to Powershell. Maybe I didn't explain my problem clearly enough? – MKANET Aug 04 '15 at 16:03