Executing 64 bit PowerShell from within a 32 bit PowerShell session
Due to legacy COM components I run PowerShell in 32 bit on 64 bit Windows. All fine, that's just a question of starting the right Powershell. From within my 32 bit PowerShell script I want to use PowerShell cmdlets to administer IIS. Now it seems they in turn use COM, but the 64 bit variant - so we'll get relatively obscure errors about unknown COM CLSIDs etc.
Doing some serious amount of Googling I came accross the Start-Job PowerShell cmdlet, which can start a job and has a magic -runAs32 switch. But this is the wrong way round. As 32 bit processes cannot always run "as 64 bit" if they are run on a purely 32 bit machine they did not provide the switch it seems. So we have to do some work ourselves to make it happen.
Attempt 1.
Found by more Googling: find the 64 PowerShell executable and invoke it directly. The powershell.exe executable accepts a command Script Block, however, due to the way we invoke it we have to convert a script block into a string. This causes issues if the script block closed over local variables for example. Furthermore, there seems no way to pass arguments to the script block.The only gotcha here is that the System32 folder (which holds 64 bit executables in general on 64 bit Windows), actually does *not* contain 64 bit PowerShell. To find the executable we can use the following:
"$env:windir\sysnative\WindowsPowerShell\v1.0\powershell.exe"
Attempt 2.
Another way to try to do this is to connect to a 64 bit PowerShell session on the local machine, and execute our code there. The nice thing about this way of doing it is that we can call Invoke-Command with a session to run the code in and an ArgumentList parameter to pass arguments to the script block.The downside of this approach, however, is that a Windows service must run which provides the necessary communication support for PowerShell as theoretically this can execute a script on the other side of the planet. The script below simply enables this if creating a session fails.
- function Ps64([scriptblock]$block) {
- $machineName = [Environment]::MachineName;
- try {
- # Note: The configuration name is what forces it to 64 bit.
- $session = New-PSSession -ComputerName $machineName -ConfigurationName Microsoft.PowerShell
- } catch {
- # Try to enable Remote Management on this machine.
- winrm quickconfig -q -f
- # Try again.
- $session = New-PSSession -ComputerName $machineName -ConfigurationName Microsoft.PowerShell
- }
- # Now invoke our code in 64 bit PowerShell.
- try {
- Invoke-Command -Session $session -ScriptBlock $block -ArgumentList $args
- } finally {
- Remove-PSSession $session
- }
- }
There you have it. If you have found different / better ways to achieving the same thing, or there are blatant errors in my code then do get in touch!
I put one of this two blocks in front of my scripts.
ReplyDeleteif ($env:Processor_Architecture -ne "x86") {
Write-Warning "changing from 64bit to 32bit powershell"
$powershell=Join-Path $PSHOME.tolower().replace("system32","syswow64") powershell.exe
if ($myInvocation.Line) {
&"$powershell" -NonInteractive -NoProfile -ExecutionPolicy Bypass $myInvocation.Line
} else {
&"$powershell" -NonInteractive -NoProfile -ExecutionPolicy Bypass -file "$($myInvocation.InvocationName)" $args
}
exit $lastexitcode
}
<#
if ($env:PROCESSOR_ARCHITEW6432 -eq "AMD64") {
Write-Log -level warning "changing from 32bit to 64bit powershell"
$powershell=join-path $PSHOME.tolower().replace("syswow64","sysnative").replace("system32","sysnative") powershell.exe
if ($myInvocation.Line) {
&"$powershell" -NonInteractive -NoProfile -ExecutionPolicy Bypass $myInvocation.Line
} else {
&"$powershell" -NonInteractive -NoProfile -ExecutionPolicy Bypass -file "$($myInvocation.InvocationName)" $args
}
exit $lastexitcode
}
#>
http://karlprosser.com/coder/2011/11/04/calling-powershell-64bit-from-32bit-and-visa-versa/
ReplyDeleteTHANK YOU
ReplyDeleteI've been fighting all day with this