mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-02-06 15:10:20 +00:00
feat: 增加基础的游戏进程监听
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user