Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to find the version of his Windows operating system.
Microsoft Scripting Guy, Ed Wilson, is here. It is not a secret that I love Windows PowerShell. It is not a secret that I also love Windows 8.1 especially on my Surface Pro 2.
Disclaimer: I bought my Surface Pro 2 myself at the Microsoft Store in the mall. I paid the same price as everyone else does, and Istood in line to make the purchase just like everyone else does. I did so because I wanted one to take with me on my five week trip to Europe. It absolutely rocked.
Anyway, I love Windows PowerShell so much that I spend a lot of time playing around with it. I was on the Sapien blog the other night, and I ran across the following post by my friend, Alexander Riedel: Windows 8.1 breaks the version API. I thought to myself, "Hmmmm, I need to test this out." In particular, he was referring to using the static OSVersion property from the System.Environment class in .NET. Here is the code:
[environment]::OSVersion.Version
When I opened the Windows PowerShell console to test this, I found that it works just fine. The code and associated output is shown here:
Well, I know that Alexander is a very intelligent and careful person. He certainly wouldn't make wild accusations without backup or proof. So I opened the Windows PowerShell ISE and typed the same code. The results are shown here:
So it would seem that his blog post is correct. Except, it is not actually broken, and Alexander points to the Microsoft page that shows that this API is in fact deprecated in Windows 8.1: Operating system version changes in Windows 8.1 and Windows Server 2012 R2.
Deprecated, as you may know, means that you should quit using it because in future versions it may be removed and may no longer be supported. Sometimes a deprecated API works—other times it gives inconsistent results, as is the current case. The article states that the reason for this move is that a number of applications do not get shimmed properly because of poor version checks. For example, an application written for Windows XP would test for that version, and even though the app would work on Windows 8.1 it would refuse to run because it was specifically checking for Windows XP. The way around this, is to use an application manifest, which is something that most Windows PowerShell scripters will not be doing.
Testing out a theory
So I decided to test out a theory. I wonder if the GetVersion function would still work if I get it directly from the Kernel32.dll. I wrote a function that I call Get-OSVersion to see if it will work.
The first thing I need to do is to go to Pinvoke.net and find the signature that I will need to use with the Add-Type command. Upon arriving at the site, I want to find Kernel32 in the left pane, and then under that section, I want to find the GetVersion function. This is pretty straightforward, and the site is shown in the following image:
Now that I have my signature, I use a HereString to store the signature as shown here:
Function Get-OSVersion
{
$signature = @"
[DllImport("kernel32.dll")]
public static extern uint GetVersion();
"@
Now, I need to use the Add-Type cmdlet to add definition. I use –PassThru because I want the object that is created to be available to me. The command is shown here:
Add-Type -MemberDefinition $signature -Name "Win32OSVersion" -Namespace Win32Functions -PassThru
That is all there is to my function, so I close out the curly braces. The more complicated part of this comes in trying to parse the DWORD that the GetVersion function returns. So the first thing I need to do is convert the DWORD into bytes. To do this, I use the System.BitConverter .NET Framework class and I call the static GetBytes method. I pass it the function Get-OSVersion that I created, and I call the static GetVersion method from my object. This line is shown here:
$os = [System.BitConverter]::GetBytes((Get-OSVersion)::GetVersion())
The $OS variable contains an array of four bytes. The first element in the array contains the major version number, the second element contains the minor version. Because an array begins with 0, I grab elements 0 and 1 and store them in the appropriate variables as shownhere:
$majorVersion = $os[0]
$minorVersion = $os[1]
I need to take an array of the last two bytes and convert them from bytes into an integer. To do this, I once again use the System.BitConverter .NET Framework class, and I call the static ToInt16 method. It accepts an array and a position in the array as arguments. Here it is easy. My array is elements 2 and 3, and I start at the first position in the newly created array. This will be my build number. Here is the command that does this:
$build = [byte]$os[2],[byte]$os[3]
$buildNumber = [System.BitConverter]::ToInt16($build,0)
The last thing to do is display some output. I thought about creating an object. But dude, for this I was only trying to see if it worked. So I created a string by using the format specifier and substitution values.
For each {number} portion in the following command, I substitute the value that is in the other side of the –F that occupies that position. So {0} will be replaced by the first item in my list—in this example, that is the value stored in the $MajorVersion variable.
"Version is {0}.{1} build {2}" -F $majorVersion,$minorVersion,$buildNumber
After all that work (it took me about an hour to write this), I finally run the script. And what happens? Yep, I proved that it lies about the operating system version. This is shown here:
So what is the answer? Dude (or dudette), it is easy. Use WMI. Here is the WMI code:
(Get-CimInstance Win32_OperatingSystem).version
Note Remember, this is only a problem in Windows 8.1, so you should definitely be using Get-CimInstance and NOT using Get-WMIObject.
Here is the output. I see that it works properly, and it was a heck of a lot easier than messing around with the Kernel32 API:
That is all there is to using Windows PowerShell to find Windows operating system version information. Poshpourri Week will continue tomorrow when I will have a guest blog post from newly minted Windows PowerShell MVP, Dave Wyatt. The post is cool, and you don’t want to miss it.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
Here's how I do it. Weakness: I'm completely ignoring the build number. I'm only considering the major and minor version and combining it to a float for comparison purposes.
Write-Output "Testing that we're running on Window 6.2 and up, and that we're on a `"server`" edition."
# get info for the running OS$WverNow = Get-CimInstance -ClassName Win32_OperatingSystem# perform a matching operation on the Version property as a string, matches nn.nn but not the second '.' or...