25

One small function of a large program examines assemblies in a folder and replaces out-of-date assemblies with the latest versions. To accomplish this, it needs to read the version numbers of the existing assembly files without actually loading those assemblies into the executing process.

starblue
  • 51,675
  • 14
  • 88
  • 146
Jay Cincotta
  • 3,922
  • 3
  • 19
  • 16

5 Answers5

42

I found the following in this article.

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);
Joel B Fant
  • 23,536
  • 4
  • 64
  • 67
  • Crap. As always for me, this doesn't work on the Compact Framework. AssemblyName is there, but AssemblyName.GetAssemblyName isn't. – MusiGenesis Oct 09 '08 at 17:10
  • An older trick I used to use (for scanning through plugin assemblies) was to create a sandbox AppDomain, load them in that, and then close down the AppDomain when I was done. Not sure about CF, though. – Joel B Fant Oct 09 '08 at 17:40
  • Won't fit here. But it relies on an object created in the new AppDoain loading the assebly, which means using AppDomain.CreateInstance(), which doesn't seem to be supported in CF. You should ask a new question, link to this one, and specify Compact Framework. – Joel B Fant Oct 09 '08 at 20:39
  • 3
    +1, but there's just one caveat - if you're using code like this and you update your DLLs to target a new CLR (i.e. when upgrading from framework 3.5 to framework 4.0), this will produce a "This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded" error... – Simon MᶜKenzie Oct 01 '12 at 05:49
14

Depending on the files, one option might be FileVersionInfo - i.e.

FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(path)
string ver = fvi.FileVersion;

The problem is that this depends on the code having the [AssemblyFileVersion] attribute, and it matching the [AssemblyVersion] attribute.

I think I'd look at the AssemblyName options suggested by others first, though.

Marc Gravell
  • 927,783
  • 236
  • 2,422
  • 2,784
  • 1
    Comparing the FileVersionInfo like this could be useful as well. An update to a library which fixes bugs but does not change the API could have the same Assembly version but should have a different file version. So in many cases the AssemblyFileVersion may be the one you want to check. – Rob Parker Nov 11 '09 at 17:48
  • Tip: Using FileVersionInfo lives in the System.Diagnostics namespace which you would need to import. – Eirik H Apr 03 '16 at 09:24
  • @eithe although to be fair, you can just press ctrl+. (with the focus on an unresolved type) and the IDE will find it and add it for you... then you don't need to memorise namespaces – Marc Gravell Apr 03 '16 at 09:26
  • True, @MarcGravell, but I was deploying this through rdp in a test area and only had notepad there :) – Eirik H Apr 03 '16 at 18:24
9

Use AssemblyName.GetAssemblyName("assembly.dll");, then parse the name. According to MSDN:

This will only work if the file contains an assembly manifest. This method causes the file to be opened and closed, but the assembly is not added to this domain.

jop
  • 77,529
  • 10
  • 52
  • 52
  • I'm trying not to think of all the hacks I've implemented trying to get a version number without loading the file. Yeesh. – MusiGenesis Oct 09 '08 at 16:54
3

Just for the record: Here's how to get the file version in C#.NET Compact Framework. It's basically from OpenNETCF but quite shorter and exctacted so it can by copy'n'pasted. Hope it'll help...

public static Version GetFileVersionCe(string fileName)
{
    int handle = 0;
    int length = GetFileVersionInfoSize(fileName, ref handle);
    Version v = null;
    if (length > 0)
    {
        IntPtr buffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(length);
        if (GetFileVersionInfo(fileName, handle, length, buffer))
        {
            IntPtr fixedbuffer = IntPtr.Zero;
            int fixedlen = 0;
            if (VerQueryValue(buffer, "\\", ref fixedbuffer, ref fixedlen))
            {
                byte[] fixedversioninfo = new byte[fixedlen];
                System.Runtime.InteropServices.Marshal.Copy(fixedbuffer, fixedversioninfo, 0, fixedlen);
                v = new Version(
                    BitConverter.ToInt16(fixedversioninfo, 10), 
                    BitConverter.ToInt16(fixedversioninfo,  8), 
                    BitConverter.ToInt16(fixedversioninfo, 14),
                    BitConverter.ToInt16(fixedversioninfo, 12));
            }
        }
        Marshal.FreeHGlobal(buffer);
    }
    return v;
}

[DllImport("coredll", EntryPoint = "GetFileVersionInfo", SetLastError = true)]
private static extern bool GetFileVersionInfo(string filename, int handle, int len, IntPtr buffer);
[DllImport("coredll", EntryPoint = "GetFileVersionInfoSize", SetLastError = true)]
private static extern int GetFileVersionInfoSize(string filename, ref int handle);
[DllImport("coredll", EntryPoint = "VerQueryValue", SetLastError = true)]
private static extern bool VerQueryValue(IntPtr buffer, string subblock, ref IntPtr blockbuffer, ref int len);
Bjørn
  • 41
  • 1
0

A .netcore update to Joel's answer, using AssemblyLoadContext:

using System.IO;
using System.Reflection;
using System.Runtime.Loader;

...

// Get current and updated assemblies
AssemblyName currentAssemblyName = AssemblyLoadContext.GetAssemblyName(currentAssemblyPath);
AssemblyName updatedAssemblyName = AssemblyLoadContext.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);

I found this useful in analyzing DLLs running (and locked) by a Windows Service, a much cleaner alternative to creating a separate AppDomain.

Eric Patrick
  • 1,797
  • 2
  • 16
  • 26