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**.
This commit is contained in:
samypr100
2026-01-11 09:10:02 -05:00
committed by GitHub
parent 7e7f1656ca
commit 339e97baa0
2 changed files with 32 additions and 3 deletions

View File

@@ -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 }

View File

@@ -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 <https://learn.microsoft.com/en-us/windows/win32/procthread/environment-variables>
#[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.