Tenho um script Pwsh com um menu que executa diversas operações diferentes para me ajudar no dia a dia.
Para uma das operações, eu inicio um processo com Start-Process, exceto que ele focaliza a janela iniciada, mas para facilitar eu gostaria que o script fosse focalizado novamente após o lançamento e não o processo iniciado.
Não quero usar os argumentos “PassThru” ou “NoNewWindow” porque não quero que o processo iniciado seja iniciado no script, nem “WindowStyle” com Minimized porque isso focaliza o processo iniciado de qualquer maneira, nem Hidden porque ainda preciso ver a janela do processo em algum lugar.
Eu tentei esse método que parece funcionar melhor:
Start-Process -FilePath "MyProcessPath"
Start-Sleep -Seconds 1
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class WinAp {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
}
"@
$p = Get-Process | Where-Object { $_.MainWindowTitle -like "*ScriptTitle*" -and $_.ProcessName -eq "powershell" }
$h = $p.MainWindowHandle
[void] [WinAp]::SetForegroundWindow($h)
[void] [WinAp]::ShowWindow($h, 1)
Mas, infelizmente, não funciona. Parece que faz alguma coisa na janela, pois ela pisca na barra de tarefas, mas não foca. Parece que, embora o script seja executado como administrador, ele não funciona para focar uma janela de administrador como essa?
Entretanto, para outra parte do meu script, fiz isso:
$tempPath = [System.IO.Path]::GetTempFileName().Replace(".tmp", ".vbs")
$code = @'
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.AppActivate "App.exe"
WScript.Sleep 250
WshShell.SendKeys "{ENTER}"
'@
$code | Out-File $tempPath -Encoding ASCII
Start-Process "wscript.exe" $tempPath -Wait
Remove-Item $tempPath
E funciona bem... ele foca a janela e, em seguida, pressiona a tecla solicitada (embora essa parte não seja necessária no meu problema atual). Tentei com o meu script, mas... nada. Principalmente porque o aplicativo é iniciado com o script, então como administrador também, eu acho? Então, não entendo por que funciona, mas não para o script em si.
Obrigado!
Por padrão, o Windows impede a ativação de janelas arbitrárias programaticamente , a menos que o processo de chamada possua a janela de primeiro plano ativa (focada) no momento da chamada , em termos gerais; os critérios exatos estão listados na seção "Comentários" do
SetForegroundWindow()
tópico de ajuda .Se os critérios não forem atendidos, a tentativa de um processo de definir programaticamente a janela em primeiro plano fará com que o ícone da barra de tarefas do processo de destino pisque algumas vezes, sem nenhuma alteração na janela ativa, que é o que você viu.
(É irrelevante se o seu script é executado com elevação (como administrador) ou não.)
Isso acontece no seu caso, porque se
Start-Process
você iniciar um processo que cria uma janela (não transitória), esta última recebe o foco (é ativada), [1] e, portanto, impede que seu script do PowerShell reative sua própria janela de console mais tarde.Você pode (temporariamente) remover essa restrição com uma chamada P/Invoke para a
SystemParametersInfo
função WinAPI, definindo seuSPI_SETFOREGROUNDLOCKTIMEOUT
parâmetro como0
.Isso incorrerá em uma penalidade única no desempenho da compilação por sessão do PowerShell, por meio de uma chamada para
Add-Type
.A ativação da janela entre processos será habilitada durante o restante da sessão do Windows do usuário, para todos os processos (portanto, considere reativar a restrição antes de sair do script, conforme mostrado abaixo).
O seguinte é um exemplo independente que demonstra a solução; para simplificar, ele usa o método
WScript.Shell
do objeto COM.AppActivate()
para reativar a janela do console de sessão do PowerShell, usando o ID do processo da sessão, conforme refletido na variável automática$PID
. [2]Uma nota sobre alternativas potenciais:
É possível que você tente iniciar seu aplicativo de uma maneira que não desvie o foco da janela do console do seu script do PowerShell, o que eliminaria a necessidade de reativá-lo.
Por exemplo,
(New-Object -ComObject Shell.Application).ShellExecute('yourApp.exe', '', '', '', 4)
pode ser iniciadoyourapp.exe
sem alterar a janela ativa; se isso acontece ou não, depende se ele foi projetado para respeitar o estilo de janela de inicialização solicitado - e parece que poucos aplicativos fazem isso.Além disso, com o estilo de janela
4
, embora a janela do aplicativo não fique ativa , ela ainda pode obscurecer o console do script do PowerShell.Se aceitável , o estilo minimiza
7
a janela do aplicativo sem roubar o foco, o que evitaria o problema de obscurecimento, mas exigiria que os usuários descobrissem o aplicativo em execução pressionando Alt-Tab ou clicando em seu ícone na barra de tarefas. No entanto, isso também funcionará apenas com aplicativos projetados para respeitar os estilos de janela de inicialização.O
.ShellExecute()
método doShell.Application
objeto COM e os estilos de janela disponíveis são documentados aqui .[Microsoft.VisualBasic.Interaction]::Shell()
oferece funcionalidade muito semelhante.[1] Isso também se aplica se você iniciar um aplicativo GUI diretamente , sem
Start-Process
. Se você usarStart-Process
para iniciar um aplicativo GUI, mesmo que isso não-WindowStyle Hidden
impeça a perda de foco. O problema #24387 do GitHub é uma solicitação de recurso que solicita que a capacidade de iniciar um aplicativo sem que ele receba o foco seja adicionada ao , mas foi recusada , sob a justificativa de que, devido à natureza multiplataforma do PowerShell (Core) 7, não deve ser aprimorado com recursos exclusivos do Windows.Start-Process
Start-Process
[2] Observe que esta abordagem, assim como a
.MainWindowHandle
da pergunta, só funciona se o processo do PowerShell for o proprietário da janela do console em que é executado. Embora isso seja válido para sessões do PowerShell iniciadas diretamente (por exemplo, a partir do Menu Iniciar ou da barra de tarefas), não se aplica a processos do PowerShell iniciados a partir de outro aplicativo de console, por exemplo, de umacmd.exe
sessão. Você pode usar a seguinte alternativa para reativação, que também requer uma abordagem P/Invoke (que pode ser combinada com a anterior em uma únicaAdd-Type
chamada):(Add-Type -PassThru -Name ConsoleActivator -Namespace WinApiHelper -MemberDefinition ' [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr hWnd); public static void ActivateOwnConsoleWindow() { SetForegroundWindow(GetConsoleWindow()); } ')::ActivateOwnConsoleWindow()
Use um script VBS temporário para refocar sua janela do PowerShell após iniciar o processo
me avise se for útil
Pelo que entendi, você tem um script que roda no console e aciona uma aplicação. Nesse ponto, o console perde o foco. Depois de um tempo, você gostaria que o foco retornasse ao console.
Geralmente uso uma função que criei para essa tarefa:
A maneira de usá-lo é: