Как устранить неполадку «Исключение System.Management.Automation.CmdletInvocationException» - PullRequest
0 голосов
/ 14 апреля 2010

Кто-нибудь знает, как лучше определить конкретную причину этого исключения?

Рассмотрим службу WCF, которая должна использовать удаленное взаимодействие Powershell 2.0 для выполнения MSBuild на удаленных машинах. В обоих случаях среды сценариев вызываются внутрипроцессными (через C # для Powershell и через Powershell для MSBuild), а не «обстрелом» - это было конкретное решение, чтобы избежать ада командной строки и разрешить передачу актуальные объекты в сценарий Powershell.

Ниже показана сокращенная версия скрипта Powershell, который вызывает MSBuild:

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 #, он завершается с ошибкой, описанной выше.

Код C #, используемый для вызова Powershell, показан ниже (функциональность удаленного взаимодействия удалена для простоты):

// 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 какое-то понимание, которое могло бы спасти мое здравомыслие?

1 Ответ

2 голосов
/ 14 апреля 2010

Я могу заставить следующий код работать, но я получаю предупреждение о том, что движок 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
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...