PE header setting that can cause an application to fail to launch on Windows 2000

Over the past couple of weeks, I've heard from a few users who tried to run the .NET Framework cleanup tool on Windows 2000 but were unable to.  On these systems, users received errors stating that the program named cleanup_tool.exe could not be run because it is not a valid Win32 application.  This issue was tricky to narrow down, so I wanted to describe the underlying issue in case it helps anyone else who runs into similar issues in the future.

I only started hearing about this issue recently, and I could not reproduce it on my Windows XP, Windows Server 2003 or Windows Vista systems.  So I tried to narrow down the root cause by looking more closely at the recent changes that I made to the cleanup tool.

The one major change that I made over the past month or so was to update the build script for the tool to embed a UAC manifest using the tool mt.exe as described in this MSDN topic.  I did this to cause the cleanup tool to prompt for elevation when it is run on Windows Vista.

The UAC manifest contains settings that did not exist at the time that Windows 2000 shipped, but Windows will ignore manifest settings that it does not recognize.  In addition, I built a version of the cleanup tool without an embedded manifest and sent it to one of the customers, and they indicated that it did not help resolve this invalid Win32 application error.  This meant that the addition of the UAC manifest was not the root cause of this error on Windows 2000.

The only other thing changed recently was that I started building the cleanup tool on a Windows Vista system instead of a Windows Server 2003 system.  Since I didn't have any other changes that could explain this error, I started examining this change in more detail.

I use the IExpress technology to create the self-extracting package for the cleanup tool.  A version of IExpress.exe is installed to %windir%\system32 on all recent versions of Windows, and the cleanup tool build script uses this version of IExpress.exe that comes with the OS.

After discussing this issue with a couple of folks who have debugged similar issues in the past, I used the tool dumpbin.exe that is a part of Visual Studio to compare the portable executable (PE) header information for an older version of the cleanup tool that worked correctly on Windows 2000 and the newest version of the cleanup tool that was not recognized as a valid Win32 application on Windows 2000.  When I did that, I found that the working version of cleanup_tool.exe had the following attributes:

  • Operating system version – 5.02
  • Image version – 5.02
  • Subsystem version – 4.00

The broken version of cleanup_tool.exe had the following attributes:

  • Operating system version – 6.00
  • Image version – 6.00
  • Subsystem version – 5.01

It turns out that Windows 2000 only recognizes EXEs that have subsystem versions less than or equal to 5.00.  I also found out during this investigation that the Windows Vista version of IExpress.exe embeds a subsystem version of 5.01 in the self-extracting EXEs it creates.  In previous versions of Windows, IExpress.exe embeds a subsystem version of 4.00 in the self-extracting EXEs it creates.

After I discovered this, I built an updated version of the cleanup tool using a Windows Server 2003 version of IExpress.exe and had a couple of customers try it on their Windows 2000 systems and the tool extracted and ran as expected.

If you run into any issues where Windows reports that an EXE is not a valid Win32 application, I suggest taking a look at the PE header information for the EXE by using the dumpbin.exe tool to see if you are running into a similar issue to the one I ran into with the .NET Framework cleanup tool.

The version of the cleanup tool located at https://astebner.sts.winisp.net/Tools/dotnetfx_cleanup_tool.zip contains the correct PE header information to allow it to run correctly on Windows 2000 again.  Thank you to the folks who reported this issue to me and helped me test out possible fixes.