Compare commits

...
Author SHA1 Message Date
l0ng-ai fd827aaa5d chore(deps): update rustix for RUSTSEC-2026-0486 2026-11-11 17:43:39 +08:00
l0ng-ai cfb5a26f38 test(i18n): allow untranslated framework and JDK names 2026-11-11 17:42:21 +08:00
l0ng-ai 721ef3b1a5 chore: unblock acceptance checks on current main 2026-11-11 17:34:33 +08:00
l0ng-ai b0f043d8d8 fix(windows): preserve inherited error mode and verify child inheritance 2026-11-11 17:29:50 +08:00
l0ng-ai 8c328610ee Merge remote-tracking branch 'origin/main' into review/pr2550 2026-11-11 17:27:26 +08:00
l0ng-ai 0b0586a71a fix(windows): don't show system error boxes for processes that fail to load
A broken python.exe found while detecting interpreters made Windows show a
modal "System Error" box ("VCRUNTIME140.dll was not found"). The app now
sets SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX at startup; child
processes inherit the error mode, so such a probe fails with an exit code.

Fixes #1161
2026-11-10 23:28:53 +08:00
7 changed files with 96 additions and 21 deletions

No files matched your search

Generated
+18 -18
View File
@@ -791,7 +791,7 @@ dependencies = [
"maybe-owned",
"rustix",
"rustix-linux-procfs",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
"winx",
]
@@ -1522,7 +1522,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d045de693cb712d0b22c6a64be5b953f67b3ce00ab5ad3dd5d8b441886ab8e1a"
dependencies = [
"quote",
"syn 3.0.9",
"syn 2.0.119",
]
[[package]]
@@ -1828,7 +1828,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -2154,7 +2154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -5157,7 +5157,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -5832,7 +5832,7 @@ dependencies = [
"png 0.18.1",
"serde",
"thiserror 2.0.21",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -5940,7 +5940,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -7776,15 +7776,15 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.1.5"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d"
checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa"
dependencies = [
"bitflags 2.13.2",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -7852,7 +7852,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -8508,7 +8508,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -9551,7 +9551,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -10083,7 +10083,7 @@ dependencies = [
"png 0.18.1",
"serde",
"thiserror 2.0.21",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -10505,7 +10505,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset",
"tempfile",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -11373,7 +11373,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -11394,7 +11394,7 @@ dependencies = [
"objc2-foundation 0.3.2",
"objc2-quartz-core 0.3.2",
"raw-window-handle",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
"windows-version",
]
@@ -11812,7 +11812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10"
dependencies = [
"cfg-if",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
+1 -1
View File
@@ -257,7 +257,7 @@ mod tests {
#[test]
fn object_array_region_rejects_void_and_primitive_elements() {
for inner in [b'V', b'I'] {
for inner in *b"VI" {
let mut w = Writer::new();
w.u8(b'[');
w.i32(1);
+1
View File
@@ -108,6 +108,7 @@ windows = { version = "0.62", features = [
windows-sys = { version = "0.61", features = [
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_System_Diagnostics_Debug",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
] }
+4
View File
@@ -37,6 +37,8 @@ mod window_groups;
mod window_work_area;
mod window_zoom;
mod windows;
#[cfg(windows)]
mod windows_error_mode;
use parking_lot::Mutex;
use state::AppState;
@@ -72,6 +74,8 @@ pub fn export_bindings(builder: &tauri_specta::Builder<tauri::Wry>) {
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
ide_workspace::process::mark_start();
#[cfg(windows)]
windows_error_mode::quiet_error_dialogs();
let context = tauri::generate_context!();
#[cfg(debug_assertions)]
let context = dev::configure(context);
+50
View File
@@ -0,0 +1,50 @@
use windows_sys::Win32::System::Diagnostics::Debug::{
GetErrorMode, SEM_FAILCRITICALERRORS, SEM_NOOPENFILEERRORBOX, SetErrorMode,
};
/// Call before starting threads or tools. Children inherit this mode, so a missing DLL
/// (for example in a broken Python install) produces an exit code instead of a modal dialog.
pub(super) fn quiet_error_dialogs() {
// SAFETY: these APIs only read/write this process's error mode. Preserve inherited policy.
unsafe { SetErrorMode(GetErrorMode() | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX) };
}
#[cfg(test)]
mod tests {
use super::*;
use std::os::windows::process::CommandExt;
use std::process::Command;
use windows_sys::Win32::System::Diagnostics::Debug::SEM_NOGPFAULTERRORBOX;
#[test]
fn preserves_policy_and_children_inherit_quiet_errors() {
const STAGE: &str = "OPENIDEA_ERROR_MODE_TEST_STAGE";
const SEED: &str = "OPENIDEA_ERROR_MODE_TEST_SEED";
let child = || {
let mut cmd = Command::new(std::env::current_exe().unwrap());
cmd.args(["--exact", "windows_error_mode::tests::preserves_policy_and_children_inherit_quiet_errors"])
.creation_flags(0x0800_0000);
cmd
};
if let Ok(stage) = std::env::var(STAGE) {
let seed: u32 = std::env::var(SEED).unwrap().parse().unwrap();
if stage == "configure" {
// SAFETY: only this dedicated subprocess's error mode is changed.
unsafe { SetErrorMode(seed) };
quiet_error_dialogs();
quiet_error_dialogs();
}
let expected = seed | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX;
// SAFETY: GetErrorMode has no preconditions.
assert_eq!(unsafe { GetErrorMode() } & expected, expected);
if stage == "configure" {
assert!(child().env(STAGE, "inherited").status().unwrap().success());
}
return;
}
// Keep process-wide state changes out of the parallel test runner.
for seed in [0, SEM_NOGPFAULTERRORBOX, SEM_NOOPENFILEERRORBOX | SEM_NOGPFAULTERRORBOX] {
assert!(child().env(STAGE, "configure").env(SEED, seed.to_string()).status().unwrap().success());
}
}
}
@@ -65,7 +65,16 @@ it("does not invoke a command when the caller signal is already aborted", async
controller.abort();
await expect(
tauriPluginsBackend.runCommand("example", "annotations", "buffer", null, [], undefined, undefined, controller.signal),
tauriPluginsBackend.runCommand(
"example",
"annotations",
"buffer",
null,
[],
undefined,
undefined,
controller.signal,
),
).rejects.toMatchObject({ name: "AbortError" });
expect(invoke).not.toHaveBeenCalled();
});
@@ -75,7 +84,16 @@ it("does not cancel a completed command when the caller later aborts", async ()
const controller = new AbortController();
await expect(
tauriPluginsBackend.runCommand("example", "annotations", "buffer", null, [], undefined, undefined, controller.signal),
tauriPluginsBackend.runCommand(
"example",
"annotations",
"buffer",
null,
[],
undefined,
undefined,
controller.signal,
),
).resolves.toBe("finished");
controller.abort();
+2
View File
@@ -283,6 +283,8 @@ describe("catalogs", () => {
"Yarn",
"Cargo",
"CMake",
"Tailwind CSS",
"JDK:",
]);
const translated = SUPPORTED_LOCALES.filter((locale) => locale !== "en").sort();