NOTE: Scroll to the appendix at the bottom If you’re using DJGPP, Free Pascal, or Free Basic. They use a different and incompatible DPMI host.
Sooner or later, everyone who plays around with 32-bit DOS programming using Open Watcom C/C++ is probably going to see one of these errors when they try to run their programs:
Stub exec failed: \dos4gw.exe No such file or directory
Can't run DOS/4G(W)
I don’t know if there are other causes for the second version, but the first one is pretty clear about what went wrong. You forgot to copy
dos4gw.exe into the same folder as your EXE file… but why is this necessary?
The important thing to understand is that DOS/4GW is a special bundle version of DOS/4G. It was used so widely back in the day because it was hundreds if not a thousand dollars cheaper than the alternatives, but it was designed to upsell programmers with artificial restrictions… and one of those restrictions was having
dos4gw.exe in a separate file.
Luckily, Open Watcom C/C++ supports other extenders which can be bundled into the EXE file. One of them (DOS/32A) is even drop-in compatible with DOS/4GW to the point that you can swap it out without recompiling. (Good for lifting restrictions and resolving bugs in closed-source software.)
The extenders which are bundled with Open Watcom C/C++ (and the meanings of their names) are:
- DOS/4GW by Tenberry Software, Inc. (DOS on 4Gig, Watcom Bundle Edition. The original site is gone now that the author has passed away, and the Watcom manual was always a paid add-on, but it’s API-compatible with DOS/32A and there are bundled and third-party docs.)
- DOS/32A by Narech K. (DOS on 32-bit, Advanced Version)
- PMODE/W by Thomas Pytel and Charles Sheffold (Protected Mode, Watcom Edition)
- CauseWay by Devore Software & Consulting (I couldn’t find an explanation of the name’s origin. The original site is gone, but there’s a manual bundled with Open Watcom C/C++.)
(DOS/4G was named after the memory limit for a 386, in contrast to Tenberry’s other product, the 286-compatible DOS/16M.)
Other extenders people have successfully used with Open Watcom C/C++ include:
- D3X by Daniel Borca (DOS 32-bit Extender, The original site is gone, but it’s in the Wayback Machine including the download)
- DPMIONE (GitHub) by Sudley Place Software (DPMI 1.0 implementation. In fact, it claims to be the only DPMI 1.0 implementation complete enough that it can substitute for Windows 3.1x’s internal DPMI extender… though functionality will still be reduced.)
- HX DOS Extender (mirror, Dr-DOS Wiki) by Andreas “Japheth” Grech (Best option for writing Win32 console applications or making limited use of the Win32 GUI APIs and having your program run under plain old DOS.)
- PMODE/WI by Thomas Pytel and Charles Sheffold (A cut-down version of PMODE/W for demoscene applications where compactness is more important than features and it’s acceptable for problems during development to be more cryptic.)
- WDOSX by Michael Tippach (Wuschel’s DOS eXtender)
I haven’t tried it, but, as described, it sounds like an explicit design goal of HX DOS Extender is to allow you to write a Win32 console binary (which would run on 64-bit Windows 10), and then stub it with HX and have it simultaneously be compatible with platforms like FreeDOS, MS-DOS 6.22, and DOSBox.
Note that, for WDOSX, if you want to have the stub bundled in automatically as part of your linker script rather than using its
stubit.exe, you’ll need to run
stubit -extract to extract the requisite
wdosxle.exe file. (See
DOC/README.TXT in the WDOSX distribution archive for more details.)
One other bit of trivia: If you look in the Open Watcom manuals and see mention of Phar Lap… that’s the company that co-designed VCPI with Quarterdeck and produced the expensive proprietary DPMI extender that Microsoft licensed for Microsoft C/C++… it’s named after a famous race horse.
Before I demonstrate how to use them, I’d first like to address a few potential tripping points I encountered while preparing this:
undefined system name: ...
System names are defined in the
wlink.lnk file in the
bin* folder containing the
wcl386 command you’re running. and the version of
wlink.lnk bundled with Open Watcom v2 has a few more of them than Open Watcom v1.9, such as the one for PMODE/WI.
wlink.lnk to see what’s available and either choose one of the ones listed in there, edit it to add one, or upgrade Open Watcom. (Note that I’ve had Open Watcom v2 builds produce broken
.com files, so don’t upgrade blindly.)
This is a ... executable when trying to run the EXE
If you saw one of these messages when trying to run your EXE…
This is a CauseWay executable This is a DOS/32 Advanced DOS Extender (LE-style) executable This is a DOS/32 Advanced DOS Extender (LX-style) executable This is a DOS/32A DOS Extender w/ Standard stub (LE-style) executable This is a DOS/32A DOS Extender w/ Standard stub (LX-style) executable This is a DOS/32A DOS Extender w/ Configurable stub (LE-style) executable This is a DOS/32A DOS Extender w/ Configurable stub (LX-style) executable This is a PMODE/W executable This is a PMODE/WI executable
… it probably means that your compiler emitted one of the following warnings:
Warning! W1093: cannot open cwstub.exe Warning! W1093: cannot open dos32a.exe Warning! W1093: cannot open stub32a.exe Warning! W1093: cannot open stub32c.exe Warning! W1093: cannot open pmodew.exe Warning! W1093: cannot open pmodewi.exe
…and fell back to linking the default stub for non-MZ
To solve the problem for all the listed stubs except
pmodewi.exe, you need to either add your
binw folder to the
PATH (but not before
binl) or copy or symlink them from the
binw folder to the
binl folder. (I chose the latter since I didn’t want to throw an arbitrary pile of files into my
wlink‘s manual doesn’t mention any other variable it check for a search path.)
This bit of shell script should do the trick when run from inside
for stub in dos32a.exe stub32a.exe stub32c.exe pmodew.exe; do ln -s ../binw/$stub . done
As for PMODE/WI (the cut-down demoscene version of PMODE/W), having a predefined
system for it doesn’t mean that it’s bundled with Open Watcom C/C++, so you’ll need to download it yourself and unpack it into
binl as appropriate. (Renamed to all-lowercase if you’re on Linux.)
An interesting side note is that the
This is a ... executable messages in Open Watcom’s default EXE stub are actually dynamically generated from the
osname values in the
wlink.lnk file in your
binl folder. (“This is a Windows executable” made general across all outputs that must have a DOS MZ-format EXE at the beginning.)
That means that, if you edit the appropriate
wlink.lnk file to make a copy of the entry for an extender that supports taking the binary as an argument (eg.
system begin joke option osname='high-security process. You do not have permission to run this' libpath %WATCOM%/lib386 libpath %WATCOM%/lib386/dos libpath %WATCOM%/lib386/l32 format os2 le end
wcl386 -l=joke whatever.c will produce a
whatever.exe file that says “This is a high-security process. You do not have permission to run this executable” if you try to run it in DOSBox, but runs as expected if you run it as
dos32a whatever.exe instead.
…and since the
op stub=dos32a.exe line uses the same mechanism as
/STUB for setting the EXE stub in PE executables. That means that the same “does one thing in DOS and another in Windows” behaviour of PE EXEs is also available to DPMI EXEs with a slight twist.
(Not that you need to edit
wlink.lnk to change the stub. The proper way is to create a file with a name like
whatever.lnk, put your
op stub=decoy.exe or
option stub=decoy.exe in there, and then call
@whatever.lnk as one of the arguments.)
If you pick a DPMI extender that supports taking the EXE as an argument, like DOS/32A, then you can compile
decoy.c as a real-mode DOS EXE with
wcl -l=dos decoy.c, specify
op stub=decoy.exe in your linker script, and have a DOS EXE which behaves one way when executed directly and another when executed as
dos32a whatever.exe. (Though, since the DPMI extender uses LE for the 32-bit part, I think it’ll ignore the decoy and run the real thing every time if you run it under OS/2.)
QEMM 97 actually uses this mechanism to put the DOS installer and the launcher for the Windows installer in the same EXE file, so the installation instructions can be simpler.
The Point (Finally)
DOS/4GW is actually the only DPMI extender listed in
wlink.lnk that doesn’t support being bundled and, in fact, the only other one that even supports relying on an external helper is DOS/32A… the extender explicitly designed to be usable by end-users as a drop-in replacement for closed-source DOS/4GW applications.
Under typical circumstances, where you’re using the
wcl386 build driver, all you need to do is specify the correct target system via the
-l=whatever option to
wcl386 and you’re good to go.
-l= values produce an EXE file which does not require an external DPMI extender EXE:
…while the following ones do require an external helper:
Don’t believe me? Create a temporary DOSBox profile with a conf file that mounts C: and launches
hello.exe, create a
hello.c containing a “Hello, World!” in it, source
owsetenv.sh, and then run this bit of shell script:
for EXTENDER in dos4g causeway dos32a dos32x stub32a stub32x stub32ac stub32xc pmodew pmodewi; do print " === $EXTENDER === " rm hello.exe 2>/dev/null wcl386 -q -l=$EXTENDER hello.c dosbox -conf dosbox-0.74.conf &>/dev/null done
For more advanced situations, you’ll want to read the Open Watcom manuals, but the following example should get you started:
wcc decoy.c wlink file decoy.o system dos # produces decoy.exe wcc386 hello.c wlink file hello.o system dos32a # produces hello.exe wlink file hello.o system dos32a option stub=decoy.exe name decoyed.exe
wlink takes a list of un-quoted directives like you’d put in the aforementioned
whatever.lnk and/or an
@whatever.lnk file reference like can be passed to
wcl386. To invoke
wlink‘s help, the command to use is
As for other DPMI extenders, from looking at the
system definitions, it seems pretty simple to add definitions for them. Just make a copy of an existing definition and set the appropriate
GNU-ecosystem DOS compilers like DJGPP, Free Pascal, and Free Basic handle DPMI differently enough from Open Watcom C/C++ that their DPMI hosts are not inter-compatible and the DPMI host they come with is called either CWSDPMI or GO32v2, depending on who you ask. (Though everything I could find on the matter says they’re the same thing.)
Like DOS/4GW, the default setup for CWSDPMI requires an external DPMI host binary and, like with DOS/4GW, it’s possible to replace it with a fully embedded stub… but CWSDPMI offers that stub for you. I assume it’s about saving disk space by putting a single copy of the actual CWSDPMI binary in your path to be shared by all the separate EXE files that make up your compiler’s toolchain.
In this case, the process is quite simple:
- Use the
exe2coffbinary from DJGPP (which is also included with DJGPP cross-compiler builds) to strip the stub off your EXE file.
cwsdstub.exeonto the beginning of the resulting bare binary:
cat cwsdstub.exe my_exe.bin > my_exe.exe
copy /B cwsdstub.exe+my_exe.bin my_exe.exe
…and yes, that
COPY /B command should work on any old DOS. I have a copy of The MS-DOS Encyclopedia, from 1988 (Microsoft’s complete and unabridged must-have book for DOS 3.3 developers) and it doesn’t say anything about
+ being added after the command debut’d in DOS 1.0.
There aren’t as many alternative DPMI extenders for this ecosystem… probably because it came about after there was no longer a market pressure for everyone to try to sell DPMI extender licenses for money, but there is a port of PMODE/W called PMODE/DJ.