我试图将我的Powershell控制台放在最前面,即使已最小化。 我发现以下代码:
function Show-Process($Process, [Switch]$Maximize)
{
$sig = '
[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")] public static extern int SetForegroundWindow(IntPtr hwnd);
'
if ($Maximize) { $Mode = 3 } else { $Mode = 4 }
$type = Add-Type -MemberDefinition $sig -Name WindowAPI -PassThru
$hwnd = $process.MainWindowHandle
$null = $type::ShowWindowAsync($hwnd, $Mode)
$null = $type::SetForegroundWindow($hwnd)
}
Show-Process -Process (Get-Process -Id $pid)
它工作正常,但是当我从Button Click事件中调用该函数时,控制台不会显示。 问题是什么?使用WinForms GUI时,是否可以将Powershell控制台置于最前面?
这是示例GUI代码:
function Show-Process($Process, [Switch]$Maximize)
{
$sig = '
[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")] public static extern int SetForegroundWindow(IntPtr hwnd);
'
if ($Maximize) { $Mode = 3 } else { $Mode = 4 }
$type = Add-Type -MemberDefinition $sig -Name WindowAPI -PassThru
$hwnd = $process.MainWindowHandle
$null = $type::ShowWindowAsync($hwnd, $Mode)
$null = $type::SetForegroundWindow($hwnd)
}
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '446,266'
$Form.text = "Form"
$Form.TopMost = $false
$Button1 = New-Object system.Windows.Forms.Button
$Button1.text = "button"
$Button1.width = 60
$Button1.height = 30
$Button1.location = New-Object System.Drawing.Point(75,29)
$Button1.Font = 'Microsoft Sans Serif,10'
$Button1.Add_Click({
Show-Process -Process (Get-Process -Id $pid)
})
$Form.controls.AddRange(@($Button1))
[void]$Form.ShowDialog()
答案 0 :(得分:3)
由于@iRon的回答,我得以弄清楚我想要的方式。 对于任何好奇的人来说,问题是,只要不调用ShowDialog,就只能获得控制台MainwindowHandle。 因此,我将控制台句柄保存在一个变量中,并使用Form_Shown事件获取Form WindowHandle,因为Form_Load仍返回控制台句柄。
try: ... except AttributeError:
现在,如果我按下按钮,控制台会在前面弹出。 当用户在控制台中输入内容后,该表单再次出现在前面。
答案 1 :(得分:2)
不幸的是,我无法完全解决它,但也许其他人可能会根据我的发现进一步为您提供帮助:
首先,按钮单击事件中的进程与父PowerShell主机运行所在的进程空间不同。可以很容易地证明这一点,但可以在{中显示$hwhd
和Write-Host $hwnd
{1}}函数,并在调用Show-Process
之前先调用Show-Process
函数:
ShowDialog
换句话说:要修复此部分,您需要首先从PowerShell窗口捕获父Show-Process -Process (Get-Process -Id $pid)
[void]$Form.ShowDialog()
:
$Pid
上面的代码段有效,但是一旦删除(或注释掉)$Button1.Add_Click({
Show-Process -Process $MyProcess
})
$Form.controls.AddRange(@($Button1))
$MyProcess = Get-Process -Id $pid
Show-Process -Process $MyProcess
[void]$Form.ShowDialog()
行(在主机级别),它就会再次中断...
答案 2 :(得分:2)
您已经发现.MainWindowHandle
不是 static 属性(来自链接的文档;已添加重点):
主窗口是由当前具有焦点的过程打开的窗口 [...]
因此,当显示表单时,当前进程的.MainWindowHandle
属性的值从控制台窗口手柄更改为 WinForms窗口 。 [1]
在显示表单之前,先缓存控制台窗口句柄 绝对是一个选择,但是有一种更简单的方法,因为您已经在WinAPI P / Invoke声明中使用了Add-Member
:GetConsoleWindow()
WinAPI函数始终返回当前进程的控制台窗口句柄。
此外,您的$Forms
表单实例具有.Handle
属性,该属性直接返回表单的窗口句柄-无需进行(Get-Process -Id $pid).MainWindowHandle
调用。
因此,以下解决方案不需要全局或脚本级变量,并且将查询窗口句柄的范围限制为按钮单击事件处理程序:
# P/Invoke signatures - note the addition of GetConsoleWindow():
$sig = '
[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")] public static extern int SetForegroundWindow(IntPtr hwnd);
[DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow();'
$type = Add-Type -MemberDefinition $sig -Name WindowAPI -PassThru
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object system.Windows.Forms.Form -Property @{
ClientSize = '446,266'
text = "Form"
}
$Button1 = New-Object system.Windows.Forms.Button -Property @{
text = "Test"
location = New-Object System.Drawing.Point(75, 29)
}
$Button1.Add_Click({
# Get this form's window handle.
$handleForm = $Form.Handle # More generically: $this.FindForm().Handle
# Get the console window's handle.
$handleConsole = $type::GetConsoleWindow()
# Activate the console window and prompt the user.
$null = $type::ShowWindowAsync($handleConsole, 4); $null = $type::SetForegroundWindow($handleConsole)
Read-Host -Prompt "Please Enter a Value"
# Reactivate this form.
$null = $type::ShowWindowAsync($handleForm, 4); $null = $type::SetForegroundWindow($handleForm)
})
$Form.controls.AddRange(@($Button1))
$null = $Form.ShowDialog()
[1]请注意,已缓存进程对象不会动态更新其.MainWindowHandle
值;您必须手动致电.Refresh()
。
因为iRon's solution在显示表单之前 缓存了当前进程对象,所以它仍然碰巧反映了按钮单击处理程序中的控制台窗口句柄。