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:
- How do I detect .NET assemblies?
- How can I detect the difference between .NET 2.0 and .NET 4.0 assemblies?
- How can I detect the difference between x86, x64, and AnyCPU .NET assemblies?
And the list goes on and on. But this raises the question…
Why detect .NET assemblies?
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:
- NGEN assemblies
- Install & update COM assemblies using RegAsm
- Install & update assemblies in the GAC (Global Assembly Cache).
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.
How not to do .NET detection
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).
How to detect .NET
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:
- CLI Partition II: Metadata Definition and Semantics (get the PDF) from the ECMA C# and Common Language Infrastructure Standards
- Portable Executable and Common Object File Format Specification (i.e. the PECOFF Spec)
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.
Tell me if you find this useful – how are you using it?
If you find this code useful, tell me how you’re using it in the comments.
Get the C# source
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 Wyatt Says...
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.