Why is the LOADPARMS32 structure so messed up?

Raymond Chen


If you look at the LOADPARMS32 structure,
you’ll find a horrific mishmash.
Double-null-terminated strings,
a null-terminated string,
some WORDs, and
even a Pascal-style string.

What’s going on here

Each of those members comes from a different era in time.
The oldest member is the Pascal-style command line,
which dates back to CP/M.
On CP/M, command lines were stored at a fixed location,
namely 0080h through 00FFh,
in the form of a Pascal string.
The byte at 0080h specified the length of the command line,
and the bytes at 0081h through 00FFh contained the command line itself.

MS-DOS based much of its initial interface on CP/M in order to
make porting to the new operating system easier,
and part of what got carried over was the way command lines were
passed from one program to another.
The MS-DOS function to load a program took two parameters,
a pointer to a null-terminated string (specifying the module to load)
and a pointer to a parameter block which took the following form:

loadp_environ dw ? ; environment of new process
loadp_cmdline dd ? ; command line of new process
loadp_fcb1 dd ? ; first FCB
loadp_fcb2 dd ? ; second FCB

To ease the transition,
Windows 1.0 used the same MS-DOS interface for launching
You loaded up the registers and issued an
int 21h instruction.
All the parameters had the same meaning.
Generally speaking, 16-bit Windows used the old MS-DOS interface
for a lot of functionality, especially disk access.
Want to write to a file?
Put the file handle in the BX register,
the number of bytes in the CX register,
a pointer to the buffer in the DS:DX registers,
function code 40h in the AH register,
and issue an
int 21h,
just like in MS-DOS.

Why do this?
Well, it saved the Windows team from having to invent a whole
boatload of functions that duplicated what MS-DOS already did,
and it meant that existing MS-DOS programs
didn’t need to change a thing in their file I/O code.
If they used a runtime library designed for MS-DOS (C or otherwise),
that library would still write to files by setting registers
and issuing an int 21h.
If you want people to switch to your new platform, you need to make it easy,
and “you don’t have to change anything; it all just works” is pretty easy.
(One minor change was that the first FCB was repurposed to contain
the nCmdShow; the magic value of “2” in the first word of
the FCB signals that it’s not really an FCB.)

As a minor convenience, the
LoadModule function provided a C-callable version of
the low-level int 21h, but you still had to provide
the parameters in the form of the MS-DOS exec structure.
It wasn’t until later versions of Windows that the
WinExec function was added,
thereby providing a much more convenient interface to starting a new
No longer did you have to mess with the crazy MS-DOS exec structure
and its strange way of passing the command line and nCmdShow.

The people who were designing Win32 created their own function
CreateProcess to launch a new process,
but for backward compatiblity, they retained the
old WinExec
and even older LoadModule mechanisms.
The pointers in the crazy 16-bit exec block got converted to 32-bit,
but the craziness of what they pointed to was retained to make
porting old code easier.
The int 21h interface no longer exists, of course.
The craziness is just a leftover from the old MS-DOS days.
The WinExec and LoadModule functions
are now just stub functions that convert their parameters and
call the CreateProcess function to do the real work.

Raymond Chen
Raymond Chen

Follow Raymond