Consider the following
Invoke-Command {
'a'; Write-Verbose 'a' -Verbose
'b'; Write-Verbose 'b' -Verbose
} |
. { process { Write-Verbose "downstream_$_" -Verbose } }
which outputs
VERBOSE: downstream_a
VERBOSE: a
VERBOSE: downstream_b
VERBOSE: b
Note that process{} of the second scriptblock is invoked immediately when the first emits an object.
On the other hand, consider a similar construction except replacing Invoke-Command with a custom compiled Cmdlet:
Add-Type `
-PassThru `
-TypeDefinition @'
using System.Management.Automation;
namespace n {
[Cmdlet(VerbsLifecycle.Invoke,"SomeScriptBlockThing")]
public class InvokeSomeScriptBlockThing : Cmdlet
{
[Parameter(Mandatory=true,Position=1)]
public ScriptBlock ScriptBlock { get; set; }
protected override void EndProcessing() {
var output = ScriptBlock.Invoke("some_fancy_argument");
foreach (var o in output) {
WriteObject(o);
}
}
}
}
'@ |
% Assembly |
Import-Module
Invoke-SomeScriptBlockThing {
param($SomeFancyArgument)
'a'; Write-Verbose 'a' -Verbose
'b'; Write-Verbose 'b' -Verbose
} |
. { process { Write-Verbose "downstream_$_" -Verbose } }
which outputs
VERBOSE: a
VERBOSE: b
VERBOSE: downstream_a
VERBOSE: downstream_b
Note that, in this case, all of the objects output from the first scriptblock are accumulated in output until the first scriptblock completes, then all objects are output all at once.
The real challenge I'm solving here is to obtain a CancellationToken using the technique demonstrated here. For that technique, the invocation of the Cmdlet must outlive the token. The natural way to ensure that is to have the Cmdlet make the token available (where $SomeFancyArgument appears here) to a scriptblock it invokes. But with the implementation shown, the Cmdlet doesn't emit anything until the scriptblock completes.
How can I make the execution order of my Cmdlet match that of the first example? That is, how can I make my Cmdlet output each object as it is produced by the scriptblock it invokes?
Note that this old question and answer is, arguably, a duplicate but it predates open-sourcing of PowerShell and so didn't have the advantages of seeing how, for example, Invoke-Command Cmdlet achieves this goal. The only answer to that question accomplishes the goal by converting the scriptblock to a string and spinning up an entirely separate PowerShell instance which, among other things, invokes the script in an entirely separate SessionState without access to any of the parent scopes. It's the same question, but from another era.
C#code is above my head 🤪, I can hardly read it, let alone write something like you did...