windows: Fix auto-update for `conpty.dll` (#39178)

张小白 and Jakub Konka created

This PR is a follow-up to #39090 and addresses two issues:

* Moves `conpty.dll` and `OpenConsole.exe` out of the `bin` folder to
prevent other programs from using them.
* Updates these files only after Zed exits, avoiding update failures due
to file locks.


Release Notes:

- N/A

---------

Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>

Change summary

Cargo.lock                               |  1 
Cargo.toml                               |  1 
crates/auto_update_helper/src/updater.rs | 46 ++++++++++++++++++++++++++
crates/zed/Cargo.toml                    |  1 
crates/zed/resources/windows/zed.iss     |  2 +
crates/zed/src/main.rs                   | 17 +++++++--
script/bundle-windows.ps1                |  5 +-
7 files changed, 63 insertions(+), 10 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -20348,7 +20348,6 @@ dependencies = [
  "web_search",
  "web_search_providers",
  "windows 0.61.1",
- "windows-sys 0.61.0",
  "winresource",
  "workspace",
  "workspace-hack",

Cargo.toml 🔗

@@ -714,7 +714,6 @@ wasmtime = { version = "29", default-features = false, features = [
 wasmtime-wasi = "29"
 which = "6.0.0"
 windows-core = "0.61"
-windows-sys = "0.61"
 wit-component = "0.221"
 workspace-hack = "0.1.0"
 yawc = "0.2.5"

crates/auto_update_helper/src/updater.rs 🔗

@@ -38,6 +38,20 @@ pub(crate) const JOBS: &[Job] = &[
         std::fs::remove_file(&zed_wsl)
             .context(format!("Failed to remove old file {}", zed_wsl.display()))
     },
+    |app_dir| {
+        let open_console = app_dir.join("OpenConsole.exe");
+        log::info!("Removing old file: {}", open_console.display());
+        std::fs::remove_file(&open_console).context(format!(
+            "Failed to remove old file {}",
+            open_console.display()
+        ))
+    },
+    |app_dir| {
+        let conpty = app_dir.join("conpty.dll");
+        log::info!("Removing old file: {}", conpty.display());
+        std::fs::remove_file(&conpty)
+            .context(format!("Failed to remove old file {}", conpty.display()))
+    },
     // Copy new files
     |app_dir| {
         let zed_executable_source = app_dir.join("install\\Zed.exe");
@@ -87,6 +101,38 @@ pub(crate) const JOBS: &[Job] = &[
                 zed_wsl_dest.display()
             ))
     },
+    |app_dir| {
+        let open_console_source = app_dir.join("install\\OpenConsole.exe");
+        let open_console_dest = app_dir.join("OpenConsole.exe");
+        log::info!(
+            "Copying new file {} to {}",
+            open_console_source.display(),
+            open_console_dest.display()
+        );
+        std::fs::copy(&open_console_source, &open_console_dest)
+            .map(|_| ())
+            .context(format!(
+                "Failed to copy new file {} to {}",
+                open_console_source.display(),
+                open_console_dest.display()
+            ))
+    },
+    |app_dir| {
+        let conpty_source = app_dir.join("install\\conpty.dll");
+        let conpty_dest = app_dir.join("conpty.dll");
+        log::info!(
+            "Copying new file {} to {}",
+            conpty_source.display(),
+            conpty_dest.display()
+        );
+        std::fs::copy(&conpty_source, &conpty_dest)
+            .map(|_| ())
+            .context(format!(
+                "Failed to copy new file {} to {}",
+                conpty_source.display(),
+                conpty_dest.display()
+            ))
+    },
     // Clean up installer folder and updates folder
     |app_dir| {
         let updates_folder = app_dir.join("updates");

crates/zed/Cargo.toml 🔗

@@ -170,7 +170,6 @@ zlog_settings.workspace = true
 
 [target.'cfg(target_os = "windows")'.dependencies]
 windows.workspace = true
-windows-sys.workspace = true
 
 [target.'cfg(target_os = "windows")'.build-dependencies]
 winresource = "0.1"

crates/zed/resources/windows/zed.iss 🔗

@@ -63,6 +63,8 @@ Source: "{#ResourcesDir}\bin\*"; DestDir: "{code:GetInstallDir}\bin"; Flags: ign
 Source: "{#ResourcesDir}\tools\*"; DestDir: "{app}\tools"; Flags: ignoreversion
 Source: "{#ResourcesDir}\appx\*"; DestDir: "{app}\appx";  BeforeInstall: RemoveAppxPackage; AfterInstall: AddAppxPackage; Flags: ignoreversion; Check: IsWindows11OrLater
 Source: "{#ResourcesDir}\amd_ags_x64.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "{#ResourcesDir}\OpenConsole.exe"; DestDir: "{code:GetInstallDir}"; Flags: ignoreversion
+Source: "{#ResourcesDir}\conpty.dll"; DestDir: "{code:GetInstallDir}"; Flags: ignoreversion
 
 [Icons]
 Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}.exe"; AppUserModelID: "{#AppUserId}"

crates/zed/src/main.rs 🔗

@@ -1518,11 +1518,20 @@ fn dump_all_gpui_actions() {
     .unwrap();
 }
 
-#[cfg(windows)]
+#[cfg(target_os = "windows")]
 fn check_for_conpty_dll() {
-    use windows_sys::{Win32::System::LibraryLoader::LoadLibraryW, w};
-    let hmodule = unsafe { LoadLibraryW(w!("conpty.dll")) };
-    if hmodule.is_null() {
+    use windows::{
+        Win32::{Foundation::FreeLibrary, System::LibraryLoader::LoadLibraryW},
+        core::w,
+    };
+
+    if let Ok(hmodule) = unsafe { LoadLibraryW(w!("conpty.dll")) } {
+        unsafe {
+            FreeLibrary(hmodule)
+                .context("Failed to free conpty.dll")
+                .log_err();
+        }
+    } else {
         log::warn!("Failed to load conpty.dll. Terminal will work with reduced functionality.");
     }
 }

script/bundle-windows.ps1 🔗

@@ -147,7 +147,6 @@ function DownloadAMDGpuServices {
 }
 
 function DownloadConpty {
-    # If you update the Conpty version, please also update the version in `crates/zed/build.rs`.
     $url = "https://www.nuget.org/api/v2/package/CI.Microsoft.Windows.Console.ConPTY/1.22.250314001"
     $zipPath = ".\conpty.zip"
     Invoke-WebRequest -Uri $url -OutFile $zipPath
@@ -161,8 +160,8 @@ function CollectFiles {
     Move-Item -Path "$innoDir\zed.sh" -Destination "$innoDir\bin\zed" -Force
     Move-Item -Path "$innoDir\auto_update_helper.exe" -Destination "$innoDir\tools\auto_update_helper.exe" -Force
     Move-Item -Path ".\AGS_SDK-6.3.0\ags_lib\lib\amd_ags_x64.dll" -Destination "$innoDir\amd_ags_x64.dll" -Force
-    Move-Item -Path ".\conpty\build\native\runtimes\*" -Destination "$innoDir\bin" -Force
-    Move-Item -Path ".\conpty\runtimes\win10-x64\native\conpty.dll" -Destination "$innoDir\bin\conpty.dll" -Force
+    Move-Item -Path ".\conpty\build\native\runtimes\x64\OpenConsole.exe" -Destination "$innoDir\OpenConsole.exe" -Force
+    Move-Item -Path ".\conpty\runtimes\win10-x64\native\conpty.dll" -Destination "$innoDir\conpty.dll" -Force
 }
 
 function BuildInstaller {