Отправка ключей в открытые окна в PowerShell

Я сделал программу, которая открывает определенную программу, а затем Ctrl+C через x промежуток времени.

Я сейчас использую это [System.Windows.Forms.SendKeys]::SendWait("^{c}"), Будет ли это предназначено для определенного окна или просто случайным образом отправит его в текущее окно?

Как я могу изменить его на определенное окно?

Это мой код:

Write-Host "Safe Botting V0.1"
Write-Host "Initializing..."
Start-Sleep -s 3
Write-Host "Program started successfully with no errors."

While($true)
{
    Write-Host "Starting bot..."
    Start-Sleep -s 3
    Start-Process -FilePath E:\Documents\bot.exe
    Write-Host "Bot started successfully"
    $rnd = Get-Random -Minimum 1800 -Maximum 10800
    Write-Host "The bot will run for:"
    Write-Host $rnd
    Start-Sleep -s $rnd
    Write-Host "Bot will now stop!"
    [System.Windows.Forms.SendKeys]::SendWait("^{c}") 
    Write-Host "Bot terminated"
    Write-Host "Starting cooldown time"
    $rnb = Get-Random -Minimum 14400 -Maximum 28800
    Write-Host "The bot will cooldown for"
    Write-host $rnb
    Start-Sleep -s $rnb
    Write-Host "Cooldown Finished, Restarting"
    Start-Sleep -s 5
}

1 ответ

Решение

Вы можете отправить сигнал CTRL_C_EVENT процессу, если у вас есть идентификатор процесса. В вашем случае вы можете получить это из Start-Process (читайте документы, если вы не знаете, как получить идентификатор процесса). Также возможно получить идентификатор процесса из дескриптора окна:

Найти идентификатор процесса по дескриптору окна

Отправка сигнала нетривиальна, но благодаря @Nemo1024, @KindDragon и Stack Overflow это было решено:

Могу ли я отправить Ctrl-C (SIGINT) в приложение в Windows?

К сожалению, используя лучший из найденных мною подходов, я также остановил вызывающий процесс PowerShell, и единственный обходной путь, который я смог найти, - это отправить сигнал из нового экземпляра PowerShell, который я запускаю.

В PowerShell это выглядит примерно так:

# be sure to set $ProcessID properly. Sending CTRL_C_EVENT signal can disrupt or terminate a process
$ProcessID = 1234
$encodedCommand = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes("Add-Type -Names 'w' -Name 'k' -M '[DllImport(""kernel32.dll"")]public static extern bool FreeConsole();[DllImport(""kernel32.dll"")]public static extern bool AttachConsole(uint p);[DllImport(""kernel32.dll"")]public static extern bool SetConsoleCtrlHandler(uint h, bool a);[DllImport(""kernel32.dll"")]public static extern bool GenerateConsoleCtrlEvent(uint e, uint p);public static void SendCtrlC(uint p){FreeConsole();AttachConsole(p);GenerateConsoleCtrlEvent(0, 0);}';[w.k]::SendCtrlC($ProcessID)"))
start-process powershell.exe -argument "-nologo -noprofile -executionpolicy bypass -EncodedCommand $encodedCommand"

Да, я знаю, что это очень уродливо.

Благодаря jimhark я нашел способ заставить его работать без создания отдельного процесса PowerShell для отправки Ctrl-C. Он работает, если процесс PowerShell, отправляющий Ctrl-C, также порожден, используя, например, Start-Process, процесс, которому он отправляет Ctrl-C:

$ProcessID = 1234
$MemberDefinition = '
    [DllImport("kernel32.dll")]public static extern bool FreeConsole();
    [DllImport("kernel32.dll")]public static extern bool AttachConsole(uint p);
    [DllImport("kernel32.dll")]public static extern bool GenerateConsoleCtrlEvent(uint e, uint p);
    public static void SendCtrlC(uint p) {
        FreeConsole();
        AttachConsole(p);
        GenerateConsoleCtrlEvent(0, p);
        FreeConsole();
        AttachConsole(uint.MaxValue);
    }'
Add-Type -Name 'dummyName' -Namespace 'dummyNamespace' -MemberDefinition $MemberDefinition
[dummyNamespace.dummyName]::SendCtrlC($ProcessID) }

Что заставляло все работать, так это отправка GenerateConsoleCtrlEvent в желаемую группу процессов вместо all processes that share the console of the calling processи AttachConsole обратно в the console of the parent of the current process.

Другие вопросы по тегам