Отправка ключей в открытые окна в 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
.