如何终止由CreateProcess启动的BAT文件所创建的子进程?
问题:终止由BAT脚本启动的子进程
示例代码
var sParams: string; SI: TStartupInfo; PI: TProcessInformation; begin FillChar(SI, SizeOf(SI), #0); FillChar(PI, SizeOf(PI), #0); with SI do begin cb := SizeOf(SI); dwFlags := STARTF_USESHOWWINDOW; wShowWindow := SW_SHOW end; sParams := 'ping 127.0.0.1 -t'; UniqueString(sParams); if CreateProcess(nil, PChar(sParams), nil, nil, False, 0, nil, nil, SI, PI) then FProcessHandle := PI.hProcess; end;
现象说明
- 直接启动
ping命令时,调用TerminateProcess(FProcessHandle, 0)可以成功关闭控制台并终止ping进程。 - 当修改参数为启动BAT脚本:
BAT脚本内容为:sParams := 'run.bat';
此时调用ping 127.0.0.1 -tTerminateProcess(FProcessHandle, 0)只能终止启动BAT的cmd.exe进程,而ping进程会继续运行。
解决方案
方法1:使用进程组(推荐,优雅终止)
通过设置进程组标志,让cmd.exe和它启动的子进程属于同一进程组,发送中断信号即可一次性终止所有关联进程。
修改后的启动代码:
var sParams: string; SI: TStartupInfo; PI: TProcessInformation; begin FillChar(SI, SizeOf(SI), #0); FillChar(PI, SizeOf(PI), #0); with SI do begin cb := SizeOf(SI); dwFlags := STARTF_USESHOWWINDOW; wShowWindow := SW_SHOW end; // 显式调用cmd.exe执行BAT,确保能设置进程组 sParams := 'cmd /c run.bat'; UniqueString(sParams); // 启用CREATE_NEW_PROCESS_GROUP标志,创建新进程组 if CreateProcess(nil, PChar(sParams), nil, nil, False, CREATE_NEW_PROCESS_GROUP, nil, nil, SI, PI) then begin FProcessHandle := PI.hProcess; // 发送CTRL_BREAK_EVENT到进程组,触发所有关联进程终止 GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, PI.dwProcessId); // 等待进程完全退出后释放句柄 WaitForSingleObject(PI.hProcess, INFINITE); CloseHandle(PI.hProcess); CloseHandle(PI.hThread); end; end;
原理:CREATE_NEW_PROCESS_GROUP会让启动的cmd.exe成为进程组组长,GenerateConsoleCtrlEvent发送的中断信号会被进程组内所有进程接收,cmd.exe会自动将信号转发给子进程ping,实现整体终止。
方法2:枚举并终止子进程
如果无法修改启动逻辑,可以通过枚举系统进程,找到父进程为cmd.exe的ping进程并单独终止。
实现代码:
procedure TerminateChildProcesses(ParentPID: DWORD); var hSnapshot: THandle; pe: TProcessEntry32; hProcess: THandle; begin hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if hSnapshot = INVALID_HANDLE_VALUE then Exit; pe.dwSize := SizeOf(TProcessEntry32); if Process32First(hSnapshot, pe) then begin repeat // 筛选父进程ID匹配的子进程 if pe.th32ParentProcessID = ParentPID then begin hProcess := OpenProcess(PROCESS_TERMINATE, False, pe.th32ProcessID); if hProcess <> 0 then begin TerminateProcess(hProcess, 0); CloseHandle(hProcess); end; end; until not Process32Next(hSnapshot, pe); end; CloseHandle(hSnapshot); end; // 调用方式:先终止子进程,再终止cmd进程 TerminateChildProcesses(PI.dwProcessId); TerminateProcess(FProcessHandle, 0);
注意:此方法需要当前进程拥有足够权限终止目标进程,且枚举过程中可能存在进程已退出的情况,需做好错误处理。
内容的提问来源于stack exchange,提问作者Pit
相关产品推荐
相关产品推荐

