Embedding the DPMI Extender for your Open Watcom C/C++ EXE (And Related Knowledge)

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?

DOS Extenders

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:

Potential Errors

undefined system name: ...

System names are defined in the wlink.lnk file in the bin* folder containing the wlink or 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.

Check 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 .exe files.

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 PATH and 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 binl:

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 binw or binl as appropriate. (Renamed to all-lowercase if you’re on Linux.)

Stub Tricks

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 binw or 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. dos32a):

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

Then 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 wcl386 or wlink with @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.

The following -l= values produce an EXE file which does not require an external DPMI extender EXE:

  • causeway
  • dos32a
  • pmodew

…while the following ones do require an external helper:

  • dos4g
  • stub32a
  • stub32x
  • stub32ac
  • stub32xc

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 wlink -?.

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 format and stub values.

(It’s a similar story for the CWSDPMI host used by DJGPP, Free Pascal, and FreeBASIC. You can use exe2coff from DJGPP (including cross-compiler builds) to strip the old stub off, and then apply the version of the stub that’s fully self-contained… but don’t try to exchange extenders between Open Watcom and the DJGPP-compatible ecosystem unless they explicitly provide support as with PMODE/DJ. They use different formats and provide different services. )

CC BY-SA 4.0 Embedding the DPMI Extender for your Open Watcom C/C++ EXE (And Related Knowledge) by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

This entry was posted in Geek Stuff. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

By submitting a comment here you grant this site a perpetual license to reproduce your words and name/web site in attribution under the same terms as the associated post.

All comments are moderated. If your comment is generic enough to apply to any post, it will be assumed to be spam. Borderline comments will have their URL field erased before being approved.