From 339e97baa00a23cb38d44831aa661ed40a0a214f Mon Sep 17 00:00:00 2001 From: samypr100 <3933065+samypr100@users.noreply.github.com> Date: Sun, 11 Jan 2026 09:10:02 -0500 Subject: [PATCH] Broadcast WM_SETTINGCHANGE on uv tool update-shell (#17404) ## Summary Closes https://github.com/astral-sh/uv/issues/17331 Certain applications on windows expect to be notified when environment variables change such as conhost.exe (traditional cmd.exe host). Without this notification conhost.exe will not pick up changes to environment variables regardless of how many times conhost.exe is re-launched after running `uv tool update-shell`. ## Test Plan Before this change 1. Removed `%USERPROFILE%\.local\bin` from environment variables via UI (which sends `WM_SETTINGCHANGE`) 2. Launched `%SYSTEMROOT%\System32\conhost.exe` and attempted to run any tool preivously installed. It fails to find any. 3. Ran `uv tool update-shell`. Confirmed `HKEY_CURRENT_USER\Environment\Path` was updated in registry. 4. Launched new `%SYSTEMROOT%\System32\conhost.exe` session. **Fails to find installed tools**. After this change 1. Removed `%USERPROFILE%\.local\bin` from environment variables via UI (which sends `WM_SETTINGCHANGE`) 2. Launched `%SYSTEMROOT%\System32\conhost.exe` and attempted to run any tool preivously installed. It fails to find any. 3. Ran `uv tool update-shell`. Confirmed `HKEY_CURRENT_USER\Environment\Path` was updated in registry. 4. Launched new `%SYSTEMROOT%\System32\conhost.exe` session. **Finds the installed tools**. --- Cargo.toml | 2 +- crates/uv-shell/src/windows.rs | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index df68f6440..49afc2bd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -196,7 +196,7 @@ uuid = { version = "1.16.0" } version-ranges = { version = "0.1.3", package = "astral-version-ranges" } walkdir = { version = "2.5.0" } which = { version = "8.0.0", features = ["regex"] } -windows = { version = "0.59.0", features = ["std", "Win32_Globalization", "Win32_System_LibraryLoader", "Win32_System_Console", "Win32_System_Kernel", "Win32_System_Diagnostics_Debug", "Win32_Storage_FileSystem", "Win32_Security", "Win32_System_Registry", "Win32_System_IO", "Win32_System_Ioctl"] } +windows = { version = "0.59.0", features = ["std", "Win32_Globalization", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Ioctl", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Registry", "Win32_UI_WindowsAndMessaging"] } windows-registry = { version = "0.5.0" } wiremock = { version = "0.6.4" } wmi = { version = "0.16.0", default-features = false } diff --git a/crates/uv-shell/src/windows.rs b/crates/uv-shell/src/windows.rs index 818d5b999..4c3ddb0a4 100644 --- a/crates/uv-shell/src/windows.rs +++ b/crates/uv-shell/src/windows.rs @@ -8,8 +8,11 @@ use std::path::Path; use anyhow::Context; use tracing::warn; -use windows::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_INVALID_DATA}; -use windows::core::HRESULT; +use windows::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_INVALID_DATA, LPARAM, WPARAM}; +use windows::Win32::UI::WindowsAndMessaging::{ + HWND_BROADCAST, SMTO_ABORTIFHUNG, SendMessageTimeoutW, WM_SETTINGCHANGE, +}; +use windows::core::{HRESULT, w}; use windows_registry::{CURRENT_USER, HSTRING}; use uv_static::EnvVars; @@ -46,9 +49,35 @@ fn apply_windows_path_var(path: &HSTRING) -> anyhow::Result<()> { environment.set_expand_hstring(EnvVars::PATH, path)?; } + // Notify WM_SETTINGCHANGE listeners + broadcast_environment_changes(); + Ok(()) } +/// Broadcast `WM_SETTINGCHANGE` to notify listeners about environment changes. +/// +/// SAFETY: `SendMessageTimeoutW` is safe to call with these set of parameters. +/// When modifying environment variables we need to broadcast a `WM_SETTINGCHANGE` +/// message with lparam set to the string "Environment" to allow processes such +/// as conhost.exe to pick up the changes made on new sessions. +/// +/// See +#[allow(unsafe_code)] +fn broadcast_environment_changes() { + unsafe { + SendMessageTimeoutW( + HWND_BROADCAST, + WM_SETTINGCHANGE, + WPARAM(0), + LPARAM(w!("Environment").as_ptr() as isize), // null terminated + SMTO_ABORTIFHUNG, + 5000, + None, + ); + } +} + /// Retrieve the windows `PATH` variable from the registry. /// /// Returns `Ok(None)` if the `PATH` variable is not a string.