feat: 增加基础的游戏进程监听

This commit is contained in:
xkeyC
2025-11-20 00:27:20 +08:00
parent f6340337db
commit b65187d4f0
19 changed files with 1873 additions and 702 deletions

View File

@@ -29,7 +29,12 @@ ndarray = "0.17"
serde_json = "1.0"
[target.'cfg(windows)'.dependencies]
windows = { version = "0.62.2", features = ["Win32_UI_WindowsAndMessaging"] }
windows = { version = "0.62.2", features = [
"Win32_UI_WindowsAndMessaging",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_Threading",
"Win32_Foundation"
] }
win32job = "2"
[lints.rust]

View File

@@ -49,4 +49,129 @@ pub fn set_foreground_window(window_name: &str) -> anyhow::Result<bool> {
pub fn set_foreground_window(window_name: &str) -> anyhow::Result<bool> {
println!("set_foreground_window (unix): {}", window_name);
return Ok(false);
}
#[derive(Debug, Clone)]
pub struct ProcessInfo {
pub pid: u32,
pub name: String,
pub path: String,
}
#[cfg(target_os = "windows")]
pub fn get_process_pid_by_name(process_name: &str) -> anyhow::Result<i32> {
// 保持向后兼容:返回第一个匹配进程的 PID
let processes = get_process_list_by_name(process_name)?;
if let Some(first) = processes.first() {
Ok(first.pid as i32)
} else {
Ok(-1)
}
}
#[cfg(not(target_os = "windows"))]
pub fn get_process_pid_by_name(process_name: &str) -> anyhow::Result<i32> {
println!("get_process_pid_by_name (unix): {}", process_name);
Ok(-1)
}
#[cfg(target_os = "windows")]
pub fn get_process_list_by_name(process_name: &str) -> anyhow::Result<Vec<ProcessInfo>> {
use std::mem;
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W,
TH32CS_SNAPPROCESS,
};
let mut result = Vec::new();
let search_lower = process_name.to_lowercase();
unsafe {
let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)?;
if snapshot.is_invalid() {
return Ok(result);
}
let mut process_entry: PROCESSENTRY32W = mem::zeroed();
process_entry.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
if Process32FirstW(snapshot, &mut process_entry).is_err() {
let _ = CloseHandle(snapshot);
return Ok(result);
}
loop {
// 将 WCHAR 数组转换为 String
let exe_file = String::from_utf16_lossy(
&process_entry.szExeFile[..process_entry
.szExeFile
.iter()
.position(|&c| c == 0)
.unwrap_or(process_entry.szExeFile.len())],
);
// 支持部分匹配(不区分大小写)
if exe_file.to_lowercase().contains(&search_lower) {
let pid = process_entry.th32ProcessID;
// 获取完整路径
let full_path = get_process_path(pid).unwrap_or_default();
result.push(ProcessInfo {
pid,
name: exe_file,
path: full_path,
});
}
if Process32NextW(snapshot, &mut process_entry).is_err() {
break;
}
}
let _ = CloseHandle(snapshot);
}
Ok(result)
}
#[cfg(target_os = "windows")]
fn get_process_path(pid: u32) -> Option<String> {
use windows::core::PWSTR;
use windows::Win32::Foundation::{CloseHandle, MAX_PATH};
use windows::Win32::System::Threading::{
OpenProcess, QueryFullProcessImageNameW, PROCESS_NAME_WIN32, PROCESS_QUERY_LIMITED_INFORMATION,
};
unsafe {
if let Ok(h_process) = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid) {
if !h_process.is_invalid() {
let mut path_buffer = [0u16; MAX_PATH as usize];
let mut path_len = path_buffer.len() as u32;
let result = if QueryFullProcessImageNameW(
h_process,
PROCESS_NAME_WIN32,
PWSTR::from_raw(path_buffer.as_mut_ptr()),
&mut path_len,
).is_ok() && path_len > 0 {
Some(String::from_utf16_lossy(&path_buffer[..path_len as usize]))
} else {
None
};
let _ = CloseHandle(h_process);
return result;
}
}
}
None
}
#[cfg(not(target_os = "windows"))]
pub fn get_process_list_by_name(process_name: &str) -> anyhow::Result<Vec<ProcessInfo>> {
println!("get_process_list_by_name (unix): {}", process_name);
Ok(Vec::new())
}

View File

@@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueNom,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -706588047;
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1227557070;
// Section: executor
@@ -156,6 +156,54 @@ fn wire__crate__api__http_api__fetch_impl(
},
)
}
fn wire__crate__api__win32_api__get_process_list_by_name_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
process_name: impl CstDecode<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "get_process_list_by_name",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_process_name = process_name.cst_decode();
move |context| {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || {
let output_ok =
crate::api::win32_api::get_process_list_by_name(&api_process_name)?;
Ok(output_ok)
})(),
)
}
},
)
}
fn wire__crate__api__win32_api__get_process_pid_by_name_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
process_name: impl CstDecode<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "get_process_pid_by_name",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_process_name = process_name.cst_decode();
move |context| {
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
(move || {
let output_ok =
crate::api::win32_api::get_process_pid_by_name(&api_process_name)?;
Ok(output_ok)
})(),
)
}
},
)
}
fn wire__crate__api__asar_api__get_rsi_launcher_asar_data_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
asar_path: impl CstDecode<String>,
@@ -627,6 +675,20 @@ impl SseDecode for Vec<u8> {
}
}
impl SseDecode for Vec<crate::api::win32_api::ProcessInfo> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut len_ = <i32>::sse_decode(deserializer);
let mut ans_ = vec![];
for idx_ in 0..len_ {
ans_.push(<crate::api::win32_api::ProcessInfo>::sse_decode(
deserializer,
));
}
return ans_;
}
}
impl SseDecode for Vec<(String, String)> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -731,6 +793,20 @@ impl SseDecode for Option<Vec<u8>> {
}
}
impl SseDecode for crate::api::win32_api::ProcessInfo {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut var_pid = <u32>::sse_decode(deserializer);
let mut var_name = <String>::sse_decode(deserializer);
let mut var_path = <String>::sse_decode(deserializer);
return crate::api::win32_api::ProcessInfo {
pid: var_pid,
name: var_name,
path: var_path,
};
}
}
impl SseDecode for (String, String) {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -918,6 +994,28 @@ impl flutter_rust_bridge::IntoIntoDart<crate::api::http_api::MyMethod>
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::api::win32_api::ProcessInfo {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[
self.pid.into_into_dart().into_dart(),
self.name.into_into_dart().into_dart(),
self.path.into_into_dart().into_dart(),
]
.into_dart()
}
}
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive
for crate::api::win32_api::ProcessInfo
{
}
impl flutter_rust_bridge::IntoIntoDart<crate::api::win32_api::ProcessInfo>
for crate::api::win32_api::ProcessInfo
{
fn into_into_dart(self) -> crate::api::win32_api::ProcessInfo {
self
}
}
// Codec=Dco (DartCObject based), see doc to use other codecs
impl flutter_rust_bridge::IntoDart for crate::api::rs_process::RsProcessStreamData {
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
[
@@ -1077,6 +1175,16 @@ impl SseEncode for Vec<u8> {
}
}
impl SseEncode for Vec<crate::api::win32_api::ProcessInfo> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<i32>::sse_encode(self.len() as _, serializer);
for item in self {
<crate::api::win32_api::ProcessInfo>::sse_encode(item, serializer);
}
}
}
impl SseEncode for Vec<(String, String)> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -1180,6 +1288,15 @@ impl SseEncode for Option<Vec<u8>> {
}
}
impl SseEncode for crate::api::win32_api::ProcessInfo {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<u32>::sse_encode(self.pid, serializer);
<String>::sse_encode(self.name, serializer);
<String>::sse_encode(self.path, serializer);
}
}
impl SseEncode for (String, String) {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -1379,6 +1496,16 @@ mod io {
}
}
}
impl CstDecode<Vec<crate::api::win32_api::ProcessInfo>> for *mut wire_cst_list_process_info {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> Vec<crate::api::win32_api::ProcessInfo> {
let vec = unsafe {
let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self);
flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len)
};
vec.into_iter().map(CstDecode::cst_decode).collect()
}
}
impl CstDecode<Vec<(String, String)>> for *mut wire_cst_list_record_string_string {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> Vec<(String, String)> {
@@ -1389,6 +1516,16 @@ mod io {
vec.into_iter().map(CstDecode::cst_decode).collect()
}
}
impl CstDecode<crate::api::win32_api::ProcessInfo> for wire_cst_process_info {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> crate::api::win32_api::ProcessInfo {
crate::api::win32_api::ProcessInfo {
pid: self.pid.cst_decode(),
name: self.name.cst_decode(),
path: self.path.cst_decode(),
}
}
}
impl CstDecode<(String, String)> for wire_cst_record_string_string {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> (String, String) {
@@ -1429,6 +1566,20 @@ mod io {
}
}
}
impl NewWithNullPtr for wire_cst_process_info {
fn new_with_null_ptr() -> Self {
Self {
pid: Default::default(),
name: core::ptr::null_mut(),
path: core::ptr::null_mut(),
}
}
}
impl Default for wire_cst_process_info {
fn default() -> Self {
Self::new_with_null_ptr()
}
}
impl NewWithNullPtr for wire_cst_record_string_string {
fn new_with_null_ptr() -> Self {
Self {
@@ -1533,6 +1684,22 @@ mod io {
)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_process_list_by_name(
port_: i64,
process_name: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__api__win32_api__get_process_list_by_name_impl(port_, process_name)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__win32_api__get_process_pid_by_name(
port_: i64,
process_name: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__api__win32_api__get_process_pid_by_name_impl(port_, process_name)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__asar_api__get_rsi_launcher_asar_data(
port_: i64,
@@ -1698,6 +1865,20 @@ mod io {
flutter_rust_bridge::for_generated::new_leak_box_ptr(ans)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_cst_new_list_process_info(
len: i32,
) -> *mut wire_cst_list_process_info {
let wrap = wire_cst_list_process_info {
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(
<wire_cst_process_info>::new_with_null_ptr(),
len,
),
len,
};
flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_starcitizen_doctor_cst_new_list_record_string_string(
len: i32,
@@ -1732,12 +1913,25 @@ mod io {
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_list_process_info {
ptr: *mut wire_cst_process_info,
len: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_list_record_string_string {
ptr: *mut wire_cst_record_string_string,
len: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_process_info {
pid: u32,
name: *mut wire_cst_list_prim_u_8_strict,
path: *mut wire_cst_list_prim_u_8_strict,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_record_string_string {
field0: *mut wire_cst_list_prim_u_8_strict,
field1: *mut wire_cst_list_prim_u_8_strict,