Updated 02-10-2011: Fixed a small bug that could make SmartProcess consume 100%
There is several ways you can spawn a new process from within PowerShell. Start-Process for instance. If you create a new object of type System.Diagnostics.Process or call Start-Process and get such an object back, you can now get access to the Standard Input/Standard Output … there is a “catch” thou. These only get updated every time the process sends a complete line including a newline.
So lets say you spawn nslookup and want to catch every time i t expects input, it wont work.
function StartProcess([String]$FileName){
$process = New-Object "System.Diagnostics.Process"
$startinfo = New-Object "System.Diagnostics.ProcessStartInfo"
$startinfo.FileName = $FileName
#$startinfo.Arguments = $arguments
#$startinfo.WorkingDirectory = $pwd.Path
$startinfo.UseShellExecute = $false
$startinfo.RedirectStandardInput = $true
$startinfo.RedirectStandardOutput = $true
$startinfo.RedirectStandardError = $false
#$startinfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
$startinfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal
$process.StartInfo = $startinfo
$temp = $process.start()
return $process
}
function GetPrompt([System.Diagnostics.Process]$process, [Int]$waitSec){
$str = ''
$i = 1
while( (!([String]$str).contains("> ")) -and (!$process.HasExited) -and ($i -le $waitSec) -and ($process.StandardOutput.Peek() -eq -1) ){
# Write-Host ("Peek: " + $process.StandardOutput.Peek())
while($process.StandardOutput.Peek() -ge 0){
$iChar = $process.StandardOutput.Read()
$str = ($str + [Convert]::ToChar($iChar))
}
if(!([String]$str).contains("> ")){ Write-Host "."; Start-Sleep -s 1; }
$i = $i + 1
}
return $str
}
if($true){
#[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
#[System.Windows.Forms.SendKeys]::SendWait("skadefro.dk")
if(!$process.HasExited){ Write-Host "Killing process"; $process.kill(); $process.StandardOutput.ReadToEnd() }
$process = StartProcess 'nslookup'
GetPrompt $process 2
$process.StandardInput.WriteLine('set q=MX')
GetPrompt $process 2
$process.StandardInput.WriteLine('skadefro.dk')
$process.StandardInput.Flush()
#$process.StandardOutput.ReadToEnd()
GetPrompt $process 2
}
if(!$process.HasExited){ $process.kill() }
So you could write some code and hook up to the ErrorDataReceived and OutputDataReceived events. but those have the same problem, so you need to dig a bit deeper. You need to take each stream object and hook up to BaseStream.BeginRead
So that is what I have done. Inside my SuperOffice Add in I have added a new command called New-SmartProcess
And now the above code will look cleaner and actually work
function WaitForPrompt([wingu.SmartProcess]$process, [Int]$waitSec){
$str = ''
$i = 1
while( (!($process.StandardOutput.contains("> ")) -and (!$process.HasExited) -and ($i -le $waitSec))){
if(!([String]$str).contains("> ")){ Write-Host "."; Start-Sleep -s 1; }
$i = $i + 1
}
return $str
}
if($process){ if(!$process.HasExited){ Write-Host "Killing process"; $process.kill(); $process.StandardOutput.ReadToEnd() } }
$process = New-SmartProcess 'nslookup'
WaitForPrompt $process 2
$process.StandardOutput
$process.StandardOutput = ''
$process.WriteLine('set q=MX')
WaitForPrompt $process 2
$process.StandardOutput
$process.StandardOutput = ''
$process.WriteLine('skadefro.dk')
WaitForPrompt $process 2
$process.StandardOutput
$process.StandardOutput = ''
if(!$process.HasExited){ $process.kill() }
You can find my SuperOffice PowerShell snapin inclucing Source code here
Ingen kommentarer:
Send en kommentar