3

在对象中进行管道传输时,我遇到了 New-WebBinding 的问题。我有一个对象,它定义了 5 个属性:名称、协议、端口、IPAddress 和 HostHeader(New-WebBinding cmdlet 支持所有 5 个属性作为 Accept Pipeline 输入:ValueByPropertyName)。但是,当您通过管道输入此对象时,它仍会请求提交 Name:。如果您想复制问题,这是一个快速测试功能。如果您在提示符处按 Enter,它会成功处理对象并添加绑定。但提示本身将其作为非交互式脚本破坏。

我已经用 PS v3 和 PS v4 对此进行了测试。

我很确定我做这一切都是正确的,但想确保没有我可能忽略的东西。现在我只是在 foreach 循环中迭代我的对象集合,它没有这个问题,但想看看这是否是我应该报告的错误。

function Test-WebBinding{
   [CmdletBinding()]
   Param()

   $testBindingCol = @()

   $testBinding1 = New-Object System.Object
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'https'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Port -Value '4000'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4000'
   $testBindingCol += $testBinding1

   $testBinding2 = New-Object System.Object
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'http'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Port -Value '4001'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4001'
   $testBindingCol += $testBinding2

   $testBindingCol | New-WebBinding
}
4

1 回答 1

5

PetSerAl 在上面的评论中提出了正确的想法:

一种解决方法是将当前位置更改为某个站点 ( cd IIS:\Sites\SomeSite),与哪个站点无关

这确实有效,但为什么它在正常的文件系统提示符下不起作用?

为了发现为什么New-WebBinding会这样,我将 Microsoft.IIS.PowerShell.Provider包含这个和其他WebAdministrationcmdlet 的程序集加载到 dotPeek 中。程序集位于 GAC 中,因此您告诉 dotPeek “从 GAC 打开”。

加载后,我们感兴趣的类被称为NewWebBindingCommand.

经过粗略的检查,我们可以看到所有参数属性都用[Parameter(ValueFromPipelineByPropertyName = true)]属性修饰,所以这是一个好的开始,管道具有匹配属性名称的对象数组应该可以工作:

在此处输入图像描述

NewWebBindingCommand最终继承自System.Management.Automation.Cmdlet并且在这种情况下是覆盖该BeginProcessing方法。如果被覆盖,BeginProcessing则由 PowerShell 调用并“为 cmdlet 提供一次性预处理功能”。

重要的是要了解BeginProcessing在处理任何管道馈送的命名参数并将其绑定到 cmdlet 的属性之前调用 cmdlet 的覆盖(请参阅:Cmdlet 处理生命周期 (MSDN))。

我们的New-WebBindingcmdlet 实现BeginProcessing如下所示:

protected override void BeginProcessing()
{
  base.BeginProcessing();
  if (!string.IsNullOrEmpty(this.siteName))
    return;
  this.siteName = this.GetSiteName("Name");
}

this.siteNameName是将绑定到-Name参数的属性的私有成员值。当我们到达if(...)上面的语句时,`this.siteName 尚未绑定(它为空),因此落入:

this.siteName = this.GetSiteName("Name");

调用GetSiteName()调用 cmdlet 的直接基类,该基类提供了许多对许多不同cmdletHelperCommand有用的“帮助”方法。WebAdministration

HelperCommand.GetSiteName(string prompt)看起来像这样:

protected string GetSiteName(string prompt)
{
  PathInfo pathInfo = this.SessionState.PSVariable.Get("PWD").Value as PathInfo;
  if (pathInfo != null && pathInfo.Provider.Name.Equals("WebAdministration", StringComparison.OrdinalIgnoreCase))
  {
    string[] strArray = pathInfo.Path.Split('\\');
    if (strArray.Length == 3 && strArray[1].Equals("sites", StringComparison.OrdinalIgnoreCase))
      return strArray[2];
  }
  if (!string.IsNullOrEmpty(prompt))
    return this.PromptForParameter<string>(prompt);
  return (string) null;
}

为了了解这个问题,我创建了自己Kevulator的PowerShell cmdlet New-WebBinding(称为BeginProcessing()New-WebBindingGetSiteName()

这是GetSiteName在绑定到绑定到 PowerShell 会话管道时在 VS2015 中闯入的屏幕截图New-Kevulator

在此处输入图像描述

顶部的箭头表明我们Name支持的属性siteName尚未绑定并且仍然存在null(如上所述导致GetSiteName执行)。我们也刚刚跨过了这一行的一个断点:

PathInfo pathInfo = 
        this.SessionState.PSVariable.Get("PWD").Value as PathInfo;

...这决定了我们坐在什么样的路径提供者处。在这种情况下,我们处于正常的文件系统C:\>提示符下,因此提供程序名称将为FileSystem. 我用第二个箭头突出显示了这一点。

如果我们Import-Module WebAdministration然后CD IIS:重新运行并再次中断,您可以看到路径提供者已更改WebAdministration为负责处理内部IIS:>和外部的生命:

在此处输入图像描述

如果pathInfo名称不等于WebAdministration,即我们没有在IIS:提示符下,那么代码将失败并会Name在命令行中提示输入参数,就像您所经历的那样。

如果pathInfo值为 WebAdministration那么将发生以下两种情况之一:

如果路径是IIS:IIS:\Sites代码失败并发出Name参数提示。

如果路径是IIS:\Sites\SomeSiteNameSomeSiteName返回并有效地成为-Name参数值,我们从GetSiteName()函数中退出回到BeginProcessing.

最后一种行为很有用,因为如果说您的当前路径,IIS:\Sites\MySite那么您可以通过管道输入该站点的绑定数组,但无需指定Name参数。或者,如果您确实Name在管道对象数组中提供了一个属性,那么这些将覆盖从您的IIS:\Sites\MySite上下文中获取的默认站点名称。

所以,回到我们的代码,一旦BeginProcessing运行了它的过程,我们的管道命名参数现在实际上是绑定的,然后ProcessRecord方法被调用,这是New-WebBinding必须执行的肉和土豆的工作。

TLDR:

New-WebBinding除非您将当前工作目录更改为 IIS 网站,否则不会绑定管道参数,例如cd iis:\Sites\MySite.

于 2016-02-19T08:13:38.407 回答