2

I'd like to see the "live" output of an application when I call it.

When I call it in a function, it works properly, as if I would call it directly. I can cancel the application with Ctrl+C and the "progress bar" the application outputs is updated:

function Test()
{
    dism.exe /online /Cleanup-Image /AnalyzeComponentStore
}

Test
[===                        5.5%                           ]

When I call it in a method however, there is no output visible. I can work around that by writing & dism.exe /online /Cleanup-Image /AnalyzeComponentStore 2>&1 | Write-Host but for this example, it would not update the progress bar but instead write a new line whenever there's a progress update:

class TestC
{
    Test()
    {
        & dism.exe /online /Cleanup-Image /AnalyzeComponentStore 2>&1 | Write-Host
    }
}

$testC = [TestC]::new()
$testC.Test()
[=                          3.3%                           ]

[===                        5.5%                           ]

I also cannot cancel the application with Ctrl+C, instead only the script would be cancelled and the called application continues in the background.

Is there an explanation for this? And a working workaround? :)

Thank you!

stxacc
  • 73
  • 1
  • 7
  • Good question, but I'd be surprised if Ctrl-C didn't also terminate the external program when called from a method. – mklement0 May 10 '20 at 12:53

2 Answers2

4

You can use the Start-Process cmdlet with the -NoNewWindow parameter:

Start-Process dism.exe -ArgumentList "/online /Cleanup-Image /AnalyzeComponentStore" -NoNewWindow -Wait
stackprotector
  • 10,498
  • 4
  • 35
  • 64
  • What's a bit unfortunate is that when I cancel the process with Ctrl+C, it continues with the output after the script has already ended: `PS C:\Users\Administrator> C:\APPS\Scripts\Tools.ps1 Deployment Image Servicing and Management tool Version: 10.0.17763.771 Image Version: 10.0.17763.1131 [========= 16.4% ] PS C:\Users\Administrator> Error: 1223 Operation was incomplete because of a cancel request. The DISM log file can be found at C:\Windows\Logs\DISM\dism.log` – stxacc May 10 '20 at 19:40
3

To complement Thomas' helpful answer with some background information:

You cannot use direct invocation in PowerShell classes if you want to invoke external console applications with pass-through display output.

Methods produce (success) output only via the return statement (and to report errors via throw).

Note that - both with your Write-Host attempt and with Start-Process -NoNewWindow (which is the only way to pass output through to the display, albeit invariably) - you won't be able to capture or redirect the external program's output.[1]

If you do want to return an external program's output as data from a class method, you'll have to define it as follows:

class TestC
{
  [string[]] Test()
  {
    return dism.exe /online /Cleanup-Image /AnalyzeComponentStore 2>&1
  }
}

Note the [string[]] return type and the use of return.

This outputs the external program's output lines in a streaming fashion to the caller, allowing further programmatic processing.

Of course, outputting progress messages this way won't be useful.


[1] Technically, you can capture Write-Host output since PowerShell 5, namely via the information stream (6), which in your case would require $output = & { $testC.Test() } 6>&1 - see this answer.

mklement0
  • 382,024
  • 64
  • 607
  • 775