Page 2 of 4

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

Posted: Wed Aug 08, 2018 11:03 am
by jvierra
Here is how to make a full async read work in a form.

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

Posted: Wed Aug 08, 2018 11:17 am
by davidc
If you don't set the $process.SynchronizingObject property, it will crash PowerShell. That was the issue I was having before.

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

Posted: Wed Aug 08, 2018 11:55 am
by jvierra
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:

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

Posted: Thu Aug 09, 2018 3:48 pm
by ALIENQuake
Guys that looks amazing, let me digg into it and post back :shock:

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

Posted: Sat Aug 11, 2018 5:54 am
by ALIENQuake
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.

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

Posted: Tue Aug 14, 2018 8:10 am
by davidc
FYI, I plan to will integrate this into the Process Tracker Control Set in an upcoming service build.

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

Posted: Fri Aug 31, 2018 3:23 pm
by ALIENQuake
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 ?

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

Posted: Fri Aug 31, 2018 3:37 pm
by jvierra
You can use WMI to get an async notification of a process exit.

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

Posted: Fri Aug 31, 2018 3:41 pm
by jvierra
The following can also be done in PowerShell:
  1. static Task<int> RunProcessAsync(string fileName){
  2.     var tcs = new TaskCompletionSource<int>();
  3.  
  4.     var process = new Process {
  5.         StartInfo = { FileName = fileName }, EnableRaisingEvents = true
  6.     };
  7.  
  8.     process.Exited += (sender, args) => {
  9.         tcs.SetResult(process.ExitCode);
  10.         process.Dispose();
  11.     };
  12.  
  13.     process.Start();
  14.  
  15.     return tcs.Task;
  16. }

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

Posted: Sat Sep 01, 2018 4:17 am
by ALIENQuake
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?