GuyPark - Online Consulting

BadImageFormatException

What does this mean?

 

Into

When the OS loads in a DLL using the Assembly.LoadFile() method, it has 4 things that are checked.

  1. the Version Number of the DLL (we can override this, see this article).
  2. the PublicKey
  3. the Culture; and
  4. the Architecture (x86/x64) of the DLL.

 

Coming into this issue recently, I was stumpped as all of the above we're the same, my DLL version was right, there was no public key, the culture was Neutral and my architecture was right as all my DLL's had been compiled as MSIL (Any CPU).

What I did not know was that a 3rd party DLL I had, which I thought was MSIL, was in fact x86.

What was helpful to know, is that if you have an x86 (32bit) library, you need to fix the main executable to be the architecture of the lowest common denominator (64bit OS will emulate 32bit).  I had my EXE set as MSIL, which I thought would be fine, but what I actually needed was to fix it to the lowest architecture (32bit) to have it run.   

After setting my main executable's build as a x86, my loaded DLL's referenced (x86 3rd party) libraries caused no problem and we're able to load.

 

What I learned.

Microsoft's .NET is very top-down when it comes to the top checklist.  

  1. Build your executable to the architecture you intend to use.  (You can build everything else as MSIL and flip the architecture on the executable to produce each version)
  2. If your library (DLL) has a public key, everything under it requires a public key
  3. .NET is compatible downwards (obviously not upwards as people keep itterating) as long as that version of .NET is installed.

The main thing to note is to keep an eye on your hierarchy of libraries and what version/publickey(signing)/architecture they are.  I ended up building it into a diagnostic tool to display all loaded DLL's and what they were.

Here's the code extract if you're interested.

         /// <summary>
        /// Gets the Products the Assembly info in a nice informative string.
        /// </summary>
        /// <param name="asm">Asm.</param>
        private string DLLInfo(Assembly asm) {
            AssemblyName info = asm.GetName();
            string pubKey = string.Empty;
            if (info.GetPublicKeyToken ().Length > 0) 
                pubKey = "publicTokenKey=" + System.Convert.ToBase64String(info.GetPublicKeyToken());

            return string.Format("{0,-30}  {1,11}  [.NET {5,-10} {2,4} {4}] {3}", info.Name, info.Version.ToString(), info.ProcessorArchitecture.ToString(), pubKey, info.VersionCompatibility.ToString(), asm.ImageRuntimeVersion);
        } 

        /// <summary>
        /// Diagnoses the libraries.
        /// </summary>
        public void ListLibraries() {
            List<Assembly> asms = new List<Assembly>(AppDomain.CurrentDomain.GetAssemblies());
            asms.Sort(new VersionSorter());

            foreach(Assembly asm in asms) {
                Console.WriteLine (" " + DLLInfo (asm));
            }
        } 

        /// <summary>
        /// Comparer to sort Assemblies by .NET version then by Name
        /// </summary>
        private class VersionSorter : IComparer<Assembly> {
            public int Compare(Assembly x, Assembly y) {
                // Compare .NET versions
                int diff = (x.ImageRuntimeVersion.CompareTo(y.ImageRuntimeVersion));
                if (diff != 0)
                    return diff * -1;

                // If same, then compare name...
                return x.GetName().Name.CompareTo(y.GetName ().Name);
            }
        }