Why is the LOADPARMS32 structure so messed up?

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:

LOADPARMS       struc
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
LOADPARMS       ends

To ease the transition, Windows 1.0 used the same MS-DOS interface for launching programs: 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 program. 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.

Comments (26)
  1. BryanK says:

    And in case anyone’s wondering where LOADPARMS32 is documented:


    It’s part of the LoadModule function docs; it does not have its own doc page, like most of the other Win32 structures do.  (Probably because it isn’t a Win32 structure, really.  It’s a Win16 structure included for source compatibility.)

  2. Krai Gahwunao says:

    So let me get this straight: Technically speaking, there’s code in Windows Vista that dates back all the way to CP/M. Nice. (Ok, it’s "just" a struct, but it’s still pretty ancient.)

  3. Tim Smith says:

    I would bet there is code in almost any system that dates back +20 years.  Why rewrite something just because it is old.

  4. Daniel Colascione says:

    Because it needs to use $LATEST_PROGRAMMING_FAD, of course!

  5. Yuhong Bao says:

    Another change for Windows was that once you return from the Int 21/AH=4B, the AX register contain the HINSTANCE of the application.

    LoadModule was not documented until Windows 3.0, though LoadModule existed in the header files since at least 2.x. Even after WinExec and LoadModule was added, Int 21/AH=4C had to still be used to terminate an app.

    And how did you get the return code of a spawned app? You had to use TOOLHELP (which was introduced in Windows 3.1) to watch for program termination.

    Int 21 later became DOS3Call in around Windows 3.0 to reduce call overhead in protected mode.

    LoadModule could not load a Win32s application because Win32s used a hook into the Windows 3.1 loader that was not invoked if you called LoadModule.

  6. When I saw the topic of this post, I was hoping to see some Windows Vista bashing in the comments… and you haven’t deceived me ;-) . Windows has code dating from the early 80s for compatibility, true. But both Linux and Mac OS are Unixes, and thus should have code from the early 70s, ten years older than the first version of MS-DOS. Are they worse OSes because of that? No, they aren’t. But still, it is quite fun to attack Vista at any chance, isn’t it?

  7. Daniel Colascione says:


    Linux was actually written from the ground up. It contains no Unix code (aside from xfs, these days), and so is technically a Unix-like operating system.

    But close enough for practical purposes, sure.


  8. Joel says:

    I’m a Linux user (Mac user at work), and not too fond of Vista, but I do also hate the Vista bashing that goes on in internet forums and blogs.  If there are problems with Vista, it’s not the fact that LOADPARM32 has double terminated null strings.  For every weird API "feature" in Win32, I can find one or more in the Linux world, some of them introduced even later than the wacko Win32 ones, and with worse rationale.  And let’s not even talk about the pile of crap that is X11…

  9. James Day says:

    Antonio, you’re too sensitive this time. It’s interesting to know how old bits are and that’s not necessarily an attack.

    It prompted me to check the oldest executable of of a program I wrote that I still use reasonably regularly. That’s from July 1997 and is a console application using the 1992-copyrighted version of the Microsoft libraries. Fifteen years of backwards compatibility is quite respectable.

    One less regularly used (last in October 2007) has a copyright date of 1988 and is written in Borland Turbo Pascal. Nineteen years of backwards compatibility is getting to be pretty good.

  10. ATZ Man says:

    The "GNU/" part of GNU/Linux is older than Linux. The nit to be picked here (I know enough to know its there but not enough to actually pick it) lies in the difference between an Operating System and a kernel.

  11. hexatron says:

    Gulp. I still sometimes use WinExec. But not shamelessly.

    Well, hell. If it does what you want…

  12. Yuhong Bao says:

    "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."

    Some DOS functions do require minor changes to ensure code using them are compatible with Windows, for example the interrupt handler code and any data it touches had to be marked as FIXED, which in Windows 3.1 requires it to be placed in a DLL because older linkers default any additional data segments as FIXED because real-mode Windows was not able to fix reference to them when these segments move, and 386 enhanced mode Windows not only fixes but even pagelocks FIXED segments. Even in protected-mode Windows, EXEs that define additional data segments are limited to a single instance for a similar reason.

  13. Worf says:

    Honestly, the oldest code in Linux proper would date to around 1991, when Linux 0.1 was released. The oldest code in a full GNU/Linux system would probably date to the 60s or 70s, as the beginnings of Emacs. Most Linux systems don’t have a version of vi derived from Bill Joy’s code, instead using nvi (a free clone), or more commonly, vim.

    OTOH, since this structure is more of an interface than code itself… this would date Linux’ (and BSD and MacOS X…) interface to the 70’s as well. After all, the syscalls haven’t changed too much over the years.

  14. Neil says:

    386 enhanced mode pagelocks fixed segments because software written for older versions of Windows wouldn’t have known about pagelocking.

  15. SuperKoko says:

    From Daniel Colascione:

    "Linux was actually written from the ground up. It contains no Unix code (aside from xfs, these days), and so is technically a Unix-like operating system."

    MS-DOS and Windows have no CP/M code either, they just have a little bit of CP/M-like interface.

    Further, I think that there’s very small to no MS-DOS code in Windows NT/2000/XP/Vista.

  16. Ray says:

    backwards compatibility is a real double edged sword isn’t it.

    If you keep doing it, you end up with hacks on top of kludges on top of workarounds and the code continues to bloat.

    But if you lose backwards compatibility, you end up being crucified in the press and people won’t buy your product because Lotus 1.0 doesn’t work anymore, or being crucified in the sales department because a lot of apps don’t work anymore.

  17. I hereby submit Yuhong Bao for the BDIU award! Raymond, he doesn’t just pollute your blog, he is doign this crap on every technical blog our there.

  18. Jan Ouen says:

    @Steveshe: Bao wrote some very interesting comments here, so I don’t see your problem.

  19. mjb says:

    "Linux was actually written from the ground up. It contains no Unix code (aside from xfs, these days), and so is technically a Unix-like operating system."

    Come now. Are you saying no code has been donated to the kernel which originally was from other sources. (appropriately licensed to GPL2 at that point) That seems unlikely to me.

  20. Ulric says:

    I’m not really at ease with these articles.

    All they seem to do is get people worked up about APIs and structures in Windows that are obsolete, perpetuating the myth that Windows is old and trapped in backward compatibility.  

    This is doing to harm to Microsoft.  it’ll just end up as fodder on sites that are campaigning against Vista.  

    I can’t see any positive aspect to these articles.  It’s dead code, just leave it alone.  

    Right now Microsoft is in a critically wounded state with Vista, and us third party developers are struggling with clients who want away from Windows because of Vista. Information about CP/M structure in Vista is not helping anyone, it’s information that is useless for everyone except the competition.

  21. Timothy Fries says:

    it’ll just end up as fodder on sites that are campaigning against Vista.

    Those sites that have such an obvious axe to grind against Microsoft that they’d use something as benign as a legacy API as a hammer are going to find things to complain about anyway.  Most of the complaints about Vista on the net are based solely in fiction as it is (such as the statement that Microsoft is "in a critically wounded state with Vista").

    Raymond’s blog is a fantastic source of, if nothing else, trivia about why these old APIs are the way they are; and it’s a shame that you’re suggesting such a useful and interesting resource stop being so because people who hate Microsoft with every breath might find ammunition here.

  22. SuperKoko says:

    From Ray:

    "If you keep doing it, you end up with hacks on top of kludges on top of workarounds and the code continues to bloat."

    There’re no hack or workaround to make LoadModule work in Windows Vista. It’s a trivial thunk to CreateProcess that most programmers safely ignore. It simply doesn’t harm (unlike some backward-compatibility hacks described in other blogs entries).

    From Ulric:

    "I can’t see any positive aspect to these articles."

    So, don’t read them and don’t flood the comment system.

    Read the title of the blog before starting reading entries.

    The OLD new thing.

  23. James Day says:

    Ulric, so help your clients do what they believe is best for their business. If that’s not Windows for some clients, then you should be considering whether and how to service the various markets that make sense.

    Meanwhile Raymond is effectively explaining that if they buy a Windows-based application, Microsoft is going to try to make sure that their large investment isn’t made obsolete in only a couple of years. And that is of considerable value.

    Unless your product is tied to Vista or Windows and can’t possibly work on other systems, stop blaming Raymond and go out and service the markets your customers want you to service. Or let the competition do it, if you’re really determined not to adapt.

  24. Yuhong Bao says:

    BTW, back in 1975, C did not exist on anything other than UNIX.

    In contrast, by 1983, C compilers are beginning to appear for MS-DOS, and even MS had it’s own version of Unix called Xenix.

  25. John Topley says:

    "Further, I think that there’s very small to no MS-DOS code in Windows NT/2000/XP/Vista."

    I don’t think that’s quite true. IIRC, ntvdm.exe is based on the MS-DOS 5.0 source code. If you were going to provide an environment for running old MS-DOS programs then it would make sense to use at least some of the original code from the original operating system itself. It is probably a small percentage of the overall Windows Vista code base though.

  26. Yuhong Bao says:

    In fact, I don’t think even the changes to Int 21/4B was documented until Windows 3.0. A MS employee posted back in the 2.x days in a newsgroup sample code for spawning a Windows app. Google in Groups "Spawning Windows Applications" to take a look at this code.

Comments are closed.

Skip to main content