A small program that launches the executable specified in its paired <name>.shim file. A helper for Scoop, the Windows command-line installer.
path = <path to executable>
args = <arguments>
cwd = <working directory>
elevate = true|false|1|0|yes|no
NAME = <environment variable override>
The file is read as UTF-8, with or without a byte order mark; plain ASCII is fine. Line endings may be LF or CRLF.
A field is key = value, where the separator is the first = and spaces around it are optional -- path = app.exe and path=app.exe are the same field. The key is trimmed of surrounding whitespace, the value of leading whitespace. Any line that is not blank, a comment, or a field is a broken shim: the shim reports the offending line and exits with code 1 without starting the target.
Lines whose first non-whitespace characters are #, ;, or //, as well as blank lines, are ignored.
| Field | Description |
|---|---|
path |
(Required) Path to the target executable |
args |
Arguments passed to the target |
cwd (workdir) |
Working directory for the target process |
elevate (runas) |
Request UAC elevation. Truthy values: true, 1, yes |
| Any other name | Environment variable set for the target process |
A path value is rooted when it starts with a drive letter and a colon, or with either separator, \ or /; otherwise it is resolved against the shim's own directory.
Values may be wrapped in double quotes (e.g. path = "C:\Program Files\app.exe") or left unquoted. In path, cwd and environment overrides the wrapping quotes are stripped after expansion; in args there is no unquoting, because the value is parsed with command-line rules, so quotes group arguments and the backslash-before-quote rules apply.
%ENV%-- Expands environment variables inpath,cwd, and environment override values; inargsthe value is passed through unchanged so the child process can expand%VAR%itself. Unknown variables (e.g.%NONEXISTENT_VAR%) are preserved as-is. Values are expanded when the shim file is read, so a value cannot reference a variable the shim itself defines.%~dp0— Inpath, expands against the shim's own directory (the target is unknown at that point). Inargs,cwd, and environment override values, expands to the directory containing the target executable with a trailing backslash. All occurrences in a value are replaced. Duplicatepathkeys: the first one wins.
User-provided runtime arguments are appended after those defined in args. Duplicate args keys accumulate in file order: args is the only list-valued field, so a repeated key appends, while every repeated scalar key except path takes its last value (path is first-wins, as stated above). An empty args value contributes nothing.
Any line whose key is not path, args, cwd, workdir, elevate, or runas is an environment variable override for the child process. To set one of those names as a variable rather than as the field, prefix the key with env::
path = %~dp0tool.exe
env:PATH = %PATH%;%~dp0
The prefix is stripped case-insensitively and the remainder is the variable name verbatim. Duplicate names: the last definition wins.
The shim waits for the child process to finish and forwards its exit code, including large values (e.g. crash codes like 0xC0000005) unmangled. If the shim fails internally, it exits with code 1.
The .shim file is found next to the executable: take the executable's own path, replace the extension of the final path component with .shim, or append .shim if that component has no dot. A dot in a parent directory is not an extension. Copying a lane's shim.exe to test.exe therefore pairs it with test.shim.
New-Item -Path test.shim -Value 'path = C:\Windows\System32\calc.exe'
Copy-Item -Path .\cpp\bin\x64\shim.exe -Destination .\test.exe
.\test.exe- C# — .NET Framework 4.5 (CLR).
- C++ — Native executable with no runtime dependencies. Zig build (default).
- Rust — Native executable using
windows-sysraw FFI bindings. Cargo build. - Zig — Native executable using custom
wWinMainCRTStartupentry. Zig build only.
All implementations share the same .shim format.
| Implementation | Build Tool | x86 | x64 | arm64 |
|---|---|---|---|---|
| C# | dotnet | 19.5 KB | 19.0 KB | 19.0 KB |
| C++ | Zig | 119.0 KB | 143.0 KB | 134.5 KB |
| C++ | MSBuild | 124.0 KB | 143.0 KB | 126.5 KB |
| Rust | Cargo | 112.5 KB | 129.0 KB | 125.0 KB |
| Zig | Zig | 87.5 KB | 71.5 KB | 23.0 KB |
- C# developer guide:
cs/README.md - C++ developer guide:
cpp/README.md - Rust developer guide:
rust/README.md - Zig developer guide:
zig/README.md - Design rationale:
RATIONALE.md - Agent conventions:
AGENTS.md - Test suite:
test/run-tests.ps1 - Test guide:
test/README.md - Startup cost:
benchmark/README.md - Tag-based release routing:
cs/v<version>→ C# release lanecpp/v<version>→ C++ release lanerust/v<version>→ Rust release lanezig/v<version>→ Zig release lane
All build.ps1 scripts accept -Target x86|x64|arm64 and -Configuration Debug|Release (default Release). Output is written to bin/{Target}/shim.exe.
This repository builds on these earlier implementations:
-
71/scoop-better-shimexe -- C implementation that solved Ctrl+C passthrough and dropped the .NET startup cost.
-
kiennq/scoop-better-shimexe -- C++ fork of 71's, adding MSBuild support and the
cwdfield to the shim format. -
zoritle/rshim -- Rust implementation, memory safety and UTF-8 BOM handling.
-
svercl/zshim -- Zig port, showed a shim can be built with no runtime dependency.
This project is dual-licensed under the Unlicense or the MIT License.
You may choose either license.