【问题标题】:How to troubleshoot a 'System.Management.Automation.CmdletInvocationException'如何对“System.Management.Automation.CmdletInvocationException”进行故障排除
【发布时间】:2011-02-07 20:04:39
【问题描述】:

有谁知道如何最好地确定此异常的具体根本原因?

考虑一个 WCF 服务,它应该使用 Powershell 2.0 远程处理在远程计算机上执行 MSBuild。在这两种情况下,脚本环境都在进程内调用(通过 C# 用于 Powershell,通过 Powershell 用于 MSBuild),而不是“shelling-out”——这是避免命令行地狱以及启用传递的特定设计决策将实际对象放入 Powershell 脚本中。

调用 MSBuild 的 Powershell 脚本的精简版如下所示:

function Run-MSBuild
{
    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Build.Engine")

    $engine = New-Object Microsoft.Build.BuildEngine.Engine
    $engine.BinPath = "C:\Windows\Microsoft.NET\Framework\v3.5"

    $project = New-Object Microsoft.Build.BuildEngine.Project($engine, "3.5")
    $project.Load("deploy.targets")
    $project.InitialTargets = "DoStuff"

    # Process the input object
    while ($input.MoveNext())
    {
        # Set MSBuild Properties & Item
    }


    # Optionally setup some loggers (have also tried it without any loggers)
    $consoleLogger = New-Object Microsoft.Build.BuildEngine.ConsoleLogger
    $engine.RegisterLogger($consoleLogger)

    $fileLogger = New-Object Microsoft.Build.BuildEngine.FileLogger
    $fileLogger.Parameters = "verbosity=diagnostic"
    $engine.RegisterLogger($fileLogger)


    # Run the build - this is the line that throws a CmdletInvocationException
    $result = $project.Build()

    $engine.Shutdown()
}

从 PS 命令提示符运行上述脚本时,一切正常。但是,一旦从 C# 执行脚本,它就会失败并出现上述异常。

用于调用 Powershell 的 C# 代码如下所示(为简单起见,删除了远程功能):

// Build the DTO object that will be passed to Powershell
dto = SetupDTO()

RunspaceConfiguration runspaceConfig = RunspaceConfiguration.Create();
using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfig))
{
    runspace.Open();

    IList errors;
    using (var scriptInvoker = new RunspaceInvoke(runspace))
    {
        // The Powershell script lives in a file that gets compiled as an embedded resource
        TextReader tr = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("MyScriptResource"));
        string script = tr.ReadToEnd();

        // Load the script into the Runspace
        scriptInvoker.Invoke(script);

        // Call the function defined in the script, passing the DTO as an input object
        var psResults = scriptInvoker.Invoke("$input | Run-MSBuild", dto, out errors);
    }
}

注意:Invoke() 方法的重载允许您传入一个 IEnumerable 对象,它负责在 Powershell 变量 '$input' 中实例化一个枚举器 - 然后通过管道将其传递到脚本中。以下是一些支持链接:

假设问题与 MSBuild 输出 Powershell 运行空间无法处理的内容有关,我还尝试了对第二个 .Invoke() 调用的以下变体:

var psResults = scriptInvoker.Invoke("$input | Run-MSBuild | Out-String", dto, out errors);
var psResults = scriptInvoker.Invoke("$input | Run-MSBuild | Out-Null", dto, out errors);
var psResults = scriptInvoker.Invoke("Run-MSBuild | Out-String");
var psResults = scriptInvoker.Invoke("Run-MSBuild | Out-String");
var psResults = scriptInvoker.Invoke("Run-MSBuild | Out-Null");
var psResults = scriptInvoker.Invoke("Run-MSBuild");

请注意,无论是否使用输入对象,潜在问题仍然存在。

我还研究过使用自定义 PSHost(基于此示例:http://blogs.msdn.com/daiken/archive/2007/06/22/hosting-windows-powershell-sample-code.aspx),但在调试过程中我无法看到对其进行的任何“有趣”调用。

Stackoverflow 的优点和优点是否有任何见解可以挽救我的理智?

【问题讨论】:

  • 您能否提供更多关于您在何处定义 $input 的详细信息?另外,查看您的 Run-MSBuild 函数,我没有遵循它应该如何允许管道输入 ($input|run-msbuild)。
  • 嗨,马可,感谢您的评论。我已经添加了一些关于此的更多信息,但是,无论是否使用输入对象,问题都存在。

标签: powershell msbuild


【解决方案1】:

我可以让以下代码工作,但我收到一条警告说 MSBUILD 引擎想要在 STA 线程上运行。不幸的是,PowerShell 引擎创建的用于执行脚本的线程是 MTA。也就是说,我的小样本有效:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
using System.Collections;

namespace RunspaceInvokeExp
{
    class Program
    {
        static void Main()
        {
            var script = @"
function Run-MSBuild 
{ 
    [System.Reflection.Assembly]::LoadWithPartialName(""Microsoft.Build.Engine"") 

    $engine = New-Object Microsoft.Build.BuildEngine.Engine 
    $engine.BinPath = ""C:\Windows\Microsoft.NET\Framework\v3.5"" 

    $project = New-Object Microsoft.Build.BuildEngine.Project($engine, ""3.5"") 
    $project.Load(""deploy.targets"") 
    $project.InitialTargets = ""DoStuff"" 

    # Process the input object 
    while ($input.MoveNext()) 
    { 
        # Set MSBuild Properties & Item 
    } 

    # Optionally setup some loggers (have also tried it without any loggers) 
    $consoleLogger = New-Object Microsoft.Build.BuildEngine.ConsoleLogger 
    $engine.RegisterLogger($consoleLogger) 

    $fileLogger = New-Object Microsoft.Build.BuildEngine.FileLogger 
    $fileLogger.Parameters = ""verbosity=diagnostic"" 
    $engine.RegisterLogger($fileLogger) 

    # Run the build - this is the line that throws a CmdletInvocationException 
    $result = $project.Build() 

    $engine.Shutdown() 
} 
";
            using (var invoker = new RunspaceInvoke())
            {
                invoker.Invoke(script);
                IList errors;
                Collection<PSObject> results = invoker.Invoke(@"$input | Run-MSBuild", new[] {0}, out errors);
                Array.ForEach<PSObject>(results.ToArray(), Console.WriteLine);
            }
        }
    }
}

C# 代码的哪一行失败了?另外,您能否发布异常中的一些细节。您可以通过执行以下操作来解决 MTA 线程问题:

using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace RunspaceInvokeExp
{
    class Program
    {
        [STAThread]
        static void Main()
        {
            var script = @"
function Run-MSBuild 
{ 
    [System.Reflection.Assembly]::LoadWithPartialName(""Microsoft.Build.Engine"") 

    $engine = New-Object Microsoft.Build.BuildEngine.Engine 
    $engine.BinPath = ""C:\Windows\Microsoft.NET\Framework\v3.5"" 

    $project = New-Object Microsoft.Build.BuildEngine.Project($engine, ""3.5"") 
    $project.Load(""deploy.targets"") 
    $project.InitialTargets = ""DoStuff"" 

    # Process the input object 
    while ($input.MoveNext()) 
    { 
        # Set MSBuild Properties & Item 
    } 

    # Optionally setup some loggers (have also tried it without any loggers) 
    $consoleLogger = New-Object Microsoft.Build.BuildEngine.ConsoleLogger 
    $engine.RegisterLogger($consoleLogger) 

    $fileLogger = New-Object Microsoft.Build.BuildEngine.FileLogger 
    $fileLogger.Parameters = ""verbosity=diagnostic"" 
    $engine.RegisterLogger($fileLogger) 

    # Run the build - this is the line that throws a CmdletInvocationException 
    $result = $project.Build() 

    $engine.Shutdown() 
} 

Run-MSBuild
";

            Runspace runspace = RunspaceFactory.CreateRunspace();
            Runspace.DefaultRunspace = runspace;
            runspace.Open();

            EngineIntrinsics engine = runspace.SessionStateProxy.
                GetVariable("ExecutionContext") as EngineIntrinsics;
            ScriptBlock scriptblock = 
                engine.InvokeCommand.NewScriptBlock(script);
            Collection<PSObject> results = scriptblock.Invoke(new[] { 0 });
            Array.ForEach<PSObject>(results.ToArray(), Console.WriteLine);

            runspace.Close(); // Really should be in a finally block
        }
    }
}

【讨论】:

  • 嗨,Keith,感谢您抽出宝贵时间。我更新了示例以说明如何在脚本中使用输入对象,但是,我应该指出,即使不使用输入对象也会发生异常。
  • 感谢 STA 提示,我之前在 Windows Installer 中托管 MSBuild 时看到过该警告,想知道您将如何解决它!我也可以让您的样品工作(具有讽刺意味的是我的样品!)。我开始认为这整个问题都不是问题,这是由于我在这方面的各个方面都受到了抨击。让示例正常工作后,我重新访问了我的实际脚本,发现了一个流氓 Write-Host,我必须在之前的故障排除后忘记删除它 - DOH!无论如何,我会接受你的回答 - 非常感谢你的努力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 2020-11-18
相关资源
最近更新 更多