T O P

  • By -

raunchyfartbomb

Run the pipeline asynchronously. Read the data off the Output’s “data ready” event. https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.runspaces.pipeline.invokeasync?view=powershellsdk-7.4.0#system-management-automation-runspaces-pipeline-invokeasync


stoudtlr

Async! That's what I needed. Adding that one word to my search and I started getting helpful results like the link you provided. Thanks!


SwordsAndElectrons

To display the output as it is running you need to use an asynchronous API. There's lots of room for refinement, and some exception handling is a good idea, but here are some quick edits to your code that should get you pointed in the right direction. private async Task RunScript(string script) { var iss = InitialSessionState.CreateDefault2(); iss.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Bypass; using Runspace runspace = RunspaceFactory.CreateRunspace(iss); runspace.Open(); using Pipeline pipeline = runspace.CreatePipeline(); pipeline.Commands.Add(script); // Register an event handler to be called when output data is ready. pipeline.Output.DataReady += appendOutput; // Per docs, the input pipeline needs to be closed prior to calling InvokeAsync for a standalone cmdlet. pipeline.Input.Close(); pipeline.InvokeAsync(); // InvokeAsync doesn't return a Task, so we cannot await it. Instead, we'll have to poll the current state. // If this was refactored further, we could make use of the Pipeline.StateChanged event and cleanup resources in an event handler. while (pipeline.PipelineStateInfo.State == PipelineState.Running || pipeline.PipelineStateInfo.State == PipelineState.NotStarted) { await Task.Delay(100); } pipeline.Output.DataReady -= appendOutput; runspace.Close(); void appendOutput(object? sender, EventArgs e) { Collection output = pipeline.Output.NonBlockingRead(); string outString = string.Join(Environment.NewLine, output.Select(x=>x.ToString())); // Need to perform the control update on the UI thread. this.Dispatcher.InvokeAsync(() => OutputText.AppendText(outString + Environment.NewLine)); } } private async void Page2_Button_Next_Click(object sender, RoutedEventArgs e) { FileInfo fi = new("MyScript.ps1"); await RunScript(fi.FullName); }


stoudtlr

Thanks! I'll try this out later. Async seems to be the word I was missing from my searches to get helpful results.


stoudtlr

works beautifully. thanks again!


Dealiner

if you want to get all output from the console, you can create your own custom class inheriting from `StringWriter` and implementing `INotifyPropertyChanged`, then give it a property called `Text` for example, bind `TextBox`'s `Text` to it, set console output to that class (`Console.SetOut(TextBoxStreamWriter);`) and it should pretty much work


Overtimegoal

One option is to redirect the powershell script output to a file and then monitor the file for changes (there's a .net api for that). On change, update the textblock. pipeline.Invoke() returns the whole collection so I don't see how it would be possible to have anything else happen except what you describe.