Предисловие
Я пишу это на моем планшете, поэтому, пожалуйста, простите меня, если форматирование не выходит правильно. Особенно важно отметить: у моей клавиатуры планшета нет обратного тика, который 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
Не очень понимаю, что вы делаете, или если этого достаточно, но 'Write-хоста $ item.Exception.Message' внутри вашей проблемы блок, кажется, работает – arco444
Я делаю это потому, что в противном случае эти сообщения не представлены разработчику (т.е. мне), чтобы помочь мне отлаживать. То, что вы предложили, работает, но я пытался его запустить в поток ошибок, например. используя Write-Error. Я уточню это в вопросе. – user2871239