In another useless project, I decided to find out why even trivial programs created with the Open Watcom compiler refuse to run on Windows NT 3.1. Attempting to start an executable failed with
foo.exe is not a valid Windows NT application. But this time, the surgery was successful.
There turned out to be several problems, some related and some not. The biggest issue was the problem previously discussed in this post. To recap, the Open Watcom runtime calls the
GetEnvironmentStringsA API, which does not exist on NT 3.1. This also causes trouble on Win32s; although newer Win32s versions do implement
GetEnvironmentStringsA, it just always fails.
The solution is to use the original
GetEnvironmentStrings API (which is equivalent to
GetEnvironmentStringsA) . But that’s not enough.
There is another related problem, which is that the runtime calls the
FreeEnvironmentStringsA API during termination. This API does not exist on NT 3.1; it was only added in NT 3.5 together with
GetEnvironmentStringsA/W. On NT 3.1 there was nothing to free so the API wasn’t necessary.
The solution is easy enough, query the (already loaded)
KERNEL32.DLL handle and then obtain the address of
FreeEnvironmentStringsA. If this fails (as it will on NT 3.1), just don’t call the function because there is nothing to free anyway.
This additional logic adds size to the runtime, so I decided to build a separate variant of the relevant startup module and call it
maint31r.obj (for the default register calling convention). Users must link this module in the unlikely case that they wish to build NT 3.1 compatible applications.
But that’s still not enough. Windows NT 3.1 (but not NT 3.5 and later) refuses to run applications marked as needing the Windows subsystem version 4.0. That’s what causes the
foo.exe is not a valid Windows NT application error.
However, that is easy to avoid during linking. The linker (
wlink) needs to be passed
runtime win=3.10 when linking, which sets the subsystem to Windows and version to 3.10, as required by NT 3.1.
When using the compile and link utility, this can be done as follows:
wcl386 winhello.c mainnt31.obj -bt=nt -l=nt_win -"runtime win=3.10"
Note that the full path to
maint31r.obj needs to be supplied (it will be eventually delivered as
%WATCOM%\lib386\nt\maint31r.obj). With everything in place, it is possible to create executables that run on Windows NT 3.1 from 1993, as the first screenshot demonstrates.
The same executable happily runs on later NT versions, including Windows 10, and it also runs under Win32s.