使用.ShowDialog() 方法调用表单模态,这意味着您的PowerShell脚本的执行被阻塞(无响应)直到表单关闭。
因此,您必须:
-
使用.Show() 方法以非模态方式显示表单,这样可以确保您的PowerShell脚本继续执行。
- 这反过来又要求您进入一个循环,在其中定期调用
[System.Windows.Forms.Application]::DoEvents(),以确保表单保持响应。
-
为了确保在调用.Show() 时表单不会获得焦点,您必须子类 Forms class 以便覆盖ShowWithoutActivation property,正如你所发现的那样。
-
警告:如果您还想为表单设置.TopMost = $true,为了使表单始终显示在其他窗口的顶部,解决方法是在各种主机环境中可靠运行所必需的 - 请参阅底部部分。
把它们放在一起:
- 注意:启动脚本后按 Ctrl-C 将终止它并关闭表单。这个工作的事实证明调用者的窗口保持焦点。
# Derive a custom form class from System.Windows.Forms.Form
# that doesn't activate itself when loaded.
Add-Type -ReferencedAssemblies System.Windows.Forms, System.ComponentModel.Primitives @'
public class MyForm: System.Windows.Forms.Form {
protected override bool ShowWithoutActivation { get { return true; } }
}
'@ -WarningAction Ignore
# Create an instance of the custom form class.
$form = [MyForm]::new()
# Show the form *non-modally*, with .Show() rather than
# .ShowDialog(), which is the prerequisite for not blocking this script.
$form.Show()
# Perform operations while the form is being shown.
try {
do {
# Process form events.
[System.Windows.Forms.Application]::DoEvents()
# Perform operations while the form is being displayed.
Start-Sleep -Milliseconds 200
Write-Host . -NoNewline
} while ($form.Visible)
} finally {
# Make sure that the form gets closed and disposed of.
$form.Dispose()
}
对于反向用例,即如果您想确保表单确实接收焦点 - 默认情况下不会发生始终 - 使用以下内容:
在调用$Form.ShowDialog() 之前,为Load event 添加一个处理程序,以确保表单在加载后获得焦点:
Add-Type -AssemblyName System.Windows.Forms
$form = [System.Windows.Forms.Form]::new()
# Ensure that the form receives the focus on loading.
# (Situationally, especially when run shortly after session startup,
# the form may otherwise end up without the focus.)
$form.add_Load({
$this.Activate()
})
$form.ShowDialog()
将表单置顶的解决方法:
由于我不知道的原因,将表单的 .TopMost 属性设置为 $true 可能会在(过时的)ISE 中、在 Visual Studio Code(ISE 继任者)的会话中的第一次调用中安静地、间歇性地发生故障,并且在 Windows 终端中。
以下应该可以解决这些问题。请注意,在重新激活调用方窗口之前,该窗口可能会非常短暂地激活,但这在实践中不应该引起注意:
Add-Type -AssemblyName System.Windows.Forms
# Create a helper type for activating a window by hWnd (window handle)
Add-Type -Namespace Util -Name WinApi -MemberDefinition @'
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
'@
# Create an instance of the custom form class.
$form = [System.Windows.Forms.Form]::new()
# Get the caller's main window handle, to use it for reactivation later.
$thisHWnd = (Get-Process -Id $pid).MainWindowHandle
# Show the form *non-modally*, with .Show() rather than
# .ShowDialog(), which is the prerequisite for not blocking this script.
# Note: This *typically activates* the form (gives it the focus), though not consistently.
$form.Show()
# Perform operations while the form is being shown.
try {
# Set the workaround flags.
$makeTopMost = $true; $reactivateMe = $true
do {
# Process form events.
[System.Windows.Forms.Application]::DoEvents()
# Apply workarounds and reset the flags.
if ($reactivateMe) { $null =[Util.WinApi]::SetForegroundWindow($thisHWnd); $reactivateMe = $false }
if ($makeTopMost) { $form.TopMost = $true; $makeTopMost = $false }
# Perform operations while the form is being displayed.
Start-Sleep -Milliseconds 200
Write-Host ([Util.WinApi]::GetForegroundWindow() -eq $thisHWnd) -NoNewline
} while ($form.Visible)
} finally {
# Make sure that the form gets closed and disposed of.
$form.Dispose()
}