Reputation: 43
I'm trying to check if Powerhell script is running as Administrator.
After searching the web, I got some sample code that works fine.
In order to get the WindowsPrincipal
object, I found two sample code as below.
First:
New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
Second:
[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
The second one made me confused.
According to this page, I know that the [ ]
is a cast operator.
And according to this page, PowerShell is built on the .NET Framework. In my opinion, this means that the above two PowerShell scripts can be converted to C#.
So I try it.
When I convert the first PowerShell script to C#. It work fine as below.
## First PowerShell script
New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
// C#
var wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
But when I try to convert the second PowerShell script to C#. I get compile error. The IDE tells me that WindowsIdentity
cannot be cast to WindowsPrincipal
.
## Second PowerShell script
[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
// both C# code below cause compile error
var wp = System.Security.Principal.WindowsIdentity.GetCurrent() as System.Security.Principal.WindowsPrincipal;
var wp2 = (System.Security.Principal.WindowsPrincipal)System.Security.Principal.WindowsIdentity.GetCurrent();
Just as I tried on C#, the System.Security.Principal.WindowsIdentity
type cannot be directly converted to the System.Security.Principal.WindowsPrincipal
type. But why is the second PowerShell script available?
Or is the [ ]
operator in the second PowerShell script not a type conversion operator?
Maybe this operator do more than just convert object type?
What's the difference between first PowerShell script and second PowerShell script?
Did I missing any other things?
Upvotes: 4
Views: 4006
Reputation: 13453
TLDR: PowerShell can do Magic. C# can't do Magic.
PowerShell can juggle Chainsaws, bowling pins, and balls at the same time.
C# can only juggle balls if they are defined ahead of time. Trying to add a new Chainsaw into the juggling routine causes the juggler(compiler) to complain.
The issues are the differences between Functions, Object types, and how to cast object types.
System.Security.Principal
is the base .NET library. The library can be used by C# and PowerShell.
WindowsIdentity.GetCurrent()
is a function in the library.
WindowsPrincipal
is an object type e.g. like string
or int
.
Calling WindowsIdentity.GetCurrent()
returns a WindowsIdentity
object, which you can then use in your code.
Since WindowsIdentity
is not necessarily the object type you want to work with, we want to use a WindowsPrincipal
object. Unfortunately we cannot directly cast from WindowsIdentity
to WindowsPrincipal
object. We have to use the WindowsPrincipal
constructor.
In PowerShell, you create a new object either by the New-Object
cmdlet, or by simply using a variable for the first time. This convenience is because PowerShell is a scripting language, where using a variable e.g. $a = 1
implicitly creates a new variable. e.g.
PS C:\> Get-Variable test
Get-Variable : Cannot find a variable with the name 'test'.
At line:1 char:1
+ Get-Variable test
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (test:String) [Get-Variable], ItemNotFoundException
+ FullyQualifiedErrorId : VariableNotFound,Microsoft.PowerShell.Commands.GetVariableCommand
PS C:\> $test = 1
PS C:\> Get-Variable test
Name Value
---- -----
test 1
Using the New-Object
example:
New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
This is the right way to do it. You are creating a new object of type WindowsPrincipal
, and passing the Windows Identity to the constructor.
The second method:
[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
Is "wrong" because it uses casting, and earlier we stated that we cannot directly cast from WindowsIdentity
to WindowsPrincipal
object. So how does this work? Well, I said earlier that PowerShell does Magic, well I'll get to that in a minute. First let's see the correct C# Code:
Calling .NET Framework specific functions in C# do differ in Syntax than PowerShell. And the reason for the specific compile error you are getting is because we are trying to cast the object, which we can't.
The example:
using System.Security.Principal;
var wp = WindowsIdentity.GetCurrent() as WindowsPrincipal; // compile error
The Compiler translates it to:
WindowsIdentity.GetCurrent()
Run function GetCurrent()
as WindowsPrincipal;
Expect a return type of WindowsPrincipal
<-- compile time error. The compile error is because the function does not return a type of WindowsPrincipal
, instead it returns a type WindowsIdentity
.
Second example is also a variation that doesn't work because it can't cast the object directly. e.g.
using System.Security.Principal;
var wp = (WindowsPrincipal) WindowsIdentity.GetCurrent(); // compile error
The Compiler translates it to:
WindowsIdentity.GetCurrent()
Run function GetCurrent()
and return with the WindowsIdentity
object.
(WindowsPrincipal) WindowsIdentity.GetCurrent();
Take the WindowsIdentity
object and directly cast it to a WindowsPrincipal
type, which it can't.
The correct C# code also needs the new
keyword is:
var wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
The Compiler translates it to:
WindowsIdentity.GetCurrent()
Run function GetCurrent()
and return with the WindowsIdentity
object.
new WindowsPrincipal(WindowsIdentity.GetCurrent())
Create a new WindowsPrincipal
object passing the WindowsIdentity
object into the constructor. Which now works.
So why does the second example:
[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
Work in PowerShell by doing something that is "wrong"? Because PowerShell is a scripting language, and is interpreted on the fly, and can use Magic, it can interpret the above and perform some Type Conversion Magic Quote:
Basically, whenever you do a type conversion in PowerShell, it will perform Magic and try each method to convert the type dynamically on the fly for you. That is why it can handle you throwing a new Chainsaw or bowling pin at the juggler. PowerShell can interpret that we aren't trying to convert a chainsaw into a ball, and instead that a chainsaw is just another object, and we already know how to juggle objects.
This interpretation isn't a part of .NET, and so C# can't do that, and we have to explicitly define everything, and do everything correctly. This means that you can convert all C# and .NET code into valid PowerShell code, but not the other way around.
Upvotes: 5