Выведите Powershell-Console на передний план из WinForms - PullRequest
0 голосов
/ 03 ноября 2018

Я пытаюсь вывести мою консоль 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) 

Работает нормально, но когда я вызываю функцию из события нажатия кнопки, консоль не отображается. В чем проблема? Есть ли способ вывести консоль PowerShell на передний план при использовании графического интерфейса WinForms?

Вот пример кода 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()

Ответы [ 3 ]

0 голосов
/ 03 ноября 2018

Благодаря ответу @ iRon я смог понять, как я этого хочу. Для любого любопытного, проблема в том, что вы можете получить приставки MainwindowHandle только до тех пор, пока ShowDialog не был вызван. Поэтому я сохраняю дескриптор консоли в переменной и использую событие Form_Shown, чтобы получить форму WindowHandle, поскольку Form_Load по-прежнему возвращает дескриптор консоли.

$sig = '
[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")] public static extern int SetForegroundWindow(IntPtr hwnd);'
$type = Add-Type -MemberDefinition $sig -Name WindowAPI -PassThru
[IntPtr]$handleConsole = (Get-Process -Id $pid).MainWindowHandle
[void]$type::ShowWindowAsync($handleConsole, 4);[void]$type::SetForegroundWindow($handleConsole)

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

$Form.Add_Shown({
 $global:handleForm = (Get-Process -Id $pid).MainWindowHandle
})

$Button1                         = New-Object system.Windows.Forms.Button
$Button1.text                    = "Clone ad-USer"
$Button1.width                   = 60
$Button1.height                  = 30
$Button1.location                = New-Object System.Drawing.Point(75,29)
$Button1.Font                    = 'Microsoft Sans Serif,10'

$Button1.Add_Click({
    [void]$type::ShowWindowAsync($handleConsole, 4);[void]$type::SetForegroundWindow($handleConsole)
    Read-Host -Prompt "Please Enter a Value"
    [void]$type::ShowWindowAsync($global:handleForm, 4);[void]$type::SetForegroundWindow($global:handleForm)
})

$Form.controls.AddRange(@($Button1))

[void]$Form.ShowDialog()

Теперь, если я нажму кнопку, перед консолью появится всплывающее окно. После того, как Пользователь ввел что-то в Консоль, Форма снова выходит вперед.

0 голосов
/ 03 ноября 2018

Как вы обнаружили, .MainWindowHandle не является статическим свойством (из связанных документов; выделение добавлено):

Главное окно - это окно, открытое процессом, который в настоящее время имеет фокус [...]

Таким образом, значение свойства .MainWindowHandle текущего процесса изменяется с дескриптора console-window на окно WinForms во время отображения формы. [1]

Кэширование дескриптора окна консоли до отображение формы, безусловно, является вариантом, но есть более простой способ, учитывая, что вы уже используете Add-Member с объявлениями WinAPI P / Invoke: 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] Обратите внимание, что объект процесса cached не динамически обновляет свое значение .MainWindowHandle; Вы должны позвонить .Refresh() вручную. Поскольку решение iRon кэширует объект текущего процесса до отображения формы, все равно случается, что он отражает дескриптор окна консоли внутри обработчика нажатия кнопки.

0 голосов
/ 03 ноября 2018

К сожалению, я не могу полностью это исправить, но, возможно, другие могут вам помочь, основываясь на моих выводах:

Прежде всего, процесс в событии нажатия кнопки - это другое пространство процесса, в котором работает родительский хост PowerShell. Это легко проверить, но обнаружив $hwhd с Write-Host $hwnd в функции Show-Process и также вызывает функцию Show-Process до вызова ShowDialog:

Show-Process -Process (Get-Process -Id $pid) 
[void]$Form.ShowDialog()

Другими словами: чтобы исправить эту часть, вам нужно сначала поймать родительский элемент $Pid из окна PowerShell:

$Button1.Add_Click({
    Show-Process -Process $MyProcess 
})

$Form.controls.AddRange(@($Button1))

$MyProcess = Get-Process -Id $pid
Show-Process -Process $MyProcess
[void]$Form.ShowDialog()

Приведенный выше фрагмент работает, но как только я удаляю (или закомментирую) строку Show-Process -Process $MyProcess (на уровне хоста), она снова прерывается ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...