Powershell 向CMD窗口发送CTRL+C(优雅结束控制台应用程序)
01 前言
Windows下在通过命令行使用ffmpeg进行屏幕录制的时候,碰到一个问题:如何通过命令来结束录屏。直接taskkill /IM ffmpeg.exe /F强制结束ffmpeg.exe进程是不行的,会导致无法正常生成录制文件(即便生成了也因文件流不完整而无法正常播放)。
由于ffmpeg.exe会默认打开一个控制台应用程序,而结束这类控制台程序(又比如Tomcat)的一般做法就是使用快捷键Ctrl+C来实现。查了一些资料,可以通过Windows API来模拟Ctrl+C信号。
02 正文
环境
Windows 11,64bit
PSVersion 5.1
使用到win32的4个API:GenerateConsoleCtrlEvent、SetConsoleCtrlHandler、AttachConsole、FreeConsole,powershell版本代码如下:
powershell
<#
通过Windows API模拟Ctrl+C结束控制台应用程序
By hokis
On 2025-12-21 18:36
#>
$code = @'
[DllImport("kernel32.dll")]
public static extern bool GenerateConsoleCtrlEvent(int dwCtrlEvent, int dwProcessGroupId);
[DllImport("kernel32.dll")]
public static extern bool SetConsoleCtrlHandler(IntPtr handlerRoutine, bool add);
[DllImport("kernel32.dll")]
public static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FreeConsole();
'@
#引入API
$myAPI = Add-Type -MemberDefinition $code -Name myAPI -PassThru
#目标进程
$targetProcess = Get-Process -Name 'ffmpeg' -ErrorAction SilentlyContinue
if(-not $targetProcess){
Write-Host 'Not found target process.'
exit(1)
}
[void]$myAPI::FreeConsole()
if(($myAPI::AttachConsole($targetProcess.Id))){
#设置自己的ctrl+c处理,防止自己被终止
[void]$myAPI::SetConsoleCtrlHandler([IntPtr]::Zero, $true)
#发送ctrl+c(注意:这是向所有共享该console的进程发送)
if($myAPI::GenerateConsoleCtrlEvent(0, 0)){
#等待处理
Sleep -Seconds 10
#重置此参数
[void]$myAPI::SetConsoleCtrlHandler([IntPtr]::Zero, $false)
}
}
Write-Host 'Done...'
另存为run.ps1,右键,选择"使用PowerShell运行"即可
其他玩法可以自行扩展。
03 后记
又可以愉快玩耍了。