Reputation: 90
I made a program that opens a certain program, then Ctrl+C it after x amount of time.
I am now using this [System.Windows.Forms.SendKeys]::SendWait("^{c}")
.
Will this target that certain window or just randomly send it to the current window?
How can I change it to a certain window?
This is my code:
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
}
Upvotes: 4
Views: 12321
Reputation: 199
Thanks to jimhark, I found a way to make it work without spawning a separate PowerShell process to send the Ctrl-C. It works if the PowerShell process sending the Ctrl-C also spawned, using for example Start-Process, the process it is sending the Ctrl-C to:
$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) }
What made things work was sending the GenerateConsoleCtrlEvent to the desired process group instead of all processes that share the console of the calling process
and AttachConsole back to the console of the parent of the current process
.
Upvotes: 5
Reputation: 5046
You could send the CTRL_C_EVENT signal to the process if you have the process id. In your case you can get that from Start-Process (read the docs if you don't know how to get the process id). It's also possible to get the process id from a Window Handle:
Find process id by window's handle
Sending the signal is non trivial, but thanks to @Nemo1024, @KindDragon, and Stack Overflow it's been worked out:
Can I send a ctrl-C (SIGINT) to an application on Windows?
Unfortunately, using the best approach I could find also terminated the calling PowerShell process and the only workaround I could come up with was to send the signal from a fresh PowerShell instance that I launch.
In PowerShell it looks something like this:
# 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"
Yes, I know this is VERY ugly.
Upvotes: 12