Thursday, May 22, 2008

Is this PowerShell Session a 32-bit or a 64-bit?

How can one identify whether the current PowerShell process is running the 32-bit version (x86) or the 64-bit version of PowerShell? Well first, why would you care? You are right, normally I do not care, but if I need to execute VBscript with Invoke-Script, I need to know as that only works on 32-bit Windows.

I'm now running Vista x64 - and that is actually a pleasure. For the first time, I feel my PC is responsive enough when running many applications. If you are interested, my PC is a Dell D830 with 4 GB RAM and a Intel Turbo Channel module having 1 GB. OK specs and nice performance.

One of the minor problems I have encountered is that MSScriptControl.ScriptControl used by Invoke-Script cannot be started from 64-bit PowerShell. I simply does not exist :(

So I set off hunting a way of finding out how to differentiate, so I could do something clever.

First, I looked at the $host variable, but that seemed to return the same info. Next, I looked at the current process. The only difference I found was the image path, there does not seem to be any flag indicating the execution mode. Strange, I would have expected that.

Before jumping on a image path test, I looked as wmi32_process as well. This did not really help me, but at least win32_operatingSystem.OSArchitecture help me figuring out the platform.

This all added up in Get-Architecture.ps1 which contains -



param([switch]$CurrentProcess)

if ($CurrentProcess.ispresent) {
$me=[diagnostics.process]::GetCurrentProcess()
if ($me.path -match '\\syswow64\\') {
32
}
else {
64
}
}
else {
$os=Get-WMIObject win32_operatingsystem
if ($os.OSArchitecture -eq "64-bit") {
64
}
else {
32
}
}


Example -



PS> powershell -noprofile {get-architecture -currentprocess}
64
PS> C:\WINDOWS\syswow64\windowspowershell\v1.0\powershell.exe -noprofile {get-architecture -currentprocess}
32
PS> powershell -noprofile {get-architecture}
64
PS> C:\WINDOWS\syswow64\windowspowershell\v1.0\powershell.exe -noprofile {get-architecture}
64
PS> powershell -noprofile {get-architecture -currentprocess}
64
PS> C:\WINDOWS\syswow64\windowspowershell\v1.0\powershell.exe -noprofile {get-architecture -currentprocess}
32


Suggestions for improvements are highly welcome!

17 comments:

Anonymous said...

Why not just check the size of a pointer?

if ([System.IntPtr]::Size -eq 8)
{
64
}
else
{
32
}

Per Østergaard said...

You are right, that actually works! The reason I did not look into this, is that I assumed that .Net would isolate me from the information: An int is the same size on 32 and 64 bit (try [int]::MaxValue). Obviously [IntPtr] reveal the underlying bitwidth.

Per Østergaard said...

The script now reads -

param([switch]$CurrentProcess)

if ($CurrentProcess.ispresent) {
    if ([intptr]::size -eq 4) {
        32
    }
    else {
        64
    }
}
else {
    $os=Get-WMIObject win32_operatingsystem
    if ($os.OSArchitecture -eq "64-bit") {
        64
    }
    else {
        32
    }
}

Faster is normally better

Anonymous said...

Would checking the PROCESSOR_ARCHITECTURE environment variable be just as easy?

on a 64bit powershell it eq AMD64 in a 32bit powershell it eq X86

this is from a non programmer so I could be totally wrong.

Per Østergaard said...

Hi Ryan

To my surprise, it turns out your are right! First, I thought you were wrong, but when I checked, PowerShell32 actually has modified environment variables compared to a normal process! But if you look at the environment variables with e.g. PROCEXP, PROCESSOR_ARCHITECTURE is always AMD64 (on my system).
It seems like PowerShell is doing something...

BTW: ProgramFiles is also changed


Per

Pat Richard, MCSE MVP said...

One liner:
if ((Get-WMIObject win32_operatingsystem).OSArchitecture -eq '64-bit'){ write-host "64 bit"}else{write-host "32-bit"}

Anonymous said...

> The OSArchitecture property doesn't seem to exist.
We have the same problem with Windows XP Professional x64 edition.

Brandon Aiken said...

Win32_OperatingSystem's OSArchitecture does not exist in versions of Windows prior to Vista. This is in the WMI documents:
http://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx

Checking the size of a pointer will work, but you must understand that it's not a good process in general programming. While almost all systems use memory addressing the same size as the word size architecture, there is no reason this need always be the case. For example, the Pentium 4 had 64-bit buses, and the Pentium 2 had a 36-bit memory address space. 99 times out of 100 it will work, but you should be aware it is not a rule. If you're running a system utilizing PAE, you very likely may get inconsistent results asking for the size of a pointer.

Brandon Aiken said...

Win32_OperatingSystem's OSArchitecture does not exist in versions of Windows prior to Vista. This is in the WMI documents:
http://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx

Checking the size of a pointer will work, but you must understand that it's not a good process in general programming. While almost all systems use memory addressing the same size as the word size architecture, there is no reason this need always be the case. For example, the Pentium 4 had 64-bit buses, and the Pentium 2 had a 36-bit memory address space. 99 times out of 100 it will work, but you should be aware it is not a rule. If you're running a system utilizing PAE, you very likely may get inconsistent results asking for the size of a pointer.

Per Østergaard said...

@Brandon, thank you for your comment. I understand the limitatitions of the method used. But until something better shows up! Actually, I just figured out a new way of determine the PowerShell version. [Environment]::CommandLine contains \syswow64\ for 32-bit and \system32\ for 64-bit PowerShell

Chris said...

I really wish the Powershell team had put this in something like "Get-PowershellVersion".

The key issue, I think, is on a 64 bit version of Windows you can launch either the 64 or 32 bit version of the Powershell host. For some VMware PowerCLI cmdlets the 32 bit host is required, which is why I've been looking for a reliable way to test this and add it to my scripts that use those 32-bit cmdlets.

Using "Get-WMIObject win32_operatingsystem).OSArchitecture -eq '64-bit')" isn't valid because it's asking about the OS architecture, not which version of the Powershell host you launched. On my computer it returns "64 bit" on both the x86 and x64 Powershell sessions.

The pointer method works, but as Brandon pointed it out, it may not be reliable.

So far it looks like checking the environment variable is the most reliable way. Thanks for digging that up.

I'm going to put this one-liner into a function for my scripts to use.

if ( [Environment]::CommandLine -match "System32" ) { "64bit" } elseif ( [Environment]::CommandLine -match "SysWOW64" ) { "32bit" } else { $null }

Olaf said...

$psHome returns the Powershell "installation" location.

Olaf said...

Per, I wandered across this again after some time. I'm still working on this problem and unable to satisfactorily answer the question. If I kick off a 64-bit mode of Powershell from within a 32-bit session, how do I track the return codes back to the calling app of the original 32-bit PoSh session?

Per Østergaard said...

Hi Olaf

Depending on what you mean by 'return code', you have several options.

First, you can always get the exit code from the built-in variable $lastexitcode.

Second, you can capture the output from the called PowerShell as text using:
$var=powershell -noprofile Get-Date
$var | %{ $_.pstypenames[0] }
# returns all strings

Third, you can have objects returned from the called PowerShell using -outputformat xml:
$var=powershell -noprofile -outputformat xml Get-Date
$var | %{ $_.pstypenames[0] }
# returns a single datetime

Best
Per

Anonymous said...

With $env:Processor_Architecture is easier to know.

But look out because I've got a Windows Server 2008 R2 SP1 and the executables are changed.


Example1:
PS C:\Users\administrador> $(get-process powershell | select -first 1).path;$env:Processor_Architecture
C:\Windows\system32\Windowspowershell\v1.0\powershell.exe
AMD64

Example2:
PS C:\Users\administrador> $(get-process powershell | select -first 1).path;$env:Processor_Architecture
C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe
x86

Dan Bjorge (MSFT) said...

Keep in mind that ($_.OSArchitecture -eq "64-bit") is not safe to use in potentially localized environments. For example, "64 位元" is what you'll get on a 64 bit system using a zn-HK locale.

Per Østergaard said...

Hi Dan

Interesting.

We should switch to $_.OSArchitecture -match '^64-' then.