mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Try to fix auto-updates when Explorer.exe holds Zed.exe (#50332)
Release Notes: - Windows: make auto-update more robust in the face of apps holding the Zed.exe handle --------- Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
This commit is contained in:
parent
d2a71b0a69
commit
6195b702d6
4 changed files with 112 additions and 3 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1352,6 +1352,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
"scopeguard",
|
||||
"simplelog",
|
||||
"tempfile",
|
||||
"windows 0.61.3",
|
||||
|
|
|
|||
|
|
@ -815,6 +815,7 @@ features = [
|
|||
"Win32_System_Ole",
|
||||
"Win32_System_Performance",
|
||||
"Win32_System_Pipes",
|
||||
"Win32_System_RestartManager",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_Threading",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ log.workspace = true
|
|||
simplelog.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
scopeguard = "1.2"
|
||||
windows.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dev-dependencies]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
use std::{
|
||||
ffi::OsStr,
|
||||
os::windows::ffi::OsStrExt,
|
||||
path::Path,
|
||||
sync::LazyLock,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use windows::Win32::{
|
||||
Foundation::{HWND, LPARAM, WPARAM},
|
||||
UI::WindowsAndMessaging::PostMessageW,
|
||||
use windows::{
|
||||
Win32::{
|
||||
Foundation::{HWND, LPARAM, WPARAM},
|
||||
System::RestartManager::{
|
||||
CCH_RM_SESSION_KEY, RmEndSession, RmGetList, RmRegisterResources, RmShutdown,
|
||||
RmStartSession,
|
||||
},
|
||||
UI::WindowsAndMessaging::PostMessageW,
|
||||
},
|
||||
core::{PCWSTR, PWSTR},
|
||||
};
|
||||
|
||||
use crate::windows_impl::WM_JOB_UPDATED;
|
||||
|
|
@ -262,9 +271,106 @@ pub(crate) static JOBS: LazyLock<[Job; 9]> = LazyLock::new(|| {
|
|||
]
|
||||
});
|
||||
|
||||
/// Attempts to use Windows Restart Manager to release file handles held by other processes
|
||||
/// (e.g., Explorer.exe) on the files we need to move during the update.
|
||||
///
|
||||
/// This is a best-effort operation - if it fails, we'll still try the update and rely on
|
||||
/// the retry logic.
|
||||
fn release_file_handles(app_dir: &Path) -> Result<()> {
|
||||
// Files that commonly get locked by Explorer or other processes
|
||||
let files_to_release = [
|
||||
app_dir.join("Zed.exe"),
|
||||
app_dir.join("bin\\Zed.exe"),
|
||||
app_dir.join("bin\\zed"),
|
||||
app_dir.join("conpty.dll"),
|
||||
];
|
||||
|
||||
log::info!("Attempting to release file handles using Restart Manager...");
|
||||
|
||||
let mut session: u32 = 0;
|
||||
let mut session_key = [0u16; CCH_RM_SESSION_KEY as usize + 1];
|
||||
|
||||
// Start a Restart Manager session
|
||||
let err = unsafe {
|
||||
RmStartSession(
|
||||
&mut session,
|
||||
Some(0),
|
||||
PWSTR::from_raw(session_key.as_mut_ptr()),
|
||||
)
|
||||
};
|
||||
if err.is_err() {
|
||||
anyhow::bail!("RmStartSession failed: {err:?}");
|
||||
}
|
||||
|
||||
// Ensure we end the session when done
|
||||
let _session_guard = scopeguard::guard(session, |s| {
|
||||
let _ = unsafe { RmEndSession(s) };
|
||||
});
|
||||
|
||||
// Convert paths to wide strings for Windows API
|
||||
let wide_paths: Vec<Vec<u16>> = files_to_release
|
||||
.iter()
|
||||
.filter(|p| p.exists())
|
||||
.map(|p| {
|
||||
OsStr::new(p)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
if wide_paths.is_empty() {
|
||||
log::info!("No files to release handles for");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pcwstr_paths: Vec<PCWSTR> = wide_paths
|
||||
.iter()
|
||||
.map(|p| PCWSTR::from_raw(p.as_ptr()))
|
||||
.collect();
|
||||
|
||||
// Register the files we want to modify
|
||||
let err = unsafe { RmRegisterResources(session, Some(&pcwstr_paths), None, None) };
|
||||
if err.is_err() {
|
||||
anyhow::bail!("RmRegisterResources failed: {err:?}");
|
||||
}
|
||||
|
||||
// Check if any processes are using these files
|
||||
let mut needed: u32 = 0;
|
||||
let mut count: u32 = 0;
|
||||
let mut reboot_reasons: u32 = 0;
|
||||
let _ = unsafe { RmGetList(session, &mut needed, &mut count, None, &mut reboot_reasons) };
|
||||
|
||||
if needed == 0 {
|
||||
log::info!("No processes are holding handles to the files");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"{} process(es) are holding handles to the files, requesting release...",
|
||||
needed
|
||||
);
|
||||
|
||||
// Request processes to release their handles
|
||||
// RmShutdown with flags=0 asks applications to release handles gracefully
|
||||
// For Explorer, this typically releases icon cache handles without closing Explorer
|
||||
let err = unsafe { RmShutdown(session, 0, None) };
|
||||
if err.is_err() {
|
||||
anyhow::bail!("RmShutdown failed: {:?}", err);
|
||||
}
|
||||
|
||||
log::info!("Successfully requested handle release");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn perform_update(app_dir: &Path, hwnd: Option<isize>, launch: bool) -> Result<()> {
|
||||
let hwnd = hwnd.map(|ptr| HWND(ptr as _));
|
||||
|
||||
// Try to release file handles before starting the update
|
||||
if let Err(e) = release_file_handles(app_dir) {
|
||||
log::warn!("Restart Manager failed (will continue anyway): {}", e);
|
||||
}
|
||||
|
||||
let mut last_successful_job = None;
|
||||
'outer: for (i, job) in JOBS.iter().enumerate() {
|
||||
let start = Instant::now();
|
||||
|
|
|
|||
Loading…
Reference in a new issue