Replies: 5 comments 38 replies
-
Command.spawn() returns a Child. That child should be Send&Sync so you can move it around and store it quite easily. You can then call For storing the child you can use stuff like once_cell or tauri's built-in State. Alternatively if this is a sidecar you can use tauris built-in Command::new_sidecar instead which uses the same creation_flag under the hood. This would have the sideeffect of tauri automatically trying to clean up those spawned children on app exit, while still providing a similar interface so you can kill it yourself. |
Beta Was this translation helpful? Give feedback.
-
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
mod functions;
use std::process::Command;
use std::sync::mpsc::{sync_channel, Receiver};
use std::sync::Mutex;
use std::thread;
use command_group::{CommandGroup, GroupChild};
use tauri::api::process::Command as TCommand;
use sysinfo::{Pid, ProcessExt, Signal, System, SystemExt};
use tauri::WindowEvent;
fn start_backend(receiver: Receiver<i32>) {
let t = TCommand::new_sidecar("windxapi")
.expect("启动API服务器失败");
let mut group = Command::from(t).group_spawn().expect("启动API失败");
thread::spawn(move || {
loop{
let mut s = receiver.recv();
if s.unwrap()==-1 {
group.kill().expect("关闭API失败");
}
}
});
}
fn main() {
let (tx,rx) = sync_channel(1);
start_backend(rx);
tauri::Builder::default()
.on_window_event(move |event| match event.event() {
WindowEvent::Destroyed => {
println!("准备关闭后台API");
tx.send(-1).expect("发送关闭信号失败");
println!("已关闭后台API");
}
_ => {}
})
// .invoke_handler(tauri::generate_handler![start_backend,terminate_backend,terminate_pid])
.run(tauri::generate_context!())
.expect("error while running tauri application");
} |
Beta Was this translation helpful? Give feedback.
-
I recommend using the kill binary. use std::process::{Child, Command};
fn kill_process(child: &mut Child) -> Result<(), Box<dyn std::error::Error>> {
#[cfg(unix)]
{
let pid = child.id().to_string();
println!("Sending INT signal to process with PID: {}", pid);
let mut kill = StdCommand::new("kill")
.args(["-s", "SIGINT", &pid])
.spawn()?;
kill.wait()?;
}
#[cfg(windows)]
{
let pid = child.id().to_string();
println!("Sending taskkill to process with PID: {}", pid);
let mut kill = StdCommand::new("taskkill")
.args(["/PID", &pid, "/F"])
.spawn()?;
kill.wait()?;
}
Ok(())
}
fn main() {
let child = Command::new("your_process")
.spawn()
.expect("Failed to start process");
// Simulate some work
std::thread::sleep(std::time::Duration::from_secs(5));
match kill_process(&child) {
Ok(_) => println!("Process killed successfully."),
Err(e) => eprintln!("Failed to kill process: {}", e),
}
} |
Beta Was this translation helpful? Give feedback.
-
Thanks to #3273 (comment) and #7558 (comment) I found a solution. commands.rsfn kill_process(pid: &str) -> Result<(), Box<dyn std::error::Error>> {
#[cfg(unix)] {
println!("Sending INT signal to process with PID: {}", pid);
let mut kill = Command::new("kill")
.args(["-s", "SIGINT", &pid])
.spawn()?;
kill.wait()?;
}
#[cfg(windows)] {
println!("Sending taskkill to process with PID: {}", pid);
let mut kill = StdCommand::new("taskkill")
.args(["/PID", &pid, "/F"])
.spawn()?;
kill.wait()?;
}
Ok(())
}
#[tauri::command]
pub async fn quit_letsdane(pid: &str) -> Result<(), String> {
match kill_process(pid) {
Ok(_) => {
println!("Process killed successfully.");
Ok(())
},
Err(e) => {
eprintln!("Failed to kill process: {}", e);
Err(e.to_string())
}
}
}
#[tauri::command]
pub async fn start_letsdane(app: tauri::AppHandle) {
let child = Command::new("/absolute/path/to/my/sidecar/executable-aarch64-apple-darwin")
.args([
"-r", "127.0.0.1:5350",
"-addr", ":5555",
"-skip-dnssec",
"-skip-icann",
"-conf", "/absolute/path/to/.config"
])
.spawn()
.expect("Failed to start process");
let pid = child.id().to_string();
app.emit("letsdane-pid", Payload { message: pid.to_string().into() }).unwrap();
} main.rs// ...
.invoke_handler(tauri::generate_handler![
commands::quit_letsdane,
commands::start_letsdane
])
// ... +page.svelte $: letsdanePID = 0;
type Payload = {
message: string;
};
const subMenu7 = await MenuItem.new({
action: async() => {
// TODO
// : stop hnsd
/// stop letsdane process
await invoke("quit_letsdane", { pid: `${letsdanePID}` });
await exit(0);
},
text: "Quit"
});
async function rustBackendListener() {
await listen<Payload>("letsdane-pid", (event) => {
console.log("Event triggered from rust!\nPayload: " + event.payload.message);
letsdanePID = Number(event.payload.message);
console.log(event.payload.message);
});
}
onMount(async() => {
await rustBackendListener();
await invoke("start_letsdane");
console.log(">>> letsdanePID", letsdanePID);
}); My app will be open-source at some point but this is the gist. |
Beta Was this translation helpful? Give feedback.
-
This seems to work for me in Tauri v2. Note that I use PyInstaller onefile which spawns 2 processes. Therefore, I also use STDIN to get some signal to terminate the other process. use std::sync::{Arc, Mutex};
use tauri_plugin_shell::ShellExt;
use tauri::Manager;
use tauri::path::BaseDirectory;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet])
.setup(|app| {
let resource_path = app
.path().resolve("resources/info.log", BaseDirectory::Resource)?;
let sidecar_command = app.shell().sidecar("client2").unwrap().args([resource_path]);
let ( _rx, sidecar_child) = sidecar_command
.spawn()
.expect("Failed to spawn sidecar");
// Wrap the child process in Arc<Mutex<>> for shared access
let child = Arc::new(Mutex::new(Some(sidecar_child)));
// Clone the Arc to move into the async task
let child_clone = Arc::clone(&child);
let window = app.get_webview_window("main").unwrap();
window.on_window_event( move |event| {
if let tauri::WindowEvent::CloseRequested { .. } = event {
let mut child_lock = child_clone.lock().unwrap();
if let Some(mut child_process) = child_lock.take() {
if let Err(e) = child_process.write("Exit message from Rust\n".as_bytes())
{
println!("Fail to send to stdin of Python: {}", e);
}
if let Err(e) = child_process.kill() {
eprintln!("Failed to kill child process: {}", e);
}
}
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
} |
Beta Was this translation helpful? Give feedback.
-
Hi, thanks for this wonderful library. I want to know how to kill process started with
use std::process::Command
This is my code to start tauri app as well as a flask server:
Beta Was this translation helpful? Give feedback.
All reactions