posix相容的系統上,想要把一個程式用正常的流程關掉可以透過 `kill -SIGTERM $pid` 來傳送signal,然後在目標程式上trap signal來進行收尾
但在windows上不管是用 `taskkill /F` 還是 `wmic call terminate` 或 `wmic delete`,程式都會被強制關閉(相當於sigkill),沒辦法執行收尾的流程
`taskkill` 不加F的話是正常關閉,但如果對象是console app時,他會說這個程式只能使用F的方式關閉
查了半天,最接近sigterm的操作是interrupt(ctrl+c)或是break(ctrl+break)
但這個event必須透過win32 api來傳送,因此只能用powershell或是做exe來達成
最後我選了用c的方式實作
#include <windows.h>
#include <errhandlingapi.h>
#include <stdio.h>
void printSystemError();
int main(int argc, char** argv) {
if (argc <= 1) {
wprintf(L"PID argument needed\n");
return 1;
}
int targetPid = atoi(argv[1]);
// Detach from current console
if (!FreeConsole()) {
wprintf(L"FreeConsole failed\n");
printSystemError();
return 1;
}
// Attach to console where target process resides
if (!AttachConsole(targetPid)) {
wprintf(L"AttachConsole failed\n");
printSystemError();
return 1;
}
// Ignore interrupt signal we later send
if (!SetConsoleCtrlHandler(NULL, TRUE)) {
wprintf(L"SetConsoleCtrlHandler failed\n");
printSystemError();
return 1;
}
// Send interrupt signal to every process in current console
if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {
wprintf(L"GenerateConsoleCtrlEvent failed\n");
printSystemError();
return 1;
}
return 0;
}
void printSystemError() {
DWORD errCode = GetLastError();
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL
);
wprintf(L"Error Code: %d\n", errCode);
wprintf(L"Message: %s\n", (LPTSTR)&lpMsgBuf);
wprintf(L"More info: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes\n");
LocalFree(lpMsgBuf);
}
原本想要直接傳送event給指定process,但GenerateConsoleCtrlEvent只能送給process group id,而process group id無從查起,直接用pid也沒辦法,只好設定成0讓他傳給當下process group中所有程式
但這樣想要關閉的程式還是會收不到,所以還需要FreeConsole和AttachConsole讓執行當下可以跟目標process掛在同一個process group底下,這樣event就可以收到了
這時會發現送event的程式執行到一半反而因為自己收到ctrl+c而中斷,為了讓他能正常結束要先對ctrl+c免疫,因此要加上一個假的event handler
到這邊這隻程式就能正確發送ctrl+c給指定的pid,唯一需要注意的是整個process group都會收到,地圖砲範圍太大,發動之前要先提醒友軍(不想被關掉的程式)迴避,不然大家都一起被關掉了
留言