Как Receive-Job хранит правильные порядки записей - PullRequest
1 голос
/ 19 мая 2019

Я пытаюсь понять, как внутренне работает Receive-Job.В приведенном ниже коде я вижу, где объект Job хранит записи из разных пар:

$InformationPreference = 'SilentlyContinue'

$sb = {
    $VerbosePreference = 'Continue'
    $InformationPreference = 'Continue'
    $WarningPreference = 'Continue'

    Write-Warning 'warning1'
    Write-Information 'information1'
    Write-Warning 'warning2'
    Write-Information 'information2'
    Write-Verbose 'verbose1'
    Write-Information 'information3'
}

$job = Start-Job -ScriptBlock $sb | Wait-Job

# my messages are here:
$job.ChildJobs[0].Verbose.Count     # prints 1
$job.ChildJobs[0].Information.Count # prints 3, only InformationRecord has TimeGenerated property
$job.ChildJobs[0].Warning.Count     # prints 2

Receive-Job $job

# prints:
# WARNING: warning1
# information1
# WARNING: warning2
# information2
# VERBOSE: verbose1
# information3

Но как мне написать свою собственную версию Receive-Job и сохранить оригинальный порядок различных сообщений?Я пытался проверить исходный код, но он не имеет большого смысла:

https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/remoting/commands/ReceiveJob.cs

private void WriteJobResults(Job job)
{
    // ...

    Collection<PSObject> output = ReadAll<PSObject>(job.Output);
    foreach (PSObject o in output)
    {
        // ... 
        WriteObject(o);
    }

    Collection<ErrorRecord> errorRecords = ReadAll<ErrorRecord>(job.Error);
    foreach (ErrorRecord e in errorRecords)
    {
        // ...
        mshCommandRuntime.WriteError(e, true);
    }

    Collection<VerboseRecord> verboseRecords = ReadAll(job.Verbose);
    foreach (VerboseRecord v in verboseRecords)
    {
        // ...
        mshCommandRuntime.WriteVerbose(v, true);
    }

    // and so on for other streams...
}

1 Ответ

0 голосов
/ 19 мая 2019

Как заметил @ PetSerAl Я указал неверный код. Вот что на самом деле исполняется:

// extract results and handle them
Collection<PSStreamObject> results = ReadAll<PSStreamObject>(job.Results);

if (_wait)
{
    foreach (var psStreamObject in results)
    {
        psStreamObject.WriteStreamObject(this, job.Results.SourceId);
    }
}
else
{
    foreach (var psStreamObject in results)
    {
        psStreamObject.WriteStreamObject(this);
    }
}

К сожалению Results является внутренней собственностью. Я исследовал, как это работает, и ниже код:

$nonPublicInstance = [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Instance
$JobResultsProperty = $job.GetType().GetProperty('Results', $nonPublicInstance)

$results = $JobResultsProperty.GetValue($job.ChildJobs[0])

$PSStreamObjectValueProperty = [System.Management.Automation.Remoting.Internal.PSStreamObject].GetProperty('Value', $nonPublicInstance)

foreach ($result in $results)
{
    $value = $PSStreamObjectValueProperty.GetValue($result)
    switch ($result.ObjectType)
    {
        'Verbose' {
            Write-Output "Verbose Record: $value"
        }
        'Information' {
            Write-Output "Information Record: $value"
        }
        'WarningRecord' {
            Write-Output "Warning Record: $value"
        }
        'MethodExecutor' {
            Write-Output "MethodExecutor"
        }
    }
}

По какой-то причине ObjectType=Verbose нет в коллекции. Я предполагаю, что подробные записи каким-то образом извлекаются из MethodExecutor записей. Выход:

MethodExecutor
Warning Record: warning1
MethodExecutor
Information Record: information1
MethodExecutor
Warning Record: warning2
MethodExecutor
Information Record: information2
MethodExecutor
MethodExecutor
Information Record: information3
MethodExecutor

Я нахожу лучший способ получения результатов от работы, однако он будет работать только с вновь созданными работами. Код ниже добавляет обработчик события DataAdded в коллекции:

$job = Start-Job -ScriptBlock $sb

$records = New-Object 'System.Collections.Generic.List[System.String]'

Register-ObjectEvent -InputObject $job.ChildJobs[0].Verbose -EventName 'DataAdded' -Action { $records.Add('verbose: ' + $Sender[$EventArgs.Index]) }.GetNewClosure() | Out-Null
Register-ObjectEvent -InputObject $job.ChildJobs[0].Information -EventName 'DataAdded' -Action { $records.Add('information: ' + $Sender[$EventArgs.Index]) }.GetNewClosure() | Out-Null
Register-ObjectEvent -InputObject $job.ChildJobs[0].Warning -EventName 'DataAdded' -Action { $records.Add('warning: ' + $Sender[$EventArgs.Index]) }.GetNewClosure() | Out-Null

Wait-Job $job | Out-Null

$records | Format-List

Выход:

warning: warning1
information: information1
warning: warning2
information: information2
verbose: verbose1
information: information3
...