Yesterday we developed a simple Shim application in order to fail gracefully when our application's entry assembly doesn't have enough permission to meet its minimum grant set, and therefore won't be loaded. However, there were quite a few problems with that version of the Shim. Today, lets improve on it a bit, to make a nicer shim application.
One of the issues with yesterday's solution was that you needed to ship two executables with your application in order for the solution to work, and you had to rely on your customers to not run the wrong application. Another oversight was that there was no way to pass in command line parameters, which could be a show-stopper for a lot of apps. Finally, the version of the Shim yesterday was bound to one specific application, main.exe.
I've updated the shim to solve all of these problems. Instead of binding to main.exe, the application configuration file can be used to specify which assembly is the real entry point. And that assembly isn't stored in the file system, instead we embed it into the shim itself as a resource. This prevents users from being confused by having multiple executables for your application. Finally, we pass any parameters that the shim received to the application itself.
The updated Shim code looks like this:
It would then be compiled with a command like:
And provided a config file similar to:
You'll notice that the code to detect that the entry assembly could not be loaded is much neater now. That's because we separate loading the assembly from actually invoking its main method. This means that any load exception that we do see must have been caused by failure to load the entry assembly or one of its dependencies, and wasn't caused by the application itself.
Once the assembly is loaded, we check to see if it needs any parameters ... if it does, we pass along the parameters the Shim received, otherwise we pass it empty parameters. Finally, we check to see if the entry point returns a value, and pass that along if it does.
One final thing to note is that we only need to verify that the config file specified an entry assembly, and that the assembly was in the resources. Once we load it from the resource stream, we can simply Assert all the other error conditions because the end user does not have control over them anymore. They can only specify which resource that we embeded into our assembly should be run, not the resource code itself.
Overall this design seems much nicer than yesterday's design, but there are still some improvements that could be made. More on that tomorrow.