- Notifications
You must be signed in to change notification settings - Fork2
Make Shims for Executables (like Chocolatey and Scoop)
License
jphilbert/shim_executable
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This application generates an executable "shim" that will in turn execute another file relative to its location. This is an attempt at a drop in replacement for the RealDimensions Software LLC's (RDS)shimgen.exe
that comes withChocolately, yet has various other possible uses. Per theirsite:
Shimming is like symlinking, but it works much better. It's a form of redirection, where you create a "shim" that redirects input to the actual binary process and shares the output. It can also work to simply call the actual binary when it shims GUI applications.
We like to call this "batch redirection that works".
This also allows applications and tools to be on the "PATH" without cluttering up the PATH environment variable.
The generator and in turn, the shims themselves, have evolved fromkiennq's c++ shim implementation for Scoop andTheCakeIsNaOH's scoop style shimgen for Chocolatey. Hence many features are attributed to or inspired by their great work.
- Free and Open Source - can be used outside of "official" Chocolatey builds without a paid license
- Backward compatible - drop-in replacement of
shimgen.exe
(seeCompatibility section for more details) - Smaller size - almost 50% smaller ❗
- Increased speed - I assume RDS
shimgen.exe
compiles a new executable usingcsc.exe
for every shim which is a significant bottleneck
- More transparent execution - GUI applications launch without the brief, yet annoying, console window display (seeMotivation section below)
- Automatic use of parent version info and icons - in Windows, shims will appear to be equivalent to their parent
- Smaller size - now 28% smaller ❗
- Increased speed - now 50% faster ❗ (noprecise speed tests were actually done)
- Better support for
crtl+c
, as shim passes signal to child process - Terminates child processes if parent process is killed
- Consistent checksum for all shims
- More testing
- Impliment changing of icon (i.e.,
--iconpath
function) - does anyone actually use this? - More testing
- ❓ Add support for embedding working directory into shim
- ❓ Add option to create Scoop-style shims (
.shim
file next to each shim.exe
)
- Download a release or build it yourself
- Run
shim_exec.exe <source>
This will create an executable in the current directory named the same as<source>
that will in turn execute it. More options can be viewed using thehelp flag-?
,-h
, or--help
. The shim itself has additional options and can be viewed using it'shelp flag--shim-help
.
Why reinvent the wheel? Ever since I foundChocolately, I thought it would solve all my problems with various app installs across home and work computers. Specifically, keeping track of where things are and the Windows path. As I began embracing the ecosystem more I foundshimgen.exe
was key, though had a small quirk I just could not get past.
If you live in the terminal most of the time, you wouldn't notice this, which is why it took me a while to become annoyed. Shims that launch a GUI executed from the command line seem no different from executing their parent, however run it from elsewhere (e.g., Windows Explorer) you'll probably notice a terminal briefly popping up. You have to be quick as its fast (250ms).
Here is an example that was slowed down 25%:
Is itTHAT big of a problem? For most, probably not, but I thought so :P and as usual, more I got into it other little improvements were done. Apart from ridding the terminal popup, it seems much quicker (600ms, by my imprecise screen capture editor).
As mentioned earlier, this application was built off the the foundation laid bykiennq andTheCakeIsNaOH, both whom used C++.
Warning
Though I appreciate the advantages using C++, I had not used it for over 20 years (this was the days of MFC, if anyone remembers) so forgive me for my poor implimentation and antiquated syntax.
As an example of my unfamiliarity, both of them also made use ofwstring
which I continued, though consistently complicated code. I believe in the end I compartmentalized it enough to keep confusion to a minimum.
The great thing I found fromTheCakeIsNaOH code was embedding the shim executable into the actual generator executable. In a way, generating a shim is simply like unzipping itself. Because the shim itself is already compiled, no external resources are needed, however it does require the shim executable to be generic enough to run any parent application. This actually was solved earlier bykiennq since they designed it to replace shims inScoop.
Scoop, unlikeChocolately, uses text files to store shim information such as the parent executable and its location. The shim executable simply reads this information and spawns a new process. These text files do provide an easy way to inspect and alter a shim, however it does double the number of files since each shim has a.exe
and.txt
.
Prefering to keep things neat and tidy, I wanted to rid myself of these files and was inspired byTheCakeIsNaOH. Why not embed this information in the actual shim executable? I found that it wasn't difficult to insert data into an.exe
. Furthermore, version info and icons are similar resources stored in the parent, so may as well copy those over too.
The final task was to solve the initial annoyance of that console window popping up. This, I some how found, was due to Windowsactually having two seperate subsystems for console and GUI apps. Basically, all shim executables were built to run off the console subsystem (i.e. console apps), which in turn would spawn the parent executable process. You should be able to see the issue by now: for GUI shims executing in the Windows subsystem, a process is created breifly in the console subsystem (i.e. the console shim) which is killed after it spawns the window subsystem process for the app.
In short, we want GUI apps to be shimmed with GUI shims and console apps to be shimmed with console shims. Thus two shims need to be built and embedded into the creator. The only difference between the two is the entry point which most would recognize as:
// Console Application Entry Pointintwmain(int argc,wchar_t* argv[])
and
// GUI Application Entry Pointint APIENTRYwWinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PWSTR pCmdLine,int nCmdShow)
As seperate code files, the compiler handles all the details, however diving deeper into this recently, I found actually that the linker does the bifurcating and thus simplify the code and build process immensely (see themakefile for more details).
For being a simple command line utility, much of the code is for user I/O and backwards compatibility with RDS'sshimgen.exe
. I actually started writing most of this prior to fully understanding some its quirks and hence, some options function as I would haveexpected them to. To remedy these nuances, the generator executable is built to behave as close to possible to the originalIF its actually namedshimgen.exe
.
Caution
To be explicit, renamingshim_exec.exe
toshimgen.exe
changes how it functions.
That being said, this only changes how its options are interpreted and not the final shim. A shim made with either will be identical. The specific differences are:
Option | Function | RDSshimgen.exe | shimgen.exe | shim_exec.exe |
---|---|---|---|---|
help | Prints help to the console | (same) | (same) | |
path | Path to the executable to be shimmed | - Paths relative to output - Willnot be expanded. | - Paths relative to output -Will be expanded. | - Can be positional, 1️⃣ - Relative to current directory. |
output | Path to the output shim | - Paths relative to shimgen.exe | (same) | ❔[OPTIONAL] ❔ - Can be positional, 2️⃣ - Relative to current directory - Default: \[current_directory]\[parent_executable].exe . |
command | Adds arguments to executable | ❔[OPTIONAL] ❔ - string w/o spaces or quoted escaped string | (same) | (same) |
iconpath | Icon to be used for shim | ❔[OPTIONAL] ❔ | (not implemented) | (not implemented) |
gui | Forces GUI shim | ❔[OPTIONAL] ❔ - forces shim to exit immediately after running parent | ❔[OPTIONAL] ❔ - forces creation of a GUI shim which by default exits immediately | (same) |
debug | Prints additional info | ❔[OPTIONAL] ❔ | (same) | (same) |
For more detail you can compare the help text of the three:shimgen-h (RDS).txt,shimgen-h.txt, andshim_exec-h.txt.
The shims themselves behave more similarly. There are no differences between shims created withshim_exec.exe
and the compatible version,shimgen.exe
. Shim arguments are prefixed with--shimgen-
however those created withshim_exec.exe
have been relaxed to requiring only--shim.*-
. The differences between the shim arguments are:
Option | RDSshimgen.exe | shim_exec.exe |
---|---|---|
help | -prints help to the console | (same) |
log | - print additional diagnostic info to console | - if console shim, prints to consol - if gui shim executed from console, print to console -if gui shim executed from Windows, print to log file |
waitforexit | - force shim to wait for parent to exit | (same) |
exit | - force shim to exit after executing for parent | (same) |
gui | - force shim to behave as a GUI shim | since it is impractical to convert a console shim to a GUI shim, this behaves exactly likeexit |
usetargetworkingdirectory | - set the working directory to the target pathONLY if the shim was created with relative paths | - set the working directory to the target pathREGARDLESS |
noop | - stop prior to executing parent | (same) |
If you choose to use this instead of theshimgen.exe
provided by Chocolately a PowerShell script,./tools/replace_shimgen.ps1, is available to toggle between the two.
- Get a copy ofVisual Studio. Installing just the "Desktop Development w/ C++" should suffice however the minimum I installed was:
- Microsoft Visual Studio Workload - Core Editor
- Microsoft Visual Studio Component - Core Editor
- Microsoft Visual Studio Workload - Native Desktop
- Microsoft Visual Studio Component Group - Native Desktop Core
- Microsoft Visual Studio Component - Roslyn Compiler
- Microsoft Visual Studio Component - Text Templating
- Microsoft Visual Studio Component - VC Core IDE
- Microsoft Visual Studio Component - VC Tools x86 x64
- Microsoft Visual Studio Component - VC Redist 14 Latest
- Microsoft Visual Studio Component - Windows 11 SDK 22000
- Microsoft Component - MSBuild
- Build using
nmake
. Visual Studio should have installednmake
however other flavors should be able to process the includedmakefile.
If I didn't mention it enough, this app directly follows from the great work of@TheCakeIsNaOH and@kiennq (I owe them 🍺, ☕, 🍰). Also thanks to those working on Scoop andChocolately and being open with its code. It doesn't make us rich but do it for thefeels.
SPDX-License-Identifier: MIT OR Unlicense
About
Make Shims for Executables (like Chocolatey and Scoop)
Resources
License
Stars
Watchers
Forks
Languages
- C++94.1%
- Makefile2.5%
- C1.7%
- PowerShell1.7%