Decrypt my World

Cryptography, Security, Debugging and more!

How to get assembly version without loading it

How to get assembly version without loading it

Rate This
  • Comments 11

Hi all,

The other day I was trying to add a simple autoupdate functionality to a little tool I developed, and I needed to check the version of current assembly against the udpated one. If current assembly was older than the updated one, I needed to substitute the older one with the newer. Plain and simple.

This was my first attempt to achieve this (code has been simplified):

using System.Reflection;
using System.IO;

...

// Get current and updated assemblies
Assembly currentAssembly = Assembly.LoadFile(currentAssemblyPath);
Assembly updatedAssembly = Assembly.LoadFile(updatedAssemblyPath);

AssemblyName currentAssemblyName = currentAssembly.GetName();
AssemblyName updatedAssemblyName = updatedAssembly.GetName();

// Compare both versions
if (updatedAssemblyName.Version.CompareTo(currentAssemblyName.Version) <= 0)
{
    // There's nothing to update
    return;
}

// Update older version
File.Copy(updatedAssemblyPath, currentAssemblyPath, true);

But File.Copy failes because current assembly is in use. Why? Because of Assembly.LoadFile. When we load an assembly no other process (including ours) can change or delete the file because we are using it. The issue is that we can't unload an assembly that we loaded in an AppDomain unless the AppDomain itself gets unloaded. Here I'm using the default AppDomain which will only get unloaded when the application exits. So then I tried creating a new AppDomain, load the assemblies in there and unload the AppDomain afterwards before changing the file. It didn't help either. So...

How can we get the assembly version without loading the assembly? The solution is easy:

using System.Reflection;
using System.IO;

...

// Get current and updated assemblies
AssemblyName currentAssemblyName = AssemblyName.GetAssemblyName(currentAssemblyPath);
AssemblyName updatedAssemblyName = AssemblyName.GetAssemblyName(updatedAssemblyPath);

// Compare both versions
if (updatedAssemblyName.Version.CompareTo(currentAssemblyName.Version) <= 0)
{
    // There's nothing to update
    return;
}

// Update older version
File.Copy(updatedAssemblyPath, currentAssemblyPath, true);

AssemblyName.GetAssemblyName won't load the assembly, so we can change the file afterwards. 

I hope this helps.

Kind regards,

 

Alex (Alejandro Campos Magencio)

 

PS: In case you are interested, I will post my simple autoupdate code one of these days, once I have time to change a couple of things on it.

  • Great solution!  I've been searching for that a long time ago!

    Thank you!

  • Great tip. Just what I was looking for. Thanks!

  • Faced the same issues, and considered going down the Appdomain path , till I saw your post. Good one.

  • It's interesting reading this article and comparing .NET with Java. I had to do this in Java some months ago. There were several problems:

    - JAR Version is an OPTIONAL value stored in the JAR's manifest file. So you can't rely in having a version value in every JAR file.

    - You can write the on-disk JAR file on the fly without problems. You only need to reload the JAR file afterwards to get the updated one to memory.

    So, in this little comparison, one up, one down. Great article!

  • Dang, that was easy!

    First I have used Assembly.ReflectionOnlyLoad with CustomAttributeData class and that actually worked, but I had to apply the same fix to our older solution, which is compiled with .NET 1.1 and those methods were not introduced yet. Luckily, quick search brought me to your solution, which is much easier and cleaner.

    Thanks!

  • Doesn't seem to work on Compact Framework 2.0 though.

    AssemblyName.GetAssemblyName is not supported.

    :-(

  • HI,

    Thanks for the post. I'd like to ask still for your support.  I have to identify the assembly's version in order to identify where the file (a dll in this case) will be installed. I have the following call:

    PS H:\temp\infra\GlobalAssemblyCache> $name= [System.Reflection.AssemblyName]::GetAssemblyName("InfraManagedTracing.dll")

    and the output I get is the following:

    <i>

    Exception calling "GetAssemblyName" with "1" argument(s): "Could not load file or assembly 'H:\Documents and Settings\orlando\InfraManagedTracing.dll' or one of its dependencies. The system cannot f

    ind the file specified."

    At line:1 char:57

    + $name= [System.Reflection.AssemblyName]::GetAssemblyName <<<< ("InfraManagedTracing.dll")

       + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

       + FullyQualifiedErrorId : DotNetMethodException</i>

    apparently the function still requires me to load the assembly, something I defnitely don't want. Any cue what am I missing? Thanks in advance,

  • Thanks for this code. I've been trying to find a way to do this without actually loading the file.

  • Thx for teh code! Nice sollution.

  • "Duh", I said to myself after reading. Thanks!

  • THANK YOU **

Page 1 of 1 (11 items)
Leave a Comment
  • Please add 7 and 7 and type the answer here:
  • Post
Translate This Page