Display output of the CMD in real time inside TextBox

Ask questions about creating Graphical User Interfaces (GUI) in PowerShell and using WinForms controls.
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
User avatar
jvierra
Posts: 12950
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Display output of the CMD in real time inside TextBox

Post by jvierra » Wed Aug 08, 2018 11:03 am

Here is how to make a full async read work in a form.
Attachments
Demo-Capture StandardOutAsync.psf
(17.2 KiB) Downloaded 28 times

User avatar
davidc
Posts: 5526
Joined: Thu Aug 18, 2011 4:56 am

Re: Display output of the CMD in real time inside TextBox

Post by davidc » Wed Aug 08, 2018 11:17 am

If you don't set the $process.SynchronizingObject property, it will crash PowerShell. That was the issue I was having before.
David
SAPIEN Technologies, Inc.

User avatar
jvierra
Posts: 12950
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Display output of the CMD in real time inside TextBox

Post by jvierra » Wed Aug 08, 2018 11:55 am

The exception is being missed by the scriptdriver. PowerShell CLI also misses the exception.
Faulting application name: scriptdriver64.exe, version: 1.4.48.0, time stamp: 0x5ae7594d
Faulting module name: KERNELBASE.dll, version: 10.0.17134.165, time stamp: 0xb0bb231d
Exception code: 0xe0434352
Fault offset: 0x000000000003a388
Faulting process id: 0x4dd0
Faulting application start time: 0x01d42f48f7837a64
Faulting application path: C:\Program Files (x86)\Common Files\SAPIEN Debugger 2018\scriptdriver64.exe
Faulting module path: C:\WINDOWS\System32\KERNELBASE.dll
Report Id: bd3e6bcf-7744-4b97-b1a6-c5b9b50365ce
Faulting package full name:
Faulting package-relative application ID:

User avatar
ALIENQuake
Posts: 27
Joined: Sun Mar 03, 2013 12:45 pm

Re: Display output of the CMD in real time inside TextBox

Post by ALIENQuake » Thu Aug 09, 2018 3:48 pm

Guys that looks amazing, let me digg into it and post back :shock:

User avatar
ALIENQuake
Posts: 27
Joined: Sun Mar 03, 2013 12:45 pm

Re: Display output of the CMD in real time inside TextBox

Post by ALIENQuake » Sat Aug 11, 2018 5:54 am

I did it! Now I con not only display progress in real time but also send input if application suddenly ask for it! I'm using:

Code: Select all

$buttonSendInput_Click={
	$process.StandardInput.WriteLine($textboxInput.Text)
}
and it works. I didn't know much about process.EnableRaisingEvents and $process.SynchronizingObject so thank you for you help.

User avatar
davidc
Posts: 5526
Joined: Thu Aug 18, 2011 4:56 am

Re: Display output of the CMD in real time inside TextBox

Post by davidc » Tue Aug 14, 2018 8:10 am

FYI, I plan to will integrate this into the Process Tracker Control Set in an upcoming service build.
David
SAPIEN Technologies, Inc.

User avatar
ALIENQuake
Posts: 27
Joined: Sun Mar 03, 2013 12:45 pm

Re: Display output of the CMD in real time inside TextBox

Post by ALIENQuake » Fri Aug 31, 2018 3:23 pm

davidc Thanks!

Do you think that it can solve yet another challenge? It's possible to wait for process exit?
Because I have two or more process, I want to execute them one by one (and at the same time, display output of the CMD in real time inside TextBox) and the next process has to wait for another because otherwise, it can't access/modify files in use. Sequence of the execution matters.

I can't use waitForExit() because it stops receiving output (works as design I think). Possible to achieve via extra Process Tracker or too good to be true?
I try to set $global:Monit = $true and wait for it to be false but usage of while of any kind [System.Threading.Thread]::Sleep(1000) is also preventing output/hang application.

Maybe I need to create new form for each process? Or add condition when there is no more OutputDataReceived ?
Last edited by ALIENQuake on Sat Sep 01, 2018 4:09 am, edited 1 time in total.

User avatar
jvierra
Posts: 12950
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Display output of the CMD in real time inside TextBox

Post by jvierra » Fri Aug 31, 2018 3:37 pm

You can use WMI to get an async notification of a process exit.

User avatar
jvierra
Posts: 12950
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Display output of the CMD in real time inside TextBox

Post by jvierra » Fri Aug 31, 2018 3:41 pm

The following can also be done in PowerShell:
  1. static Task<int> RunProcessAsync(string fileName){
  2.  
  3.     var tcs = new TaskCompletionSource<int>();
  4.  
  5.  
  6.  
  7.     var process = new Process {
  8.  
  9.         StartInfo = { FileName = fileName }, EnableRaisingEvents = true
  10.  
  11.     };
  12.  
  13.  
  14.  
  15.     process.Exited += (sender, args) => {
  16.  
  17.         tcs.SetResult(process.ExitCode);
  18.  
  19.         process.Dispose();
  20.  
  21.     };
  22.  
  23.  
  24.  
  25.     process.Start();
  26.  
  27.  
  28.  
  29.     return tcs.Task;
  30.  
  31. }

User avatar
ALIENQuake
Posts: 27
Joined: Sun Mar 03, 2013 12:45 pm

Re: Display output of the CMD in real time inside TextBox

Post by ALIENQuake » Sat Sep 01, 2018 4:17 am

jvierra wrote:
Fri Aug 31, 2018 3:37 pm
You can use WMI to get an async notification of a process exit.
This is what I've manage to create:

Code: Select all


$formAsyncPingerTest_Load = {
	
	$pstart = 'SELECT * FROM Win32_ProcessStartTrace'
	$pstop = 'SELECT * FROM Win32_ProcessStopTrace'
	
	$action = {
		$nevent = $Event.SourceEventArgs.NewEvent
		if ($nevent.ProcessName -eq 'ping.exe' -and $Event.SourceIdentifier -eq 'Process.Stop') {
			$global:processExited = $true
		}
		if ($nevent.ProcessName -eq 'nslookup.exe' -and $Event.SourceIdentifier -eq 'Process.Stop') {
			$global:processExited = $true
		}
	}
	Register-WmiEvent -Query $pstart -SourceIdentifier 'Process.Start' -Action $action
	Register-WmiEvent -Query $pstop -SourceIdentifier 'Process.Stop' -Action $action

}
$global:process_OutputReceived = {
	$global:processExited = $false
	$textbox1.Lines += $_.Data
	$textbox1.Select($textbox1.Text.Length, 0)
	$textbox1.ScrollToCaret()
}
$global:process_Exited = {
	$global:processExited = $true
	Write-Host 'Exited'
	Write-Host $global:processExited
	}

$buttonPing_Click = {


	'ping', 'ping' | % {
		
		$script:process = New-Object System.Diagnostics.Process
		$process.EnableRaisingEvents = $true
		$process.StartInfo.RedirectStandardError = $true
		$process.StartInfo.RedirectStandardOutput = $true
		$process.StartInfo.RedirectStandardInput = $true
		$process.StartInfo.FileName = $_
		$process.StartInfo.RedirectStandardOutput = $true
		$process.StartInfo.UseShellExecute = $false
		$process.StartInfo.CreateNoWindow = $true
		$process.StartInfo.Arguments = '-n 5 google.com'
		$process.add_OutputDataReceived($process_OutputReceived)
		$process.add_Exited($process_Exited)
		$process.SynchronizingObject = $formAsyncPingerTest
		
		$global:processExited = $false
		
		$process.Start() | Out-Null

		$process.BeginOutputReadLine()

		while ($global:processExited -eq $false) {
			[System.Threading.Thread]::Sleep(1000)
			Write-Host $global:processExited
		}
	}
	
}

$buttonStop_Click={
	#TODO: Place custom script here
	$process.CancelOutputRead()
	$process.Kill()
	$process.Dispose()
}

$formAsyncPingerTest_FormClosed=[System.Windows.Forms.FormClosedEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.FormClosedEventArgs]
	#TODO: Place custom script here
	$process.CancelOutputRead()
	$process.Kill()
	$process.Dispose()
	Get-EventSubscriber | Unregister-Event -Force
}
1. One button, two process names from array, each should be executed
2. at the start of the process, $global:processExited = $false
3. while $global:processExited = $false, wait before dealing with next object from array <-- this is where it hangs application
4. if OutputReceived , set $global:processExited = $false
5. if process_Exited, set $global:processExited = $true so while loop can continue

In theory, it should work but it hangs whole app and it doesn't display output in textbox. Without 'while' both process are executed at once. How to achieve this concept without using 'while'/blocking BeginOutputReadLine() ?
The following can also be done in PowerShell:
The c# static Task<int> RunProcessAsync code from you post is too complicated, maybe I will find powershell version of it but does it will suit my concept from above?

Locked