hook: carry the MSVC runtime instead of asking for it - #544
Merged
Conversation
The Rust target links vcruntime140.dll dynamically, and Windows does not ship it. On a machine that never installed a VC++ redistributable — a fresh install, a reset PC — LittleBigMouse.Hook.exe dies before main() with "VCRUNTIME140.dll was not found" (#539). The UI comes up, the daemon never does, and nothing in the app is in a position to say why. The C++ hook this one replaced did not have the problem, so the whole failure arrived with the port, on exactly the machines least able to diagnose it. Static linking rather than a redistributable bundled in the installer: the daemon is a standalone exe with no C dependency on Windows (`cargo tree --target x86_64-pc-windows-msvc -i cc` finds nothing), so there is no second CRT to disagree with this one over a heap — the reason static linking is usually the wrong answer does not apply here. It also keeps the portable artifact working, which an installer-side fix would not. The config sits at the repository root rather than next to the hook's Cargo.toml because Cargo resolves .cargo/config.toml from the working directory upwards, never from the manifest, and CI builds the hook from the root with --manifest-path. Under LittleBigMouse-Hook-Rust/ it would be read by a developer building in that directory and ignored by the build that ships. CI asserts on the artifact rather than on the flag: every machine that builds the hook has a redistributable installed, so the break is invisible until it reaches a user who has none. A PE names its imported DLLs in plain ASCII, so the check needs no toolchain. Not verified here: the MSVC target cannot be built or run from Linux. The Windows build still has to confirm that it compiles, that the guard passes, and that the daemon starts — ideally on a Windows with no redistributable, which is the machine the bug is about. Co-Authored-By: Claude <noreply@anthropic.com>
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #539.
The reporter reset his PC, reinstalled, and got a dialog naming both the culprit and the missing piece:
Rust's
x86_64-pc-windows-msvctarget linksvcruntime140.dlldynamically, and Windows does not ship that DLL — only the UCRT is part of the OS. On a machine that never installed a VC++ redistributable, the daemon dies beforemain(). The UI starts, the daemon never does, and nothing in the app is in a position to explain why: from the user's side LBM simply "doesn't work".Nothing in the tree ever provided the runtime — no
crt-static, no.cargo/config.toml, and no redistributable inLittleBigMouse.iss. The C++ hook this one replaced did not have the problem, so the failure arrived with the port, and it lands on exactly the machines least equipped to diagnose it — clean installs.Why static linking
Rather than bundling the redistributable in the installer:
cargo tree --target x86_64-pc-windows-msvc -i ccprints nothing — so there is no second CRT to disagree with this one over a heap. The usual argument against a static CRT does not apply here.Why the config is at the repository root
Cargo resolves
.cargo/config.tomlfrom the working directory upwards, never from the manifest, and CI builds the hook from the root with--manifest-path. Placed underLittleBigMouse-Hook-Rust/, the flag would be honoured for a developer building in that directory and silently dropped by the build that ships — the one failure mode the file exists to prevent. From the root, both are covered.Verified rather than assumed, from the exact cwd CI uses:
The CI guard
Every machine that builds the hook has a redistributable installed, so this break is invisible right up to the user. The new step asserts on the artifact instead of trusting the flag: a PE names its imported DLLs in plain ASCII, so the check reads the file and needs no toolchain. It sits next to the existing VERSIONINFO assertion, which exists for the same reason.
Tested
cargo check --all-targetsclean,cargo testgreen (81 tests, unchanged) — Linux.cargo/config.tomlparses, and resolves to the expected rustflags from both the root and the hook directoryNot tested — needs the Windows build
The MSVC target cannot be built or run from Linux. Still to confirm:
VCRUNTIMEis gone from the importsThe first two are covered by this PR's own CI run. The last one is the only real proof, and it needs a clean VM.
Scope
This covers the hook only. If the Avalonia native libraries shipped with the UI (
libSkiaSharp.dll,av_libglesv2.dll) had the same need, the guard would not catch it — but the UI does start for the reporter, so they do not.🤖 Generated with Claude Code