private string use32bitAssembly(Dictionary <string, Assembly> assemblyLocations)
 {
     if (assemblyLocations != null && assemblyLocations.Count > 0)
     {
         string osFolder = Path.GetDirectoryName(Environment.GetFolderPath(Environment.SpecialFolder.System));
         foreach (Assembly a in assemblyLocations.Values)
         {
             try
             {
                 if (a.GlobalAssemblyCache)
                 {
                     if (a.Location.StartsWith(osFolder, StringComparison.OrdinalIgnoreCase))
                     {
                         continue;
                     }
                 }
                 AssemblyDetails ad = AssemblyDetails.FromFile(a.Location);
                 if (ad != null)
                 {
                     if (ad.CPUVersion == CPUVersion.x86)
                     {
                         return(a.FullName);
                     }
                 }
             }
             catch
             {
             }
         }
     }
     return(string.Empty);
 }
        /// <summary>
        /// Get the AssemblyDetails of a .NET 2.0+ assembly.
        /// </summary>
        /// <param name="file">The file to get the AssemblyDetails of.</param>
        /// <returns>An AssemblyDetails object for .NET 2.0+ assemblies. Null otherwise.</returns>
        public static AssemblyDetails FromFile(string file)
        {
            AssemblyDetails assembInfo = new AssemblyDetails();

            using (FileStream s = new FileStream(file, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader r = new BinaryReader(s))
                {
                    byte[] bytes = r.ReadBytes(2);

                    // Verify file starts with "MZ" signature.
                    if ((bytes[0] != 0x4d) || (bytes[1] != 0x5a))
                    {
                        // Not a PE file.
                        return(null);
                    }

                    // Partion II, 25.2.1

                    // OFFSET_TO_PE_HEADER_OFFSET = 0x3c
                    s.Seek(0x3c, SeekOrigin.Begin);

                    // read the offset to the PE Header
                    uint offset = r.ReadUInt32();

                    // go to the beginning of the PE Header
                    s.Seek(offset, SeekOrigin.Begin);

                    bytes = r.ReadBytes(4);

                    // Verify PE header starts with 'PE\0\0'.
                    if ((bytes[0] != 0x50) || (bytes[1] != 0x45) || (bytes[2] != 0) || (bytes[3] != 0))
                    {
                        // Not a PE file.
                        return(null);
                    }


                    // It's a PE file, verify that it has the right "machine" code.
                    // Partion II, 25.2.2
                    //
                    ushort machineCode = r.ReadUInt16();

                    // IMAGE_FILE_MACHINE_AMD64 (aka x64) = 0x8664
                    // IMAGE_FILE_MACHINE_I386 (aka x86) = 0x14c
                    if (!(machineCode == 0x014c || machineCode == 0x8664))
                    {
                        // Invalid or unrecognized PE file of some kind.
                        return(null);
                    }


                    // Locate the PE_OPTIONAL_HEADER


                    // The PE_FILE_HEADER is 20bytes long we already
                    // read the 2 byte machine code (hence 18byte seek)
                    s.Seek(18, SeekOrigin.Current);

                    ushort magic = r.ReadUInt16();

                    switch (magic)
                    {
                    case 0x10b:                             // PE32

                        // set to AnyCPU for now - we'll check later if image is x86 specific
                        assembInfo.CPUVersion = CPUVersion.AnyCPU;

                        break;

                    case 0x20b:                             // PE32+ (aka x64)
                        assembInfo.CPUVersion = CPUVersion.x64;
                        break;

                    default:                             // unknown assembly type
                        return(null);
                    }


                    // Read the SectionAlignment & FileAlignment for
                    // conversion from RVA to file address
                    s.Seek(30, SeekOrigin.Current);

                    uint sectionAlignment = r.ReadUInt32();
                    uint fileAlignment    = r.ReadUInt32();


                    // go to 'NumberOfRvaAndSizes' in the PE Header
                    // at 92/108 from start of PE Header for PE32/PE32+
                    s.Seek(magic == 0x10b ? 52 : 68, SeekOrigin.Current);

                    // verify that the number of data directories is 0x10.

                    uint numDataDirs = r.ReadUInt32();           // Partition II, 25.2.3.2

                    if (numDataDirs != 0x10)                     // Partition II, 25.2.3.2
                    {
                        // Invalid or unrecognized PE file of some kind.
                        return(null);
                    }


                    // Go to the CLR Runtime Header
                    // at 208/224 from start of PE Header for PE32/PE32+
                    s.Seek(112, SeekOrigin.Current);


                    // Check for the existence of a non-null CLI header.
                    // If found, this is an assembly of some kind, otherwise
                    // it's a native PE file of one kind or another.

                    uint rvaCLIHeader = r.ReadUInt32();                      // Partition II, 25.2.3.3, CLI Header (rva)
                    // uint cliHeaderSize = UIntFromBytes(pPEOptionalHeader + 212); // Partition II, 25.2.3.3, CLI Header (size)

                    if (rvaCLIHeader == 0)
                    {
                        // Not an assembly.
                        return(null);
                    }


                    // Partition II, 25.3.3 (CLI Header)


                    // Go to the begginning of the CLI header (RVA -> file address)

                    /*
                     * -> Converting from Relative Virtual Address to File Address:
                     *
                     * FA = RVA - sectionAlignment + fileAlignment
                     *
                     *         The section alignment in memory is sectionAlignment (usually 0x2000),
                     * and since the RVA for the CLR header is 2008, on subtracting 2000 from
                     * 2008, the difference comes to 8. Thus, the CLR header is placed 8 bytes
                     * away from the start of the section.
                     *
                     *         A file on disk has the alignment of fileAlignment (usually 512 bytes).
                     * Therefore, the first section would start at position 512 from the start
                     * of the file. As the CLR is 8 bytes away from the section start, 8 is added
                     * to 512, (section start for a file on disk), thereby arriving at a value of 520.
                     * The next 72 bytes (0x48) are picked up from this position, since they
                     * constitute the CLR header, and they are loaded at location 0x4002008.
                     *
                     */

                    // Also, skip the CLI header size = 4 bytes
                    s.Seek((rvaCLIHeader - sectionAlignment + fileAlignment) + 4, SeekOrigin.Begin);

                    ushort majorVersion = r.ReadUInt16();
                    ushort minorVersion = r.ReadUInt16();

                    // 2.5 means the file is a .NET Framework 2.0+ assembly
                    if (!(majorVersion == 2 && minorVersion == 5))
                    {
                        return(null);
                    }


                    // RVA for the MetaData (we'll read the metadata later)
                    uint rvaMetaData = r.ReadUInt32();
                    s.Seek(4, SeekOrigin.Current);                     // skip the size

                    // Partition II, 25.3.3.1

                    // read the CLI flags
                    uint cliFlags = r.ReadUInt32();

                    // Detect if compiled with Platform Target of "x86"
                    // COMIMAGE_FLAGS_32BITREQUIRED = 0x2;
                    if (assembInfo.CPUVersion == CPUVersion.AnyCPU && (cliFlags & 0x2) == 0x2)
                    {
                        assembInfo.CPUVersion = CPUVersion.x86;
                    }

                    // Detect if the assembly is built with a strong name
                    // CLI_FLAG_STRONG_NAME_SIGNED = 0x8;
                    assembInfo.StrongName = ((cliFlags & 0x8) == 0x8);

                    s.Seek((rvaMetaData - sectionAlignment + fileAlignment) + 12, SeekOrigin.Begin);


                    // Read the framework version required (meta data - Partition II, 24.2.1 - pg 200)

                    // read the version string length
                    int versionLen = r.ReadInt32();

                    char[] versionStr = r.ReadChars(versionLen);

                    // read the .NET framework version required from the meta-data
                    //Note: we only read the first 2 numbers of the version - if you want to
                    //      detect beta vs. rc vs. rtm, then read the whole version.
                    // We assume no one will be stupid enough to use beta created exes/dlls in the wild.
                    if (versionStr[1] == '2' && versionStr[3] == '0')
                    {
                        assembInfo.FrameworkVersion = FrameworkVersion.Net2_0;
                    }
                    else if (versionStr[1] == '4' && versionStr[3] == '0')
                    {
                        assembInfo.FrameworkVersion = FrameworkVersion.Net4_0;
                    }
                    else
                    {
                        assembInfo.FrameworkVersion = FrameworkVersion.Unknown;
                    }
                }
            }

            return(assembInfo);
        }