"Wyatt Says..." is a collection of articles by Wyatt O'Day talking about wyDay products and the things we've learned along the way.
In today’s article I’m going to talk about an interesting problem: detecting .NET assemblies. More than that, I’ll be talking about detecting some features of .NET assemblies and how you can expand and mold our code for your own uses. The code’s at the bottom of the article, it’s written in C# and licensed under the BSD license. Go get it.
I’ve seen this question pop up in a few different forms:
And the list goes on and on. But this raises the question…
We detect .NET assemblies because we respect humans’ time. Let me explain.
When wyUpdate (our open source updater) installs updates it can do a few things beyond simple patching, registry changing, and file copying. Namely it can:
Which means wyUpdate needs to know whether the executable (or dll) is a .NET assembly, whether it’s strong signed, and what platform target it is (i.e. x86, x64, or Any CPU).
We could ask the user for every file, but that’s such a hassle. Who wants to waste time checking boxes for every exe and dll in their project? Rather than wasting the users’ time we quickly scan the .dll and .exe files for their details when the update is built inside wyBuild.
Do not use LoadLibrary(), or Assembly.Load() functions to load an assembly in memory to then parse it. This breaks when you have an x86 process trying to a load an x64 assembly (or vice versa).
Instead of using LoadLibrary (or one of its brethren) we’ll just treat the executables as dumb files. That is, just run a simple loop over the file and skip over the unneeded parts. You can check out the C# code posted at the bottom of this article, but you should be aware of 2 resources we used when designing the .NET detection algorithm:
The PECOFF spec gives you the general layout of .exe and .dll files, and the CLI Partition II gives .NET specific features that we detect. Namely, is the assembly strong signed, is it built for Any CPU or x86 alone, and what base version of the .NET framework is it built for (2.0 or 4.0).
Also, when you check out the code, notice how the code handles PE32 files versus how it handles PE32+ files. That is to say, 32-bit assemblies have a subtly different layout than 64-bit assemblies.
If you find this code useful, tell me how you’re using it in the comments.
Download the AssemblyDetails C# source. It works with .NET 2.0, 3.0, 3.5, 4.0.
AssemblyDetails ad = AssemblyDetails.FromFile(filename); // ad == null for non .NET assemblies if (ad != null) Console.WriteLine(Path.GetFileName(filename) + ": " + ad.CPUVersion + ", " + ad.FrameworkVersion); else Console.WriteLine(Path.GetFileName(filename) + ": Not a .NET 2.0+ executable.");
Update 7/3/2010: There was a slight bug in the first version. Re-download the code.
Subscribe to the 'Wyatt Says...' RSS Feed and keep up to date on on my articles on updaters, usability, open source C# components, and software licensing.