refactor: replace shell command check with WinAPI call

This commit is contained in:
wonfen
2025-06-22 18:45:38 +08:00
parent e7461fccab
commit f6b5524e0e
2 changed files with 137 additions and 83 deletions

View File

@@ -94,6 +94,8 @@ winapi = { version = "0.3.9", features = [
"errhandlingapi", "errhandlingapi",
"minwindef", "minwindef",
"winerror", "winerror",
"tlhelp32",
"processthreadsapi",
] } ] }
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]

View File

@@ -514,70 +514,102 @@ impl CoreManager {
Ok(()) Ok(())
} }
/// 根据进程名查找进程PID列 /// 根据进程名查找进程PID列
async fn find_processes_by_name( async fn find_processes_by_name(
&self, &self,
process_name: String, process_name: String,
_target: &str, _target: &str,
) -> Result<(Vec<u32>, String)> { ) -> Result<(Vec<u32>, String)> {
let output = if cfg!(windows) { #[cfg(windows)]
tokio::process::Command::new("tasklist") {
.args([ use std::mem;
"/FI", use winapi::um::handleapi::CloseHandle;
&format!("IMAGENAME eq {}", process_name), use winapi::um::tlhelp32::{
"/FO", CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W,
"CSV", TH32CS_SNAPPROCESS,
"/NH", };
]) use winapi::um::winnt::HANDLE;
.output()
.await?
} else if cfg!(target_os = "macos") {
tokio::process::Command::new("pgrep")
.arg(&process_name)
.output()
.await?
} else {
// Linux
tokio::process::Command::new("pidof")
.arg(&process_name)
.output()
.await?
};
if !output.status.success() { let process_name_clone = process_name.clone();
return Ok((Vec::new(), process_name)); let pids = tokio::task::spawn_blocking(move || -> Result<Vec<u32>> {
} let mut pids = Vec::new();
let stdout = String::from_utf8_lossy(&output.stdout); unsafe {
let mut pids = Vec::new(); // 创建进程快照
let snapshot: HANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snapshot == winapi::um::handleapi::INVALID_HANDLE_VALUE {
return Err(anyhow::anyhow!("Failed to create process snapshot"));
}
if cfg!(windows) { let mut pe32: PROCESSENTRY32W = mem::zeroed();
// 解析CSV格式输出: "进程名","PID","会话名","会话#","内存使用" pe32.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
for line in stdout.lines() {
if !line.is_empty() && line.contains(&process_name) { // 获取第一个进程
let fields: Vec<&str> = line.split(',').collect(); if Process32FirstW(snapshot, &mut pe32) != 0 {
if fields.len() >= 2 { loop {
// 移除引号并解析PID // 将宽字符转换为String
let pid_str = fields[1].trim_matches('"'); let end_pos = pe32
if let Ok(pid) = pid_str.parse::<u32>() { .szExeFile
pids.push(pid); .iter()
.position(|&x| x == 0)
.unwrap_or(pe32.szExeFile.len());
let exe_file = String::from_utf16_lossy(&pe32.szExeFile[..end_pos]);
// 检查进程名是否匹配
if exe_file.eq_ignore_ascii_case(&process_name_clone) {
pids.push(pe32.th32ProcessID);
}
if Process32NextW(snapshot, &mut pe32) == 0 {
break;
}
} }
} }
// 关闭句柄
CloseHandle(snapshot);
} }
Ok(pids)
})
.await??;
Ok((pids, process_name))
}
#[cfg(not(windows))]
{
let output = if cfg!(target_os = "macos") {
tokio::process::Command::new("pgrep")
.arg(&process_name)
.output()
.await?
} else {
// Linux
tokio::process::Command::new("pidof")
.arg(&process_name)
.output()
.await?
};
if !output.status.success() {
return Ok((Vec::new(), process_name));
} }
} else {
let stdout = String::from_utf8_lossy(&output.stdout);
let mut pids = Vec::new();
// Unix系统直接解析PID列表 // Unix系统直接解析PID列表
for pid_str in stdout.split_whitespace() { for pid_str in stdout.split_whitespace() {
if let Ok(pid) = pid_str.parse::<u32>() { if let Ok(pid) = pid_str.parse::<u32>() {
pids.push(pid); pids.push(pid);
} }
} }
}
Ok((pids, process_name)) Ok((pids, process_name))
}
} }
/// 终止进程并验证结果 /// 终止进程并验证结果 - 使用Windows API直接终止更优雅高效
async fn kill_process_with_verification(&self, pid: u32, process_name: String) -> bool { async fn kill_process_with_verification(&self, pid: u32, process_name: String) -> bool {
logging!( logging!(
info, info,
@@ -588,14 +620,30 @@ impl CoreManager {
pid pid
); );
let success = if cfg!(windows) { #[cfg(windows)]
tokio::process::Command::new("taskkill") let success = {
.args(["/F", "/PID", &pid.to_string()]) use winapi::um::handleapi::CloseHandle;
.output() use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess};
.await use winapi::um::winnt::{HANDLE, PROCESS_TERMINATE};
.map(|output| output.status.success())
.unwrap_or(false) tokio::task::spawn_blocking(move || -> bool {
} else { unsafe {
let process_handle: HANDLE = OpenProcess(PROCESS_TERMINATE, 0, pid);
if process_handle.is_null() {
return false;
}
let result = TerminateProcess(process_handle, 1);
CloseHandle(process_handle);
result != 0
}
})
.await
.unwrap_or(false)
};
#[cfg(not(windows))]
let success = {
tokio::process::Command::new("kill") tokio::process::Command::new("kill")
.args(["-9", &pid.to_string()]) .args(["-9", &pid.to_string()])
.output() .output()
@@ -643,34 +691,49 @@ impl CoreManager {
} }
} }
/// 检查进程是否仍在运行 /// Windows API检查进程
async fn is_process_running(&self, pid: u32) -> Result<bool> { async fn is_process_running(&self, pid: u32) -> Result<bool> {
let output = if cfg!(windows) { #[cfg(windows)]
tokio::process::Command::new("tasklist") {
.args(["/FI", &format!("PID eq {}", pid), "/FO", "CSV", "/NH"]) use winapi::shared::minwindef::DWORD;
.output() use winapi::um::handleapi::CloseHandle;
.await? use winapi::um::processthreadsapi::GetExitCodeProcess;
} else { use winapi::um::processthreadsapi::OpenProcess;
tokio::process::Command::new("ps") use winapi::um::winnt::{HANDLE, PROCESS_QUERY_INFORMATION};
let result = tokio::task::spawn_blocking(move || -> Result<bool> {
unsafe {
let process_handle: HANDLE = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid);
if process_handle.is_null() {
return Ok(false);
}
let mut exit_code: DWORD = 0;
let result = GetExitCodeProcess(process_handle, &mut exit_code);
CloseHandle(process_handle);
if result == 0 {
return Ok(false);
}
Ok(exit_code == 259)
}
})
.await?;
result
}
#[cfg(not(windows))]
{
let output = tokio::process::Command::new("ps")
.args(["-p", &pid.to_string()]) .args(["-p", &pid.to_string()])
.output() .output()
.await? .await?;
};
Ok(output.status.success() && !output.stdout.is_empty()) Ok(output.status.success() && !output.stdout.is_empty())
}
} }
async fn start_core_by_sidecar(&self) -> Result<()> { async fn start_core_by_sidecar(&self) -> Result<()> {
if let Err(e) = self.cleanup_orphaned_mihomo_processes().await {
logging!(
warn,
Type::Core,
true,
"清理多余 mihomo 进程时发生错误: {}",
e
);
}
logging!(trace, Type::Core, true, "Running core by sidecar"); logging!(trace, Type::Core, true, "Running core by sidecar");
let config_file = &Config::generate_file(ConfigType::Run)?; let config_file = &Config::generate_file(ConfigType::Run)?;
let app_handle = handle::Handle::global() let app_handle = handle::Handle::global()
@@ -1033,17 +1096,6 @@ impl CoreManager {
pub async fn restart_core(&self) -> Result<()> { pub async fn restart_core(&self) -> Result<()> {
self.stop_core().await?; self.stop_core().await?;
// 在重启时也清理多余的 mihomo 进程
if let Err(e) = self.cleanup_orphaned_mihomo_processes().await {
logging!(
warn,
Type::Core,
true,
"重启时清理多余 mihomo 进程失败: {}",
e
);
}
self.start_core().await?; self.start_core().await?;
Ok(()) Ok(())
} }