diff --git a/.gitignore b/.gitignore index 81419bd..e13ad4f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ target **/*~ **/node_modules **/.DS_Store +index.d.ts + diff --git a/README.md b/README.md index 51e9580..9f293a5 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,15 @@ id, and sends a `CTRL-C` event to that console. > Note: This is a no-op on non-windows platforms +## API + +### `ctrlc(pid, processKillerPath?)` + +Arguments: + +* **pid** pid of process to kill +* **processKillerPath** (optional) path to `process-killer.exe` if it is not at the default location. Normally `process-killer.exe` should be located in the `ctrlc-windows` module, but if you're using a bundler like `rspack` and also using `node-loader` then you'll have to manually copy `ctrlc-windows/dist/{x64,arm64}/process-killer.exe` into your app bundle and provide that path here. + ## Usage ``` javascript diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 51c27f3..0000000 --- a/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ - -/* auto-generated by NAPI-RS */ - -export function ctrlc(pidFromJs: number, killerExePath: string): boolean diff --git a/lib/index.d.ts b/lib/index.d.ts index 52afef6..6a2f908 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1 +1 @@ -export function ctrlc(pid: number): void; +export function ctrlc(pid: number, processKillerPath?: string): void; diff --git a/lib/index.js b/lib/index.js index 3e4c30b..261b908 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,21 +1,41 @@ +const { execFileSync } = require("child_process"); const { platform, arch } = require("os"); const { join } = require("path"); -const archDistDirName = arch() === "arm64" ? "arm64" : "x64"; +const isArm64 = arch() === "arm64"; // ? "arm64" : "x64"; +const archDistDirName = isArm64 ? "arm64" : "x64"; +const isWindows = platform() === "win32"; -const native = - platform() === "win32" - ? require(`../dist/${archDistDirName}/ctrlc-windows.node`) - : require("./posix"); +// this would be more friendly to bundlers +const native = isWindows + ? isArm64 + ? require("../dist/arm64/ctrlc-windows.node") + : require("../dist/x64/ctrlc-windows.node") + : null; + +/** + * workaround for node-loader users; because node-loader only copies .node files, + * devs using them will need to manually copy process-killer.exe into their app bundle + * and provide the path to it when calling ctrlc() + */ +const defaultKillerPath = join(__dirname, "..", `dist/${archDistDirName}/process-killer.exe`); module.exports = { - ctrlc(pid) { + ctrlc(pid, processKillerPath = defaultKillerPath) { + if (!isWindows) { + let error = new Error( + "tried to invoke windows-specific ctrlc on a non-windows platform", + ); + error.name = "PlatformError"; + throw error; + } try { // don't even attempt if the // process is not running if (process.kill(pid, 0)) { - const processKillerPath = join(__dirname, "..", `dist/${archDistDirName}/process-killer.exe`); - native.ctrlc(pid, processKillerPath); + native.ignoreSignal(true); + execFileSync(processKillerPath, [pid.toString()]); + native.ignoreSignal(false); } } catch (error) { if (error.code !== "ESRCH") { @@ -24,3 +44,4 @@ module.exports = { } }, }; + diff --git a/lib/posix.js b/lib/posix.js deleted file mode 100644 index e72ce74..0000000 --- a/lib/posix.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - ctrlc() { - let error = new Error('tried to invoke windows-specific ctrlc on a non-windows platform'); - error.name = 'PlatformError'; - throw error; - } -} diff --git a/src/bin/process-killer.rs b/src/bin/process-killer.rs index ab77e7d..773e244 100644 --- a/src/bin/process-killer.rs +++ b/src/bin/process-killer.rs @@ -1,9 +1,11 @@ +#![windows_subsystem = "windows"] + #[cfg(windows)] use std::error::Error; #[cfg(windows)] use windows::Win32::System::Console::{ - AttachConsole, FreeConsole, GenerateConsoleCtrlEvent, SetConsoleCtrlHandler, CTRL_C_EVENT, + AttachConsole, GenerateConsoleCtrlEvent, SetConsoleCtrlHandler, CTRL_C_EVENT, }; #[cfg(windows)] @@ -21,8 +23,6 @@ pub fn main() -> Result<(), Box> { #[cfg(windows)] fn kill_pid(pid: u32) -> Result<(), Box> { unsafe { - FreeConsole(); - if AttachConsole(pid).as_bool() { SetConsoleCtrlHandler(None, true); GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); diff --git a/src/lib.rs b/src/lib.rs index 0050a6a..dd37234 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,56 +3,11 @@ #[macro_use] extern crate napi_derive; -use napi::{Error, JsNumber, JsString, Result, Status}; -use std::ffi::OsString; -use std::process::Command; use windows::Win32::System::Console::SetConsoleCtrlHandler; #[napi] -fn ctrlc(pid_from_js: JsNumber, killer_exe_path: JsString) -> Result { - let pid = match pid_from_js.get_int32() { - Ok(child) => child, - Err(error) => { - return Err(napi::Error { - status: Status::GenericFailure, - reason: format!("unable to parse passed pid: {}", error), - }); - } - }; - let killer_exe = match OsString::try_from(killer_exe_path.into_utf8()?.into_owned()?) { - Ok(child) => child, - Err(error) => { - return Err(napi::Error { - status: Status::GenericFailure, - reason: format!("unable to kind process killer executable: {}", error), - }); - } - }; - +fn ignore_signal(ignore: bool) { unsafe { - SetConsoleCtrlHandler(None, true); - }; - - let mut killer = match Command::new(killer_exe).arg(format!("{:?}", pid)).spawn() { - Ok(child) => child, - Err(error) => { - return Err(napi::Error { - status: Status::GenericFailure, - reason: format!("unable to spawn process to kill pid: {}", error), - }); - } - }; - - let status = killer.wait(); - - unsafe { SetConsoleCtrlHandler(None, false) }; - - match status { - Ok(status) if status.success() => Ok(true), - Ok(_) => Err(Error::new( - Status::GenericFailure, - format!("unable to kill process with pid '{:?}'", pid), - )), - Err(error) => Err(Error::new(Status::GenericFailure, format!("{}", error))), + SetConsoleCtrlHandler(None, ignore); } }