How to detect for which .NET Framework version an assembly was built?
Posted: (EET/GMT+2)
In certain situations you need to be aware for (against) which .NET Framework version an assembly (executable or library, you decide) was built (compiled, written). For instance, you might have used Visual Studio 2002 or 2003 to develop a library and then an application (such as Visual Studio 2005) can load it as an add-in. Or, like I mentioned yesterday, you try to reference an assembly built for .NET 2.0 with Delphi 2006.
Now, .NET assemblies can indeed work in a mixed-version environment, but sometimes this just isn't the case. In these situation you need to be able to determine which .NET Framework version was used to build the given assembly.
So the question is: how do you easily determine this version? The information is stored in the file's CLR header, and you can use the dumpbin.exe utility that comes with Visual Studio to see this header:
dumpbin /clrheader myassembly.exe
However, I've noticed that this method is not a very clear one (it always appears to say version 2.0), and thus I wanted to build my own solution with C#. I named my application simply Show Assembly Version, and when you browse or drag-and-drop a file onto the main window, the .NET version of the assembly is shown:
To programmatically determine the assembly's original .NET version, you can use the managed (i.e. .NET) APIs, for instance Assembly.LoadFrom, or an unmanaged API function called GetFileVersion from MSCOREE.DLL. The header file MSCOREE.h defines this function as follows:
STDAPI GetFileVersion(LPCWSTR szFilename, LPWSTR szBuffer, DWORD cchBuffer, DWORD* dwLength);
Translated into C#, the wrapper around this function would be:
using System.Runtime.InteropServices;
...
[DllImport("mscoree.dll", CharSet=CharSet.Unicode)]
public extern static int GetFileVersion(
string szFilename, StringBuilder szBuffer,
int cchBuffer, out int dwLength);
To call this function, my sample application uses the following code:
private void UpdateVersionInformation()
{
string filename = selectedFileLabel.Text;
StringBuilder buf = new StringBuilder(20);
int len = 0;
int result = NetCoreApi.GetFileVersion(filename,
buf, buf.Capacity, out len);
if (result == 0)
{
if (len > 0)
{
dotNetVersionLabel.Text = ".NET " + buf.ToString();
}
else
{
dotNetVersionLabel.Text = "Unknown .NET version";
}
}
else
{
const uint COR_E_BADIMAGEFORMAT = 0x8007000B;
if ((uint)result == COR_E_BADIMAGEFORMAT)
{
dotNetVersionLabel.Text =
"File appears not to be a .NET assembly";
}
else
{
dotNetVersionLabel.Text = "Error " + result;
Marshal.ThrowExceptionForHR(result);
}
}
}
The function returns a simple string such as "v1.1.4322" or "v2.0.50727", which I then store in a label. If you select a non-.NET application such a regular native Win32 application, the message "File appears not to be a .NET assembly" is shown.
In these cases GetFileVersion returns 0x8007000B (-2147024885 decimal) which equals to the constant COR_E_BADIMAGEFORMAT. Note how I'm using the Marshal.ThrowExceptionForHR call to raise an exception in case the given HRESULT is something else. See MSDN for more info.