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都會收到,地圖砲範圍太大,發動之前要先提醒友軍(不想被關掉的程式)迴避,不然大家都一起被關掉了
留言