“ New-Object WindowsPrincipal([WindowsIdentity] :: GetCurrent())”和“ [WindowsPrincipal] [WindowsIdentity] :: GetCurrent()”之间的区别

时间:2019-04-17 16:26:18

标签: powershell

我正在尝试检查Powerhell脚本是否以管理员身份运行。 在网上搜索后,我得到了一些可以正常工作的示例代码。
为了获得WindowsPrincipal对象,我找到了以下两个示例代码。

第一:

New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())

第二:

[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()

第二个让我感到困惑。

根据this page,我知道[ ]是强制转换运算符。 根据{{​​3}},PowerShell是建立在.NET Framework之上的。我认为这意味着可以将上述两个PowerShell脚本转换为C#。
所以我尝试一下。
当我将第一个PowerShell脚本转换为C#时。可以正常工作,如下所示。

## First PowerShell script
New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())

// C#
var wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());

但是当我尝试将第二个PowerShell脚本转换为C#时。我得到编译错误。 IDE告诉我WindowsIdentity无法转换为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();

就像我在C#上尝试过的那样,System.Security.Principal.WindowsIdentity类型不能直接转换为System.Security.Principal.WindowsPrincipal类型。但是为什么第二个PowerShell脚本可用?
还是第二个PowerShell脚本中的[ ]运算符不是类型转换运算符?
也许这个运算符不仅仅可以转换对象类型?
第一个PowerShell脚本和第二个PowerShell脚本有什么区别?
我还想念其他东西吗?

1 个答案:

答案 0 :(得分:3)

TLDR:PowerShell可以执行 Magic 。 C#不能做魔术。

PowerShell可以同时处理电锯,保龄球和球。

如果C#是提前定义的,则C#只能处理它们。尝试在杂耍例程中添加新的电锯会导致杂耍者(编译器)抱怨。


问题在于函数,对象类型以及如何转换对象类型之间的差异。

System.Security.Principal是基础.NET库。该库可由C#和PowerShell使用。

WindowsIdentity.GetCurrent()是库中的函数。

WindowsPrincipal是一种对象类型,例如例如stringint

调用WindowsIdentity.GetCurrent()返回一个WindowsIdentity对象,然后可以在代码中使用它。

由于WindowsIdentity不一定是您要使用的对象类型,因此我们想使用WindowsPrincipal对象。不幸的是,我们无法直接从WindowsIdentity投射到WindowsPrincipal对象。我们必须使用WindowsPrincipal构造函数。

在PowerShell中,您可以通过New-Object cmdlet或首次使用变量来创建新对象。这样做的方便是因为PowerShell是脚本语言,其中使用了变量,例如$a = 1隐式创建一个新变量。例如

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

使用New-Object示例:

New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())

这是正确的方法。您正在创建WindowsPrincipal类型的新对象,并将Windows身份传递给构造函数。

第二种方法:

[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()

是“错误的”,因为它使用了强制类型转换,并且前面我们说过我们不能直接从WindowsIdentity强制转换为WindowsPrincipal对象。那么这是如何工作的呢?好吧,我之前说过,PowerShell可以发挥魔力,好一会儿我会讲到。首先,让我们看看正确的C#代码:

在C#中调用.NET Framework特定功能的语法与PowerShell确实有所不同。而导致出现特定编译错误的原因是,因为我们正在尝试投射对象,而这是我们无法做到的。

示例:

using System.Security.Principal;
var wp = WindowsIdentity.GetCurrent() as WindowsPrincipal; // compile error

编译器将其翻译为:

WindowsIdentity.GetCurrent()

运行功能GetCurrent()

as WindowsPrincipal;

期望返回类型为WindowsPrincipal <-编译时错误。编译错误是因为函数没有返回类型WindowsPrincipal,而是返回了类型WindowsIdentity

第二个示例也是一个变体,因为它不能直接强制转换对象,因此无法使用。例如

using System.Security.Principal;
var wp = (WindowsPrincipal) WindowsIdentity.GetCurrent(); // compile error

编译器将其翻译为:

WindowsIdentity.GetCurrent()

运行函数GetCurrent()并返回WindowsIdentity对象。

(WindowsPrincipal) WindowsIdentity.GetCurrent();

采用WindowsIdentity对象并将其直接转换为WindowsPrincipal类型,这是不可以的。

正确的C#代码也需要使用new关键字:

var wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());

编译器将其翻译为:

WindowsIdentity.GetCurrent()

运行函数GetCurrent()并返回WindowsIdentity对象。

new WindowsPrincipal(WindowsIdentity.GetCurrent())

创建一个新的WindowsPrincipal对象,将WindowsIdentity对象传递给构造函数。现在可以使用。

那为什么要第二个例子:

[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()

通过执行“错误”操作在PowerShell中工作?因为PowerShell是一种脚本语言,并且可以即时进行解释,并且可以使用 Magic ,所以它可以解释以上内容并执行一些Type Conversion Magic Quote:

  1. 直接分配。。如果您的输入可以直接分配,只需将输入强制转换为该类型。
  2. 基于语言的转换。当目标类型为void,布尔值,字符串,数组,哈希表,PSReference(即:[ref]),XmlDocument(即: [xml])。委托(以支持ScriptBlock到委托的转换)和枚举。
  3. 解析转换。如果目标类型定义了接受该输入的Parse()方法,请使用该方法。
  4. 静态创建转换。如果目标类型定义了采用该输入的静态:: Create()方法,请使用该方法。
  5. 构造函数转换。如果目标类型定义了一个接受您输入的构造函数,请使用它。
  6. 强制转换。。如果目标类型从源类型定义了隐式或显式强制转换运算符,请使用它。如果源类型为目标类型定义了隐式或显式强制转换运算符,请使用该类型。
  7. IConvertible转换。如果源类型定义了一个IConvertible实现,该实现知道如何转换为目标类型,请使用它。
  8. IDictionary转换。。如果源类型是IDictionary(即哈希表),请尝试使用其默认构造函数创建目标类型的实例,然后在IDictionary中使用名称和值。在源对象上设置属性。
  9. PSObject属性转换。如果源类型是PSObject,请尝试使用其默认构造函数创建目标类型的实例,然后使用PSObject中的属性名称和值来设置属性在源对象上。 。如果名称映射到方法而不是属性,请使用值作为参数来调用该方法。
  10. TypeConverter转换。如果存在注册的TypeConverter或PSTypeConverter可以处理转换,请执行此操作。您可以通过type.ps1xml文件(请参阅:$ pshome \ Types.ps1xml)或通过Update-TypeData注册TypeConverter。

基本上,每当您在PowerShell中进行类型转换时,它将执行Magic并尝试每种方法为您动态地动态转换类型。这就是为什么它可以处理您在杂耍者身上扔新的电锯或保龄球的原因。 PowerShell可以解释为我们不是在试图将电锯转换为球,而是将电锯只是另一个对象,并且我们已经知道如何处理对象。

此解释不是.NET的一部分,因此C#无法做到这一点,因此我们必须显式定义所有内容,并正确执行所有操作。这意味着您可以将所有C#和.NET代码转换为有效的PowerShell代码,但不能反过来。