Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Wednesday, September 29, 2010 2:36 PM
I'm trying to add some "Command Prompt Here" and "PowerShell Prompt Here" shell extensions in the registry under HKLM:\Software\Classes\Directory\shell.
Here are a few that DO work:
- unelevated Command Prompt Here: %WINDIR%\System32\cmd.exe /k cd "%L"
- elevated Command Prompt Here: %WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -NoLogo -Command Start-Process -Verb RunAs -FilePath %WINDIR%\System32\cmd.exe -ArgumentList /k, cd, '%L'
- unelevated PowerShell Prompt Here: %WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoExit -Command Set-Location '%L'
What I can't get to work is an elevated PowerShell Prompt Here command. The one that seems the most logical is this:
%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -NoLogo -Command Start-Process -Verb RunAs -FilePath %WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -ArgumentList -NoLogo,-NoExit,-Command,Set-Location,'%L'
But the single-quotes in the parameter '%L' are not making it through. The command opens an elevated PowerShell console, but it does not set the correct directory and opens with this error (when used from a path that contains spaces):
Set-Location : A positional parameter cannot be found that accepts argument 'Files\Test'.
At line:1 char:13
- Set-Location <<<< C:\Program Files\Test
+ CategoryInfo : InvalidArgument: (:) [Set-Location], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.SetLocationCommand
Does anyone have any suggestions as to how I could get this to work?
Here is some PSH code to set up the registry correctly with one of the strings above:
$shellPath = 'HKLM:\Software\Classes\Directory\shell'
$path = "$shellPath\PshHere1"
if (!(Test-Path $path))
{
New-Item -Path $path
}
Set-ItemProperty -Path $path -Name '(Default)' -Type 'String' -Value 'PowerShell Prompt Here'
$path = "$path\command"
if (!(Test-Path $path))
{
New-Item -Path $path
}
Set-ItemProperty -Path $path -Name '(Default)' -Type 'ExpandString' -Value "%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoExit -Command Set-Location '%L'"
The user is assumed to be a member of the Administrators group on the machine.
Thanks.
All replies (3)
Wednesday, September 29, 2010 5:41 PM âś…Answered | 1 vote
Wow.... really weird one.... You can duplicate the problem from a powershell window running from start-process with a location that uses a space, i.e.,
Start-Process -Verb runas -FilePath $env:windir\system32\WindowsPowerShell\v1.0\powershell.exe -ArgumentList "-Nologo","-noexit","-command","set-location","-Path",'c:\program files'
The above gives you the same error you were getting. The solution is really strange. It looks like start-process wants two separate arguments from 'c:\program files'. Check it out.... the following works:
Start-Process -Verb runas -FilePath $env:windir\system32\WindowsPowerShell\v1.0\powershell.exe -ArgumentList "-Nologo","-noexit","-command","set-location","-Path","'c:\program", "files'"
Regarding a solution to your problem, I'm not sure - but, I'm hoping what I found helps in the pursuit.
write-host ((0..56)|%{if (($_+1)%3 -eq 0){[char][int]("116111101110117102102064103109097105108046099111109"[($_-2)..$_] -join "")}}) -separator ""
Thursday, September 30, 2010 2:41 PM
Thanks Tome, that was very helpful. I was able to get this command to work from a PowerShell command line:
$p="'C:\Program Files\Common Files'"; $a='-NoLogo','-NoExit','-Command','Set-Location'; $b=$p.Split(' '); $a=$a + $b; Start-Process -FilePath $env:windir\system32\WindowsPowerShell\v1.0\powershell.exe -Verb RunAs -ArgumentList $a
Running this command directly from the command line caused the UAC prompt to appear, followed by a new console window opening in the correct path!
However, I'm having trouble getting this to work in the registry as a quoted string, where it needs to work in order for the shell extension to function properly.
I've tried this (using the code sample in my first post):
Set-ItemProperty -Path $path -Name '(Default)' -Type 'ExpandString' -Value '%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -NoLogo -Command $p="''%L''"; $a="-NoLogo","-NoExit","-Command","Set-Location"; $b=$p.Split(" "); $a=$a + $b; Start-Process -FilePath C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Verb RunAs -ArgumentList $a'
I'm wrapping the -Value parameter in single quotes so that the inline variables such as $a aren't resolved. But this string is not working, when I select a directory in Windows Explorer and execute the shell extension (named 'PowerShell Prompt Here'), a window opens and closes but there's no UAC prompt and no subsequent console window.
Any suggestions as to how I can quote this correctly? Or maybe I need to wrap the code differently so the -Command will interpret all of it?
Thanks.
Thursday, September 30, 2010 11:41 PM
OK, after a lot of experimentation, I got the quoting figured out for the registry. I'm including my revised (and tested) code here, and Tome, I'll mark your answer as the official answer. Thanks again!
function Invoke-Conditional
{
<#
.SYNOPSIS
A conditional (or ternary) operator.
.DESCRIPTION
Returns one of two values depending on the value of the first Boolean expression. The name 'ternary' dates back to the name of this operator in the C, C++, and C# languages, because it has three arguments.
The original author is Karl Prosser, posted online by Jeffrey Snover on the Windows PowerShell Blog.
.EXAMPLE
$processWidth = Invoke-Conditional { [System.IntPtr]::Size -eq 4 } { '32' } { '64' }
#>
Param(
[scriptblock]$decider,
[scriptblock]$ifTrue,
[scriptblock]$ifFalse
)
if (&$decider)
{
&$ifTrue
}
else
{
&$ifFalse
}
}
# There's probably better ways to do this determination of OS type<br/>
$osType = '32-bit'<br/>
if (Test-Path "$env:SystemDrive\Program Files (x86)")<br/>
{<br/>
$osType = '64-bit'<br/>
}<br/>
<br/>
$shellPath = 'HKLM:\Software\Classes\Directory\shell'
$sysdir = '[sysdir]'
$sysdirNative = 'System32'
$sysdir32on64 = 'SysWOW64'
$command1 = '%WINDIR%\[sysdir]\cmd.exe /k cd "%L"'
$command2 = "%WINDIR%\[sysdir]\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -NoLogo -Command Start-Process -Verb RunAs -FilePath %WINDIR%\[sysdir]\cmd.exe -ArgumentList /k, cd, '%L'"
$command3 = "%WINDIR%\[sysdir]\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoExit -Command Set-Location '%L'"
$command4 = "%WINDIR%\[sysdir]\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -NoLogo -Command `$q=[Convert]::ToChar(39); `$p=`$q + '%L' + `$q; `$a='-NoLogo','-NoExit','-Command','Set-Location'; `$b=`$p.Split(' '); `$a=`$a + `$b; Start-Process -FilePath %WINDIR%\[sysdir]\WindowsPowerShell\v1.0\powershell.exe -Verb RunAs -ArgumentList `$a"
$table = New-Object System.Collections.Generic.Dictionary['string,string']
$table['CmdHere1'] = $command1.Replace($sysdir, $sysdirNative)
$table['CmdHere15'] = $command2.Replace($sysdir, $sysdirNative)
$table['PshHere1'] = $command3.Replace($sysdir, $sysdirNative)
$table['PshHere15'] = $command4.Replace($sysdir, $sysdirNative)
if ($osType -eq '64-bit')
{
$table['CmdHere2'] = $command1.Replace($sysdir, $sysdir32on64)
$table['CmdHere25'] = $command2.Replace($sysdir, $sysdir32on64)
$table['PshHere2'] = $command3.Replace($sysdir, $sysdir32on64)
$table['PshHere25'] = $command4.Replace($sysdir, $sysdir32on64)
}
foreach ($key in $table.Keys)
{
$path = "$shellPath\$key"
$name = Invoke-Conditional { $key.StartsWith('Cmd') } { 'Command Prompt Here' } { 'PowerShell Prompt Here' }
if ($key.EndsWith('5'))
{
$name += ' as Administrator'
}
if ($osType -eq '64-bit')
{
if ($key.IndexOf('1') -ge 0)
{
$name += ' (64-bit)'
}
elseif ($key.IndexOf('2') -ge 0)
{
$name += ' (32-bit)'
}
}
if (!(Test-Path $path))
{
New-Item -Path $path | Out-Null
}
Set-ItemProperty -Path $path -Name '(Default)' -Type 'String' -Value $name | Out-Null
$path = "$path\command"
if (!(Test-Path $path))
{
New-Item -Path $path | Out-Null
}
Set-ItemProperty -Path $path -Name '(Default)' -Type 'ExpandString' -Value $table[$key] | Out-Null
}