2010-04-14 2 views
0

Кто-нибудь знает, как лучше всего определить конкретную основную причину этого исключения?Как устранить неполадки в «System.Management.Automation.CmdletInvocationException»

Рассмотрите службу WCF, которая должна использовать удаленную версию Powershell 2.0 для запуска MSBuild на удаленных компьютерах. В обоих случаях среды сценариев называются in-process (через 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 какое-либо понимание, которое может спасти мое здравомыслие?

+0

Можете ли вы предоставить более подробную информацию о том, где вы определили $ input? Кроме того, глядя на вашу функцию Run-MSBuild, я не буду следить за тем, как она должна допускать ввод конвейера ($ input | run-msbuild). –

+0

Привет, Марко, спасибо за комментарий. Я добавил дополнительную информацию об этом, однако проблема существует с использованием или без использования входного объекта. – JamesD

ответ

2

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

Привет Кит, спасибо, что нашли время. Я обновил образец, чтобы проиллюстрировать, как входной объект используется в скрипте, однако я должен указать, что исключение происходит даже тогда, когда не используется входной объект. – JamesD

+0

Спасибо за отзыв STA, я видел это предупреждение раньше, когда принимал MSBuild внутри установщика Windows и задавался вопросом, как вы можете решить его! Я тоже мог бы заставить ваш образец работать (мой тоже, по иронии!). Я начинаю думать, что весь этот вопрос не является проблемой, в результате моего издевательства над различными аспектами этого. После того, как я работал над образцом, я снова просмотрел свой реальный скрипт и обнаружил мошенник Write-Host, который я, должно быть, забыл удалить после более раннего устранения неполадок - DOH! Во всяком случае, я приму свой ответ - большое спасибо за ваши усилия. – JamesD

Смежные вопросы