2014-12-02 1 views
1

Я изучаю использование Runspaces для параллельного запуска. В настоящее время я пытаюсь вернуть сообщения из любого сценария, который я мог бы запустить, чтобы помочь мне диагностировать проблемы в том же поточном типе, из которого они происходят: подробные сообщения в потоке Verbose и т. Д. До сих пор я могу получить предупреждение и Подробные сообщения, но всякий раз, когда я пытаюсь использовать Write-Error (или даже просто получить доступ к свойствам объекта ErrorRecord), консоль блокируется.Почему я не могу написать ошибку из PowerShell.Streams.Error.add_DataAdded?

В конце этого примера есть образец, который продемонстрирует то, что я вижу. В настоящее время сценарий производит четыре сообщения:

VERBOSE: This is a verbose message. 
WARNING: This is a warning message. 
System.Management.Automation.ErrorRecord 
This is an error message. 

Если вы раскомментировать любого из комментариев в $ps1.Streams.Error.add_DataAdded({}) ScriptBlock он запирает в этой точке. Может ли кто-нибудь объяснить, почему и/или дать мне обходное решение/исправить?

Я после того, как что-то вроде этого:

VERBOSE: This is a verbose message. 
WARNING: This is a warning message. 
C:\Work\me\PowerShell\Test-ReadPSDataStreams.ps1 : This is an error message. 
At line:1 char:1 
+ .\Test-ReadPSDataStreams.ps1 
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    + CategoryInfo   : NotSpecified: (:) [Write-Error], WriteErrorException 
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-ReadPSDataStreams.ps1 

Это сценарий:

$VerbosePreference = 'Continue' 

$is1 = [InitialSessionState]::CreateDefault() 

$rs1 = [RunspaceFactory]::CreateRunspace($is1) 
$rs1.ApartmentState = 'STA' 
$rs1.ThreadOptions = 'ReuseThread' 
$rs1.Open() 

$ps1 = [PowerShell]::Create() 
$ps1.Runspace = $rs1 
$ps1.Streams.Verbose.add_DataAdded({ 
    Param (
     [Object]$sender, 
     [System.Management.Automation.DataAddedEventArgs]$e 
    ) 

    foreach ($item in $sender.ReadAll()) { 
     Write-Verbose $item.Message 
    } 
}) 

$ps1.Streams.Warning.add_DataAdded({ 
    Param (
     [Object]$sender, 
     [System.Management.Automation.DataAddedEventArgs]$e 
    ) 

    foreach ($item in $sender.ReadAll()) { 
     Write-Warning $item.Message 
    } 
}) 

$ps1.Streams.Error.add_DataAdded({ 
    Param (
     [Object]$sender, 
     [System.Management.Automation.DataAddedEventArgs]$e 
    ) 

    foreach ($item in $sender.ReadAll()) { 
     Write-Host $item.GetType() 
     Write-Host $item.ToString() 
     #Write-Host $item.ErrorDetails.Message 
     #Write-Error 'test' 
     #Write-Error $item.ToString() 
    } 
}) 

[void]$ps1.AddScript({ $VerbosePreference = 'Continue' }) 
[void]$ps1.AddScript({ Write-Verbose 'This is a verbose message.' }) 
[void]$ps1.AddScript({ Write-Warning 'This is a warning message.' }) 
[void]$ps1.AddScript({ Write-Error 'This is an error message.' }) 
$ps1.Invoke() 

Если есть другой способ сделать это, я открыт к нему!

+0

Не очень понимаю, что вы делаете, или если этого достаточно, но 'Write-хоста $ item.Exception.Message' внутри вашей проблемы блок, кажется, работает – arco444

+0

Я делаю это потому, что в противном случае эти сообщения не представлены разработчику (т.е. мне), чтобы помочь мне отлаживать. То, что вы предложили, работает, но я пытался его запустить в поток ошибок, например. используя Write-Error. Я уточню это в вопросе. – user2871239

ответ

1

Смотрите, если это то, что может помочь:

Смотрите, если это помогает: http://mjolinor.wordpress.com/2014/06/03/invoke-scritptasync-v2/

Если я правильно читал этот вопрос, он предназначен именно для сценария вы описываете - тестирование сценариев в среда выполнения и возможность записывать диагностическую информацию в различные выходные потоки во время выполнения, а затем извлекать эту информацию после завершения выполнения и размещенных пробелов.

+0

Это не совсем то, что я ищу. Я разорвал это из исходного кода, чтобы избежать затенения проблемы. Я не ответил ранее, потому что я все еще работаю над последствиями вашего кода для своей работы. Я поддержал это, потому что это помогло мне. – user2871239

+0

Что он делает, что вам нужно? – mjolinor

+0

Я делаю UI вещи с WPF и используя Runspace для запуска фоновых потоков, чтобы поддерживать пользовательский интерфейс. Я столкнулся с проблемами, и я пытаюсь вернуть диагностический материал на главный сеанс PowerShell, чтобы я мог понять, что происходит, когда это происходит. – user2871239

2

Предисловие

Я пишу это на моем планшете, поэтому, пожалуйста, простите меня, если форматирование не выходит правильно. Особенно важно отметить: у моей клавиатуры планшета нет обратного тика, который PowerShell использует для своего escape-символа; таким образом, я написал последовательность строк в примерах как «\ n».

моего решение

Я играл с техникой, и это то, что я заметил:

(1) Для того, чтобы работать вокруг потока ошибок не закрытие, доступ ошибок фонового хоста через переменный $ Error вместо. Для этого используйте синхронизированную хеш-таблицу в качестве SessionStateProxy для обеспечения доступа к $ Error; см. пример ниже.

→ $PRXY = [HashTable]::Synchronised(@{}) 

$rs1 = [RunspaceFactory]::CreateRunspace() 
→ $rs1.SessionStateProxy.SetVariable("PRXY",$PRXY) 
$rs1.Open() 

$ps1 = [PowerShell]::Create().AddScript({ 
→ $PRXY.Error = $Error 
    Write-Error "This is just a test from ps1" 
}) 
$ps1.Runspace = $rs1 

Хорошая статья о том, как использовать синхронизированные HashTables с многопоточности можно найти здесь: http://learn-powershell.net/2013/04/19/sharing-variables-and-live-objects-between-powershell-runspaces/

Другие вещи

(2) Использование Write-Error Чтобы отправить сообщение об ошибке на фоне пространства выполнения в на интерактивную консоль будет перезаписывать InvocationInfo ошибки с InvocationInfo командлета Write-Error. Из-за этого я отправляю объект ошибки непосредственно на вывод консоли.

(3) Использование обработчиков событий дало мне проблемы.В качестве работы я использовал Register-ObjectEvent и использовал опрос событий в цикле while, чтобы поймать сообщения из фоновых пробелов.

(4) Использование потока. [Type] .ReadAll() с: warning, verbose и debug заставляет хост зависать таким же образом, как и попытка чтения из потока ошибок. Чтобы обойти это, я отправил содержимое потока в конвейерный цикл ForEach, а затем вызвал метод Clear() потоков.

Полный пример

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

# Turn on verbose and debug messages 
$VerbosePreference = "Continue" 
$DebugPreference = "Continue" 

# Create background runspaces 
$PRXY = [HashTable]::Synchronised(@{}) 

$rs = @() 
For ($i = 0; $i -le 1; $i++) 
{ 
    $rs += [RunspaceFactory]::CreateRunspace() 
    $rs[$i].SessionStateProxy.SetVariable("PRXY", $PRXY) 
    $rs[$i].Open() 
} 

$sb1 = { 
    $PRXY.PS1.Error = $Error 
    $VerbosePreference = "Continue" 
    $DebugPreference = "Continue" 
    For ($i = 0; $i -lt 5; $i++) 
    { 
     $msg = "Test [$i]" 
     Write-Error $msg 
     Write-Warning $msg 
     Write-Verbose $msg 
     Write-Debug $msg 
     Start-Sleep -Milliseconds 750 
    } 
} 
$sb2 = { 
    $PRXY.PS2.Error = $Error 
    $VerbosePreference = "Continue" 
    $DebugPreference = "Continue" 
    For ($i = 0; $i -lt 5; $i++) 
    { 
     $msg = "Test [$i]" 
     Write-Error $msg 
     Write-Warning $msg 
     Write-Verbose $msg 
     Write-Debug $msg 
     Start-Sleep -Milliseconds 500 
    } 
} 
$PRXY.PS1 = @{} 
$ps1 = [PowerShell]::Create().AddScript($sb1) 
$ps1.Runspace = $rs[0] 

$PRXY.PS2 = @{} 
$ps2 = [PowerShell]::Create().AddScript($sb2) 
$ps2.Runspace = $rs[1] 

# Map event SourceIdentifiers to the runspace that produces the event 
$EventRegister = @{} 
$EventRegister.Error = @{} 
$EventRegister.Warning = @{} 
$EventRegister.Verbose = @{} 
$EvevtRegister.Debug = @{} 
$Registered = @() 

# Register PS1 -------------------- 
Register-ObjectEvent -InputObject $ps1.streams.error -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Error.Add($id, $ps1) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps1.streams.warning -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Warning.Add($id, $ps1) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps1.streams.verbose -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Verbose.Add($id, $ps1) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps1.streams.debug -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Debug.Add($id, $ps1) 
     $Registered += $id 
    } 
} 

# Register PS2 ----------------------- 
Register-ObjectEvent -InputObject $ps2.streams.error -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Error.Add($id, $ps2) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps2.streams.warning -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Warning.Add($id, $ps2) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps2.streams.verbose -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Verbose.Add($id, $ps2) 
     $Registered += $id 
    } 
} 
Register-ObjectEvent -InputObject $ps2.streams.debug -EventName DataAdded 
ForEach ($id in (@(Get-EventSubscriber)).SourceIdentifier) 
{ 
    If ($id -notin $Registered) 
    { 
     $EventRegister.Debug.Add($id, $ps2) 
     $Registered += $id 
    } 
} 

$hndl_ps1 = $ps1.BeginInvoke() 
$hndl_ps2 = $ps2.BeginInvoke() 

While (!(hndl_ps1.IsCompleted) -or 
     !(hndl_ps2.IsCompleted)) 
{ 
    $Event = Wait-Event 
    If ($EventRegister.Error.ContainsKey($Event.SourceIdentifier)) 
    { 
     $psid = $EventRegister.Error[$Event.SourceIdentifier].InstanceId 
     $stamp = "$psid::$($Event.TimeGenerated)" 
     Write-Error $stamp 
     If ($psid -eq $ps1.InstanceId) 
     { 
      $PRXY.PS1.Error 
      $PRXY.PS1.Error.Clear() 
     } 
     If ($psid -eq $ps2.InstanceId) 
     { 
      $PRXY.PS2.Error 
      $PRXY.PS2.Error.Clear() 
     } 
     Remove-Event -EventIdentifier $Event.EventIdentifier 
     Continue 
    } 
    If ($EventRegister.Warning.ContainsKey($Event.SourceIdentifier)) 
    { 
     $stamp = "$($EventRegister.Warning[$Event.SourceIdentifier].InstanceId::" 
     $stamp += "$($Event.TimeGenerated)" 
     $EventRegister.Warning[$Event.SourceIdentifier].streams.warning | 
      ForEach {Write-Warning "{0}\n{1}\n\n" -f $stamp, $_} 
     $EventRegister.Warning[$Event.SourceIdentifier].streams.warning.Clear() 
     Remove-Event -EventIdentifier $Event.EventIdentifier 
     Continue 
    } 
    If ($EventRegister.Verbose.ContainsKey($Event.SourceIdentifier)) 
    { 
     $stamp = "$($EventRegister.Verbose[$Event.SourceIdentifier].InstanceId)::" 
     $stamp += "$($Event.TimeGenerated)" 
     $EventRegister.Verbose[$Event.SourceIdentifier].streams.verbose | 
      ForEach {Write-Verbose "{0}\n{1}\n\n" -f $stamp, $_} 
     $EventRegister.Verbose[$Event.SourceIdentifier].streams.verbose.Clear() 
     Remove-Event -EventIdentifier $Event.EventIdentifier 
     Continue 
    } 
    If ($EventRegister.Debug.ContainsKey($Event.SourceIdentifier)) 
    { 
     $stamp = "$($EventRegister.Debug[$Event.SourceIdentifier].InstanceId)::" 
     $stamp += "$($Event.TimeGenerated)" 
     $EventRegister.Debug[$Event.SourceIdentifier].streams.debug | 
      ForEach {Write-Debug "{0}\n{1}\n\n" -f $stamp, $_} 
     $EventRegister.Debug[$Event.SourceIdentifier].streams.debug.Clear() 
     Remove-Event -EventIdentifier $Event.EventIdentifier 
     Continue 
    } 
} 
$ps1.EndInvoke($hndl_ps1) 
$ps2.EndInvoke($hndl_ps2) 

# Optionally you can read the contents of all the streams after EndInvoke() 
# to see if any messages were missed. 
$ps1.streams.error 
$ps1.streams.warning.ReadAll() 
$ps1.streams.verbose.ReadAll() 
$ps1.streams.debug.ReadAll() 

$ps2.streams.error 
$ps2.streams.warning.ReadAll() 
$ps2.streams.verbose.ReadAll() 
$ps2.streams.debug.ReadAll() 

# Unregister subscribers if in the ISE 
Get-EventSubscriber | Unregister-Event -SourceIdentifier $_.SourceIdentifier 
Смежные вопросы