Donnerstag, 17. Juli 2008

Binding failures in mixed-mode applications

In .NET applications, assemblies are often loaded dynamically as soon as they are needed. This is not only true for explicitly loaded assemblies (via Assembly.Load), but also for other assemblies linked 'statically'.

In a pure .NET application, if such an assembly doesn't exist (perhaps because the user messed up his installation and deleted the file), an exception is thrown, which you can catch and handle appropriately (e.g. with an error message to the user).

In a mixed-mode application, having a native main program which uses some .NET components, things are more complicated. In my case, the .NET assembly is linked to the program with the #using directive (you could also use the project settings, but #using has the advantage that only the necessary files are recompiled if the assembly changes). This means that it will get loaded as soon as managed code from the corresponding object file is executed.
But typically, that code is called from unmanaged code. There is no place to put a try / catch for the binding failure!

If you don't do anything at all about this, your application may crash with a cryptic (to the user) message dialog, perhaps containing the Windows error code for .NET exceptions (0xE0434F4D). Not very helpful. What can you do?
  • Try to use a global 'unhandled exception handler' or a try / catch on a more global level. Unfortunately, while the binding failure propagates to your handler, it can cause other exceptions. In my case, a STATUS_SXS_INVALID_DEACTIVATION, which is even more cryptic.
  • Always call the code of the file with the #using directive only from managed code. Which means that you have to remember to insert otherwise useless wrapper functions in other source files just so that the exception can be caught. Definitely not a nice solution.
  • Perhaps you can use MDAs? Indeed, they fire also in this situation. But (quite apart from the problem that they must be enabled globally in the registry or through an environment variable) there is no good API to handle MDA messages. There is one in the .NET hosting interfaces (ICLROnEventManager), but do you want to host the CLR just for this? Certainly not.
  • Try to load the required assemblies yourself before they are loaded automatically. Then you can catch the binding failure and do what you like. This also is not a nice solution, but at least the code for that is not very large and locally confined. You just have to remember to adapt it if you add or remove an assembly, or update it to a new version (for signed assemblies).
The last one is the way I chose. I'm still not happy with it - you shouldn't have to do this test yourself - but I can't think of anything better. If you have many signed assemblies which often change their version and can't adapt the code automatically, however, the solution won't be good for you either. In my case, most assemblies always have the same version as the main application (they are versioned automatically by the automatic build), so that's no problem for me.

Labels:

0 Kommentare:

Kommentar veröffentlichen

Abonnieren Kommentare zum Post [Atom]

<< Startseite