升级交互
This commit is contained in:
@@ -12,6 +12,7 @@ windows = { version = "0.56", features = [
|
||||
"Win32_System_Console",
|
||||
"Win32_System_Pipes",
|
||||
"Win32_System_IO",
|
||||
"Win32_System_Threading",
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_Storage",
|
||||
|
||||
@@ -1419,20 +1419,48 @@ fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, _debu
|
||||
app_name, current_ver, latest_ver
|
||||
);
|
||||
|
||||
// 3. 对每个 PID 直接用 Win32 命名管道发送(不再用 PowerShell)
|
||||
// 3. 对每个 PID 发送升级确认并等待用户响应(双向握手)
|
||||
for pid in pids {
|
||||
let pipe_name = format!("\\\\.\\pipe\\EasyTest_{}", pid);
|
||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||
log_print!("{} [升级确认] 连接到 {} (PID={})", ts, pipe_name, pid);
|
||||
log_print!("{} [升级确认] 连接到 {} (PID={}),等待响应...", ts, pipe_name, pid);
|
||||
|
||||
// 直接用 windows-rs Win32 API 连接命名管道
|
||||
let pipe_result = connect_named_pipe_byte(&pipe_name, &msg);
|
||||
// 发送 UpgradeConfirm 并等待 UpgradeResponse(超时 10 秒)
|
||||
let response = connect_and_wait_response(&pipe_name, &msg, 10000);
|
||||
|
||||
if pipe_result {
|
||||
log_print!("{} [升级确认] 已通知 {} (PID={}): {} -> {}",
|
||||
ts, app_name, pid, current_ver, latest_ver);
|
||||
} else {
|
||||
log_print!("{} [升级确认] 连接 {} (PID={}) 失败", ts, app_name, pid);
|
||||
match response {
|
||||
Ok(true) => {
|
||||
log_print!("{} [升级确认] 用户批准升级 {} (PID={}),应用将退出",
|
||||
ts, app_name, pid);
|
||||
// 等待应用退出(最多 5 秒)
|
||||
log_print!("{} [升级确认] 等待 {} 退出...", ts, app_name);
|
||||
let start = std::time::Instant::now();
|
||||
loop {
|
||||
let check = Command::new("powershell")
|
||||
.args(["-NoProfile", "-NonInteractive", "-Command",
|
||||
&format!("if ((Get-Process -Id {pid} -ErrorAction SilentlyContinue) -eq $null) {{ 'exit' }} else {{ 'running' }}")])
|
||||
.output();
|
||||
if let Ok(out) = check {
|
||||
if String::from_utf8_lossy(&out.stdout).contains("exit") {
|
||||
log_print!("{} [升级确认] {} 已退出,升级完成", ts, app_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if start.elapsed().as_secs() > 5 {
|
||||
log_print!("{} [升级确认] 等待退出超时,跳过", ts);
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
log_print!("{} [升级确认] 用户拒绝升级 {} (PID={})",
|
||||
ts, app_name, pid);
|
||||
}
|
||||
Err(e) => {
|
||||
log_print!("{} [升级确认] 通信失败 {} (PID={}): {}",
|
||||
ts, app_name, pid, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1444,14 +1472,21 @@ fn notify_app_upgrade(app_name: &str, current_ver: &str, latest_ver: &str, _debu
|
||||
}
|
||||
}
|
||||
|
||||
/// 用 Win32 API 连接命名管道并发送字节数据(字节模式)
|
||||
fn connect_named_pipe_byte(pipe_name: &str, msg: &str) -> bool {
|
||||
use windows::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE};
|
||||
use windows::Win32::Storage::FileSystem::{CreateFileW, WriteFile, FILE_ATTRIBUTE_NORMAL};
|
||||
/// 用 Win32 API 连接命名管道,发送消息后等待响应(双向握手)
|
||||
/// 返回 Ok(true) = 用户批准,Ok(false) = 用户拒绝,Err = 通信失败/超时
|
||||
fn connect_and_wait_response(
|
||||
pipe_name: &str,
|
||||
msg: &str,
|
||||
timeout_ms: u32,
|
||||
) -> Result<bool, String> {
|
||||
use windows::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE, HANDLE};
|
||||
use windows::Win32::Storage::FileSystem::{CreateFileW, WriteFile, ReadFile, FILE_ATTRIBUTE_NORMAL};
|
||||
use windows::Win32::System::Threading::CreateEventW;
|
||||
use windows::core::PCWSTR;
|
||||
|
||||
// Windows API 常量(跨版本稳定)
|
||||
// Windows API 常量
|
||||
use windows::Win32::Storage::FileSystem::{FILE_SHARE_MODE, FILE_CREATION_DISPOSITION};
|
||||
const GENERIC_READ: u32 = 0x80000000u32;
|
||||
const GENERIC_WRITE: u32 = 0x40000000u32;
|
||||
const FILE_SHARE_NONE: u32 = 0u32;
|
||||
const OPEN_EXISTING: u32 = 3u32;
|
||||
@@ -1459,11 +1494,11 @@ fn connect_named_pipe_byte(pipe_name: &str, msg: &str) -> bool {
|
||||
// 将管道名转换为宽字符串
|
||||
let wide_name: Vec<u16> = pipe_name.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
|
||||
// 用 CreateFileW 打开命名管道(写模式,字节模式)
|
||||
// 打开命名管道(同时读写模式)
|
||||
let pipe = unsafe {
|
||||
CreateFileW(
|
||||
PCWSTR::from_raw(wide_name.as_ptr()),
|
||||
GENERIC_WRITE,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_MODE(FILE_SHARE_NONE),
|
||||
None,
|
||||
FILE_CREATION_DISPOSITION(OPEN_EXISTING),
|
||||
@@ -1472,18 +1507,13 @@ fn connect_named_pipe_byte(pipe_name: &str, msg: &str) -> bool {
|
||||
)
|
||||
};
|
||||
|
||||
// CreateFileW 返回 Result,需要 unwrap
|
||||
let pipe = match pipe {
|
||||
Ok(h) => h,
|
||||
Err(e) => {
|
||||
eprintln!("[Pipe] CreateFileW failed: {:?}", e);
|
||||
return false;
|
||||
}
|
||||
Err(e) => return Err(format!("连接管道失败: {:?}", e)),
|
||||
};
|
||||
|
||||
// 检查是否成功打开
|
||||
if pipe == INVALID_HANDLE_VALUE {
|
||||
return false;
|
||||
return Err("管道句柄无效".to_string());
|
||||
}
|
||||
|
||||
// 写入消息 + 换行(C# 端用 StreamReader.ReadLine 读取)
|
||||
@@ -1492,7 +1522,7 @@ fn connect_named_pipe_byte(pipe_name: &str, msg: &str) -> bool {
|
||||
let data = msg_with_newline.as_bytes();
|
||||
|
||||
let mut written: u32 = 0;
|
||||
let success = unsafe {
|
||||
let write_ok = unsafe {
|
||||
WriteFile(
|
||||
pipe,
|
||||
Some(data),
|
||||
@@ -1501,9 +1531,81 @@ fn connect_named_pipe_byte(pipe_name: &str, msg: &str) -> bool {
|
||||
)
|
||||
};
|
||||
|
||||
let _ = unsafe { CloseHandle(pipe) };
|
||||
if write_ok.is_err() || written == 0 {
|
||||
unsafe { let _ = CloseHandle(pipe); };
|
||||
return Err("写入消息失败".to_string());
|
||||
}
|
||||
|
||||
success.is_ok() && written > 0
|
||||
// 创建事件用于等待响应(失败则用 INVALID_HANDLE_VALUE)
|
||||
let event = unsafe { CreateEventW(None, true, false, None) }
|
||||
.unwrap_or(HANDLE(0));
|
||||
|
||||
let mut buf = [0u8; 4096];
|
||||
let mut bytes_read: u32 = 0;
|
||||
let mut resp_buf = Vec::new();
|
||||
|
||||
// 循环读取直到收到完整响应(读到 \n)或超时
|
||||
let start_time = std::time::Instant::now();
|
||||
let deadline = timeout_ms as u64;
|
||||
|
||||
loop {
|
||||
// 检查是否超时
|
||||
if start_time.elapsed().as_millis() as u64 > deadline {
|
||||
if event != INVALID_HANDLE_VALUE {
|
||||
unsafe { let _ = CloseHandle(event); };
|
||||
}
|
||||
unsafe { let _ = CloseHandle(pipe); };
|
||||
return Err("等待响应超时".to_string());
|
||||
}
|
||||
|
||||
// 尝试读取数据(非阻塞轮询)
|
||||
let result = unsafe {
|
||||
ReadFile(
|
||||
pipe,
|
||||
Some(&mut buf),
|
||||
Some(&mut bytes_read),
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
if result.is_ok() && bytes_read > 0 {
|
||||
resp_buf.extend_from_slice(&buf[..bytes_read as usize]);
|
||||
|
||||
// 检查是否包含换行符(完整消息)
|
||||
if resp_buf.contains(&b'\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 短暂等待后重试
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
}
|
||||
|
||||
if event != INVALID_HANDLE_VALUE {
|
||||
unsafe { let _ = CloseHandle(event); };
|
||||
}
|
||||
unsafe { let _ = CloseHandle(pipe); };
|
||||
|
||||
// 解析响应 JSON
|
||||
let response = String::from_utf8_lossy(&resp_buf)
|
||||
.trim_end_matches(|c| c == '\r' || c == '\n')
|
||||
.to_string();
|
||||
|
||||
// 调试:打印原始响应
|
||||
{
|
||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||
log_print!("{} [升级确认] 收到原始响应: {:?}", ts, response);
|
||||
}
|
||||
|
||||
// 解析 {"Type":"UpgradeResponse","Approved":true/false}
|
||||
if let Some(start) = response.find("\"Approved\":") {
|
||||
// "Approved": 共11个字符,true/false 从 start+11 开始
|
||||
let rest = &response[start + 11..];
|
||||
let approved = rest.starts_with("true");
|
||||
return Ok(approved);
|
||||
}
|
||||
|
||||
Err(format!("无法解析响应: {}", response))
|
||||
}
|
||||
|
||||
/// 通知所有升级了的应用(EasyTest 等)
|
||||
|
||||
Reference in New Issue
Block a user