再次感谢您对我之前问题的帮助,但我遇到了另一个问题。如上所述,下面代码中的exe如果在cmd中运行,则在运行时立即输出进度。
然而,表单中的文本框是空白的,并且有一个延迟,在整个输出粘贴到框中之前看起来什么都没有发生。
我在网上看了一下,提到了一个表格。应用程序的do events方法,但不建议使用,而且有点草率。
你知道我该怎么直播吗?我确实尝试了一个消息框,但我需要在exe运行之前关闭它,我仍然需要等待。
我指的是xiso_build函数中xtrace -iso.exe的文本框输出
代码:
Function xiso_build {
Set-Location -Path $PSScriptRoot # change to root folder of this script wherever it's run from
[System.Windows.Forms.Messagebox]::Show("Building, Please Wait...")
$outputBox.text= & .extract-xiso.exe -r $selected_file 2>&1 | out-string # '2>&1' needs to be there otherwise any errors get outputted to terminal, out-string for better formatting
}
##########################################################################################################################
# the main form
$form = New-Object System.Windows.Forms.Form
$form.StartPosition = 'CenterScreen'
$form.Text = 'Xbox Iso Extractor'
$form.Size = '600,600'
# Choose iso label
# Create a "computer name" label control and add it to the form
# Set label location, text, size, etc
$Label1 = New-Object Windows.Forms.Label
$label1.Font = [System.Drawing.Font]::new("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
$Label1.Size = '180,40'
$Label1.Location = '10,20'
$Label1.Text = "Select An Xbox ISO:"
$Label1.Font.Bold
$form.Controls.Add($Label1)
# textbox
$isotextBox = New-Object System.Windows.Forms.TextBox
$isotextBox.Location = '10,60'
$isotextBox.Size = '320,200'
$form.Controls.Add($isotextBox)
# open file button
$Select_Iso_button = New-Object System.Windows.Forms.button
$Select_Iso_button.Text = 'Choose ISO'
$Select_Iso_button.Size = '100,25'
$Select_Iso_button.Location = '350,60'
$form.controls.Add($Select_Iso_button)
# below code: on click run 'iso_open func above and run global '$selected_file_path' variable from fun, then insert path and file into textbox
# save this selected text into var called $selected_file then execute var
$Select_Iso_button.Add_Click({iso_open; $global:selected_file = $isotextBox.Text = $selected_file_path; $selected_file})
# Output of xtract-iso textbox
$outputBox = New-Object System.Windows.Forms.TextBox #creating the text box
$outputBox.Location = '10,150' #location of the text box (px) in relation to the primary window's edges (length, height)
$outputBox.Size = New-Object System.Drawing.Size(565,200) #the size in px of the text box (length, height)
$outputBox.MultiLine = $True #declaring the text box as multi-line
$outputBox.ScrollBars = "Vertical" #adding scroll bars if required
$form.Controls.Add($outputBox) #activating the text box inside the primary window
# Build Iso Button
$build_button = New-Object System.Windows.Forms.button
$build_button.Text = 'Build ISO'
$build_button.Size = '200,50'
$build_button.Location = '10,360'
# $button.Anchor = 'Bottom,left' # uncomment to move button down to bottom left of app window
$form.Controls.Add($build_button)
$build_button.Add_Click({xiso_build}) # run 'xiso_build' func from above
默认情况下,Out-String
首先收集所有输入,然后将输入对象的格式化表示输出为单个多行字符串。[1]
为了从正在执行的命令中获得近乎实时的反馈,您需要迭代地向$output.Text
添加,当变得可用时,您可以通过ForEach-Object
调用:
$sep = ''; $outputBox.text = ''
& .extract-xiso.exe -r $selected_file 2>&1 |
ForEach-Object {
# Append the line at hand to the text box.
$outputBox.AppendLine($sep + $_); $sep = "`n"
# Keep the form responsive - THIS MAY NOT BE ENOUGH - see below.
[System.Windows.Forms.Application]::DoEvents()
}
[System.Windows.Forms.Application]::DoEvents()
[2]用于在命令执行时保持表单响应,但请注意,此依赖于由.extract-xiso.exe
调用生成的连续输出行以相对较短的连续发出(原因是表单不能处理任何用户事件,而PowerShell正在等待发出下一个输出行)。
如果这不能保持你的表单足够响应,你需要一个不同的方法,例如使用后台作业来运行可执行文件,使用.Show()
而不是.ShowDialog()
来显示表单,随后使用手动[System.Windows.Forms.Application]::DoEvents()
循环定期轮询后台作业以获取新的输出行。
下面的简化的、自包含的示例演示这个方法:
Add-Type -AssemblyName System.Windows.Forms
# === Helper functions:
# Launches the external program in a a background job and store
# the job-inf variable in script-level variable $job.
function Start-ExtractionJob {
$script:job = Start-Job {
# Simulate a long-running call to an external program.
# Here is where you'd make your & .extract-xiso.exe ... call
1..20 | % { cmd /c "echo line $_" 2>&1; Start-Sleep 1 }
}
}
# Adds an output line to the $ouputBox text box
function Add-TextBoxLine {
param([string] $line)
if ($prevLen = $outputBox.Text.Length) {
$sep = "`r`n"
}
else {
$sep = ''
}
$outputBox.AppendText($sep + $line)
# Scroll to the *start* of the new line to prevent horizontal scrolling.
$outputBox.Select($prevLen + $sep.Length, 0)
}
# === Create the form.
$form = [System.Windows.Forms.Form] @{
StartPosition = 'CenterScreen'
Text = 'Xbox Iso Extractor'
Size = '600,600'
}
# Mutiline text box that will receive the program's output, line by line.
$outputBox = [System.Windows.Forms.TextBox] @{
Location = '10,150'
Size = [System.Drawing.Size]::new(565, 200)
MultiLine = $true
ScrollBars = "Vertical" #adding scroll bars if required
}
$form.Controls.Add($outputBox)
# Build Iso Button
$build_button = [System.Windows.Forms.Button] @{
Text = 'Build ISO'
Size = '200,50'
Location = '10,360'
}
$form.Controls.Add($build_button)
# Set up the button event handler for launching
# the external program in a background job.
$build_button.Add_Click({
$this.Enabled = $false
$outputBox.Clear()
Add-TextBoxLine "=== Launching..."
Start-ExtractionJob
})
# --- Manage display of the form and handle job output.
$job = $null
$form.Show() # Show form asynchronously.
while ($form.Visible) {
# While the form is visible, process events periodically.
[System.Windows.Forms.Application]::DoEvents()
# If a job has been launched, collect its output on an ongoing basis.
# If the job has completed, remove it, and reenable the launch button.
if ($job) {
$job | Receive-Job | ForEach-Object {
# $outputBox.Text += $nl + $_; $outputBox.Select($outputBox.Text.Length + $nl.Length, 0); $outputBox.ScrollToCaret()
Add-TextBoxLine $_
}
# Check if the job has terminated, for whatever reason.
if ($job.State -in 'Completed', 'Failed') {
$job | Remove-Job; $job = $null
Add-TextBoxLine '=== Completed'
$build_button.Enabled = $true
}
# Sleep a little.
Start-Sleep -Milliseconds 100
}
}
# ... clean up job, if necessary, ...
[1]虽然有一个-Stream
开关输出格式化的行,这在这里没有帮助,因为实时反馈的关键是在行可用时迭代地分配给$output.Text
。[2][System.Windows.Forms.Application]::DoEvents()
通常会有问题(这实际上是阻塞.ShowDialog()
调用在幕后循环中所做的事情),但在这个受限的场景中(假设只有一个形式要显示),它应该是好的。