BCUninstaller/Bulk-Crap-Uninstaller · error · IOException

Not a Windows .exe file.

Error message

Not a Windows .exe file.

What it means

Thrown by FilesystemTools.CheckExecutableMachineType(string filename) when filename does not end in ".exe" (case-insensitive, invariant culture). The method reads the PE header at offset 0x3C, which only exists in Windows PE executables; non-.exe files are rejected up front with IOException rather than being misparsed.

Source

Thrown at source/KlocTools/Tools/FilesystemTools.cs:25

using System.IO;
using System.Management;
using System.Runtime.InteropServices;
using Klocman.Extensions;

namespace Klocman.Tools
{
    public static class FilesystemTools
    {
        /// <summary>
        /// Check the architecture of the executable. E.g. 64bit.
        /// Returns Unknown if the architecture is unsupported or not specified.
        /// </summary>
        /// <param name="filename">Full path to the executable file.</param>
        public static MachineType CheckExecutableMachineType(string filename)
        {
            if (!filename.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
            {
                throw new IOException("Not a Windows .exe file.");
            }

            using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                stream.Position = 0x3c;
                var fileData = new byte[1024];

                var bytesRead = stream.Read(fileData, 0, 1024);

                for (var i = 0; i < bytesRead; i++)
                {
                    // Look for the PE signature (PE\0\0)
                    if (i + 5 >= bytesRead) break;
                    if (fileData[i] != 0x50) continue;
                    if (fileData[i + 1] != 0x45 || fileData[i + 2] != 0 || fileData[i + 3] != 0) continue;

                    // Join two bytes representing the architecture
                    var machineId = fileData[i + 5] << 8 | fileData[i + 4];

View on GitHub (pinned to 608321de98)

Solutions

  1. Verify the extension is .exe before calling, or branch to a DLL-aware PE reader if you need DLL support.
  2. Validate user input at the UI layer and restrict the open dialog filter to *.exe.
  3. If you genuinely need machine type for a DLL, copy/extend the PE-parsing logic without the extension gate.

Example fix

// before
var mt = FilesystemTools.CheckExecutableMachineType(path); // path may be .dll

// after
if (!path.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
    return MachineType.Unknown;
var mt = FilesystemTools.CheckExecutableMachineType(path);
Defensive patterns

Strategy: validation

Validate before calling

if (!filename.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
    return MachineType.Unknown;
var mt = FilesystemTools.CheckExecutableMachineType(filename);

Type guard

static bool IsExePath(string path)
    => path != null && path.EndsWith(".exe", StringComparison.OrdinalIgnoreCase);

Try / catch

try { return FilesystemTools.CheckExecutableMachineType(path); }
catch (IOException) { return MachineType.Unknown; }

Prevention

When it happens

Trigger: Calling CheckExecutableMachineType with a path whose extension is not .exe — e.g. a .dll, .com, .scr, or a path with no extension. Note: .dll files DO contain PE headers but are still rejected because of this guard.

Common situations: User-selected file that is actually a DLL or a shortcut, file with no extension, path computed from a string that lost its extension, or assuming the tool works on any PE image.

Related errors


AI-assisted analysis of BCUninstaller/Bulk-Crap-Uninstaller@608321de98 (2026-08-13). Data as JSON: /api/errors/bf05e6badde96226. Report an issue: GitHub.