How do 16-bit programs start up?


Back in 16-bit Windows, MS-DOS cast a long and dark shadow. The really ugly low-level munging was very much in the MS-DOS spirit. You opened files by setting up registers and issuing an int 21h, just like in MS-DOS. Although the interrupt went to Windows instead, Windows maintained the MS-DOS calling convention. Process startup followed the same "real men write in assembly language" philosophy.

All the parameters to a 16-bit program were passed in registers. The entry point to a 16-bit process received the following parameters on Windows 3.1:

AX zero (used to contain even geekier information in Windows 2)
BX stack size
CX heap size
DX unused (reserved)
SI previous instance handle
DI instance handle
BP zero (for stack walking)
DS application data segment
ES selector of program segment prefix
SS application data segment (SS=DS)
SP top of stack

Hey, nobody said that 16-bit Windows was designed for portability.

The first thing a 16-bit program did was call the InitTask function. This function receives its parameters in registers, precisely in the format that they are received by the program entry point. The InitTask function initializes the stack, the data segment, the heap, retrieves and prepares the command line, recovers the nCmdShow parameter that was passed to WinExec, all the normal startup stuff. It even edits the stack of the caller so that real-mode stack walking works (critical for memory management in real-mode). When InitTask is all finished, it returns with the registers set for the next phase:

AX selector of program segment prefix (or 0 on error)
BX offset of command line
CX stack limit
DX nCmdShow
SI previous instance handle
DI instance handle
BP top of stack (for stack walking)
DS application data segment
ES selector of command line
SS application data segment (SS=DS)
SP edited top of stack

Once InitTask returns, the stack, heap, and data segment are "ready to run," and if you have no other preparations to do, you can head right for the application's WinMain function. Minimal startup code therefore would go like this:

    call    far InitTask
    test    ax, ax
    jz      exit
    push    di      ; hInstance
    push    si      ; hPrevInstance
    push    es      ; lpszCmdLine selector
    push    bx      ; lpszCmdLine offset
    push    dx      ; nCmdShow

    ... some lines of code that aren't important to the discussion ...

    call    far WinMain ; call the application's WinMain function

    ; return value from WinMain is in the AL register,
    ; conveniently positioned for the exit process coming up next

exit:
    mov     ah, 4Ch ; exit process function code
    int     21h     ; do it

Why wasn't the application entry point called main? Well, for one thing, the name main was already taken, and Windows didn't have the authority to reserve an alternate definition. There was no C language standardization committee back then; C was what Dennis said it was, and it was hardly guaranteed that Dennis would take any special steps to preserve Windows source code compatibility in any future version of the C language. Since K&R didn't specify that implementations could extend the acceptable forms of the main function, it was entirely possible that there was a legal C compiler that rejected programs that declared main incorrectly. The current C language standard explicitly permits implementation-specific alternate definitions for main, but requiring all compilers to support this new Windows-specific version in order to compile Windows programs would gratuitously restrict the set of compilers you could use for writing Windows programs.

If you managed to overcome that obstacle, you'd have the problem that the Windows version of main would have to be something like this:

int main(int argc, char *argv[], HINSTANCE hinst,
         HINSTANCE hinstPrev, int nCmdShow);

Due to the way C linkage was performed, all variations of a function had to agree on the parameters they had in common. This means that the Windows version would have to add its parameters onto the end of the longest existing version of main, and then you'd have to cross your fingers and hope that the C language never added another alternate version of main. If you went this route, your crossed fingers failed you, because it turns out that a third parameter was added to main some time later, and it conflicted with your Windows-friendly version.

Suppose you managed to convince Dennis not to allow that three-parameter version of main. You still have to come up with those first two parameters, which means that every program's startup code needs to contain a command line parser. Back in the 16-bit days, people scrimped to save every byte. Telling them, "Oh, and all your programs are going to be 2KB bigger" probably wouldn't make you a lot of friends. I mean, that's four sectors of I/O off a floppy disk!

But probably the reason why the Windows entry point was given a different name is to emphasize that it's a different execution environment. If it were called main, people would take C programs designed for a console environment, throw them into their Windows compiler, and then run them, with disastrous results.

Comments (56)
  1. jachymko says:

    What geeky stuff used to be in the AX register in Windows 2.0, if I may ask? :)

  2. Mark Mullin says:

    Uggh – having horrible flashbacks to DS != SS errors

  3. Idiot says:

    "If it were called main, people would take C programs designed for a console environment, throw them into their Windows compiler, and then run them, with disastrous results."

    You can only idiot-proof things so much; in the end, the idiot always wins.

  4. Bahbar says:

    @jachymko: geeky stuff in the AX register

    42?

  5. Keithius says:

    This is one of the best articles I have ever read here. It’s geeky and historical in a way that is just intensely satisfying.

    And that last comment brought a very geeky smile to my face. ;-)

  6. mikeb says:

    Most of these excuses for why Win16 programs didn’t fit well with normal C programs could have been solved with a little bit of work – take a look at the WinIO/WinDOS library that Andrew Schulman (et al?) came up with:

    http://www.digitalmars.com/rtl/winio.html

    I suspect that the main reasons that this did not occur at Microsoft are (of course the following is complete speculation):

    1) Windows was originally viewed as something that ran on top of DOS, not as an OS in its own right

    2) these changes would have required some sort of cooperation form the compiler team, and the effort/clout/whatever did not exist.  It’s even possible that the reason might have been that the compiler was still OEM’ed from a different company (Lattice C) and therefore was largely controlled by Lattice at that time.

  7. Thoe says:

    Nice to see you use present tense when speaking about 16-bit applications. I spend 30-50% of my time maintaining an old C++ 16-bit application, so I still live with all the joys of 64K limits :-)

    Thinking about the good old "Interrupt 21" days – made me think: Is there still a benefit in writing "raw code"? Back then you could beat the compiler by an order of magnitude, writing assembly in critical places. Can you beat the compiler today by writing CIL directly?

    (I don’t think I will take the asm-route, modern CPU’s seems to complicated)

    And if you can beat the compiler – where is the great guide that shows how?

  8. Aaargh! says:

    I’ve got no experience with low-level windows stuff, so this might be a stupid question, but what is the purpose of "HINSTANCE hinst, HINSTANCE hinstPrev and int nCmdShow" and why do they need to be included in the main function ?

    "If it were called main, people would take C programs designed for a console environment, throw them into their Windows compiler, and then run them, with disastrous results."

    How come ? wouldn’t they just run as a console program ? GUI and CL applications start exactly the same in a normal OS. What is so different in Windows that a normal main() function is not enough ?

  9. Ulric says:

    I’m often baffled that some people were disturbed WinMain vs main.  From my point of view, writing a Windows app was already not pure character mode "C" code, and with many GUI toolkits it was typical that the ‘main’ was in the library and not your code.  The name of that one startup function doesn’t matter.

    In any case, I seem to remember that Borland C++ detected you used ‘main’ in your stdio app and linked with EasyWin instead, which created a GUI window and handled printf/scanf, everything for your character-mode C app to run in Windows 3.1.  

    For Microsoft VC++ 1.x, it was offered by a library called "QuickWin"

    In both cases your standard C app became a GUI Windows 3.x app with a basic menu and MDI window.

  10. Aaargh! says:

    "Is there still a benefit in writing "raw code"? Back then you could beat the compiler by an order of magnitude, writing assembly in critical places. Can you beat the compiler today by writing CIL directly?"

    Probably not. Since the CIL code is not native code there will always be a compiler or interpreter between you and the hardware.  The JIT can do runtime optimization so at least in theory it could be faster that native compiled code because it can analyse the running system and adapt it’s optimizations, and it can optimize for the actual CPU you’re using instead of a class of cpu’s.

    Furthermore, you cannot assume to know what kind of CPU architecture the application wil run on. (I know .NET is not as portable as MS says it is, but still)

  11. Triangle says:

    "How come ? wouldn’t they just run as a console program ? GUI and CL applications start exactly the same in a normal OS. What is so different in Windows that a normal main() function is not enough ?"

    Because of the funky way the Windows executable format works. There are bits in it that you can set to indicate that the program is a console program or a GUI program. If you set the ‘console program’ bit, Windows will pop up a console when the program is launched. The compiler is supposed to set that bit if the program has a main() function, and set the GUI program bit if it has a WinMain function.

  12. MS says:

    "I’ve got no experience with low-level windows stuff, so this might be a stupid question, but what is the purpose of "HINSTANCE hinst, HINSTANCE hinstPrev and int nCmdShow" and why do they need to be included in the main function ?"

    I was going to go back to my Win16 programming books to look it up again, but as it so happens Raymond wrote about it:

    http://blogs.msdn.com/oldnewthing/archive/2004/06/15/156022.aspx

    http://blogs.msdn.com/oldnewthing/archive/2004/06/14/155107.aspx

    The above should give some explanation for the previous instance handle at least.  I thought I knew what the cmdshow thing was but it is escaping me at the moment.

  13. Ulric says:

    Aaargh!, Triangle, I think you’re both confused with Win32 newness.  This is Windows 3.1  that we’re talking about, a GUI environment that ran on top of DOS, and dos executable. It wasn’t an operating system.  There was no console mode windows 16-bit application, that’s all new in Win32.

  14. Aaargh! says:

    “There are bits in it that you can set to indicate that the program is a console program or a GUI program. If you set the ‘console program’ bit, Windows will pop up a console when the program is launched.”

    What if you want a GUI program with a console ? (e.g. for debug output while developing) And what happens to the programs stdin/stderr/stdout if there is no console ? Are they still connected to something ?

    [I don’t know what you folks are talking about. There was no such thing as a ‘console program’ in 16-bit Windows. -Raymond]
  15. Aaargh! says:

    "There was no console mode windows 16-bit application, that’s all new in Win32."

    IIRC it was possible to open a shell in Win 3.11 and you could run console applications from there.

  16. Triangle says:

    "IIRC it was possible to open a shell in Win 3.11 and you could run console applications from there."

    But there were no Windows Executable files in windows 3.11, it used COM files. So Ulric is right about the impossibility of that.

  17. Thoe says:

    I’ve got no experience with low-level windows

    stuff, so this might be a stupid question, but

    what is the purpose of "HINSTANCE hinst,

    HINSTANCE hinstPrev and int nCmdShow" and why

    do they need to be included in the main

    function ?

    Well – this is a 15 year old design, running on very small PC’s. (With applications only multitasking, when they asked for it). Knowing what is my reference-number (hinst), who started me (hinstPrev) and how should I show my main windows? – are pretty critical in the start-up – So giving that as parameters probably seemed logical.

    How come ? wouldn’t they just run as a console

    program ? GUI and CL applications start

    exactly the same in a normal OS. What is so

    different in Windows that a normal main()

    function is not enough ?

    In Windows 3.1 a program were either a semi-multitasking Windows program or an exclusive DOS/console program – DOS didn’t multitask! – Win16-programs could only multi-task, when they were event-driven or very cooperative.

    Of course Win32 changed this (with  preemptive multitasking), and allowed console applications as equals to GUI-applications – But by then winmain was popular, and couldnt be ignored…

  18. mikeb says:

    >> There was no console mode windows 16-bit application, that’s all new in Win32.

    Again, that was mere oversight or laziness (see the above link to WinIO/WinDOS).  It did not take rocket science to come up with a library that implemented stdio function calls that placed the output in a window.

    [There’s nobody stopping you from using that external library. That’s the 16-bit Windows way. -Raymond]
  19. Thoe says:

    ""Is there still a benefit in writing "raw code"?""

    "Probably not. Since the CIL code is not native code there will always be a compiler or interpreter between you and the hardware."

    I know that…

    • I am not trying to beat the CIL to native code. (Which I am sure is possible, the compiler can’t know exactly the most effective code for each CPU – but it is probably close enough)

    I want to know, if I can write better CIL than the C# to CIL compiler…

  20. Aaargh! says:

    "I want to know, if I can write better CIL than the C# to CIL compiler…"

    The question is if there is such a thing as ‘better’ CIL code. If you write an algorithm say in C# and compile that to CIL, and then you write the exact same algorithm in ‘optimized’ CIL. Shouldn’t both result in the exact same machine code after the CIL-to-native compiler/optimizer is done with it ? And is one really better than the other ?

  21. Gazpacho says:

    Maybe C wasn’t always the center of the software development universe?

    Windows was created in the mid-80s. People were still using Pascal, Fortran, and assembly language when it suited them. There were a lot of people who didn’t trust compilers to generate good code.

  22. Ulric says:

    > There was no console mode windows 16-bit application, that’s all new in Win32.

    Again, that was mere oversight or laziness

    (see the above link to WinIO/WinDOS).

    Stop trying to plug WinIO.. :) I’ve already written about Microsoft’s own QuickWin and Borland EasyWin.  These are separate libraries that allow making Windows app by recompiling against them without changing your code.   In 90s, Microsoft did not create Windows so that you would code with ‘printf’ and ‘scanf’. That was already a solved problem: you could compile your app as a DOS application and it runs inside a DOS box.  The point of Windows was the mouse, event – driven programming, graphics.  If you weren’t interested in that, you wouldn’t make a Windows app, you’d make a DOS app. Duh.

    Window 3.x could run DOS apps in a virtual box, and with preemptive multi-tasking at that.  But that does not make these DOS apps Windows app. As well, running DOS apps in VMWare on a Mac doesn’t mean that DOS apps are OS X application.

    A Win32 console application, however, is a true Windows 95 application with full support from and access to the API.

  23. mikeb says:

    >> There’s nobody stopping you from using that external library.

    Of course I realize that – I’m not saying that Microsoft needs to go back and change the Win16 programming model.  I think it’s clear that my point is that nothing prevented Microsoft from doing something similar.

    [You don’t write a feature because you can’t think of a reason not to do it. Don’t forget, Windows 1.0 had to run off two floppy drives. -Raymond]
  24. mikeb says:

    > Stop trying to plug WinIO.. :) I’ve already written about Microsoft’s own QuickWin and Borland EasyWin.

    I’m not plugging WinIO. Raymond made some points about how problematic using main() as the user’s program starting point would be.  I’m just using that library (which I will refrain from naming again) as an example that shows that the several paragraphs pointing out problems of having Win16 support stdio and main() are somewhat misleading.  It’s wonderful that you’ve pointed out a couple other libraries that indicate the same – good for you!

  25. mikeb says:

    >> You don’t write a feature because you can’t think of a reason not to do it. Don’t forget, Windows 1.0 had to run off two floppy drives

    So the reasons for “Why wasn’t the application entry point called main?” don’t really have to do with conflicts in the parameters passed to main().  I guess they have to do with saving space?

    [Nice job, you change the subject (from “How did 16-bit Windows programs start up?” to “Why didn’t 16-bit Windows have a console subsystem?”), I give an explanation for the changed subject (“Among other reasons, there wasn’t disk space”) and then you say “Aha, so that’s the real reason why the entry point wasn’t called main!” (A third topic.) I need to use that trick. -Raymond]
  26. Dean Harding says:

    "I guess they have to do with saving space?"

    I think you’re arguing two different points. From the webpage you liked to about winio:

    "WINIO is an easy-to- use, higher level library built on top of the standard Windows 3.x API"

    That the "standard Windows 3.x API" decided to make "WinMain" the conventional entry-point is totally unrelated to whether SOME OTHER application library did not.

    If you’re trying to figure out the reason why Microsoft decided to use "WinMain" as the convention instead of "main", then the answer could just as well be "to annoy mikeb" — it really doesn’t MATTER. What advantages would there have been to naming "WinMain" as "main"?

  27. Mark Steward says:

    Triangle: NE (.exe) was the executable format used for all Windows applications.

    Thoe: Windows 1.0 SDK’s are hard to find, but it’s probably 22 years.

    mikeb: if you’re suggesting using QuickWin for all program startup, how would you justify the "bloat" of parsing the command line and creating a window with menus for stdin/out?  You’d need a new way (i.e. WinMain) for programs that wanted to opt out.  Well, why not make WinMain the default, and try to shed some of this back-compat cruft? ;-)

    Ulric: The entry point is the value of CS:IP in the NE header.  EntryPoint is the entry point from the OS’s POV.  WinMain is the entry point from the user/programmers’s point of view.

  28. Mark Steward says:

    P.S. I thought this issue was put to rest last year:

    http://blogs.msdn.com/1205831.aspx#1229735

    http://blogs.msdn.com/1205831.aspx#1265954

  29. mikeb says:

    Seems that I have been unclear about some of my points.  Let me try to be clear, then I’ll drop this.

    1) Raymond made some remarks about “Why wasn’t the application entry point called main?”.  I believe that most of the problems pointed out about this are really non-problems.  main() could have been used as the user starting point just as easily as WinMain(), with the Windows specific parameters provided in some other way.  I’m not saying that MS’s decision to use WinMain() was improper.  I’m just saying that the reasons that Raymond gave for not using main() weren’t particularly strong reasons.

    2) other people started talking about consoles.  I simply mentioned that the same library I had mentioned before showed that getting stdio working in Win16 was not rocket science.

    So at this point I’d like to claim that I didn’t change subjects – I responded to 2 separate subjects.  As for my ‘third topic’, I was being sarcastic since I interpreted your remark about the 2 floppy limit as being a reason for using WinMain() instead of main(); the loss of that intonation is an unfortunate side-effect of my (lack of) prose writing skills.  I’m sorry for misunderstanding your meaning.

    [Sure, the entry point could’ve been called “main” with the GUI information passed some other way. But it’s a whole new programming model; give it a new name to emphasize that new rules apply, especially in real-mode Windows, where memory could move at almost any time. Your average C program from a textbook would corrupt memory pretty quickly. -Raymond]
  30. Triangle says:

    "But it’s a whole new programming model; give it a new name to emphasize that new rules apply, especially in real-mode Windows, where memory could move at almost any time. Your average C program from a textbook would corrupt memory pretty quickly. -Raymond"

    What about GlobalLock and co. ?

    http://blogs.msdn.com/oldnewthing/archive/2004/11/04/252258.aspx

  31. Gazpacho says:

    There was no particular reason when Windows was created, to expect that that it would ever become its own OS. That just happened to be the only option left for Microsoft after the IBM partnership and the Unix standardization process both flopped.

    Just imagine the howls of protest if Windows had been ported to Unix with a nonstandard main declaration.

  32. Mark Steward says:

    Triangle: so your suggestion is GlobalLock everything?

  33. Hi Raymond,

    I’m trying to contact you in regards to a book I’m writing for which I’d like to interview you. I can’t seem to locate anywhere your contact information therefore this is the only way I could see to reach you. If you’re interested please contact me.

  34. dcook says:

    Important point to note (and still true today) is that contrary to what you might think after reading MSDN, your process does NOT start at

       int main(int argc, char** argv);

    nor does it start at

       WinMain(…);

    It starts at

       void EntryPoint(void); // Name doesn’t matter.

    The "EntryPoint" symbol is provided via an offset in the executable’s header, and that is set by the linker based on the /ENTRY parameter.

    Windows does NOT provide you with a parsed command line. Nor does it provide you with the Show command value. Your program has to get them itself. Luckily, if you’re using a "runtime", the runtime usually hides this from you.

    It just so happens that if you let the compiler and linker do their thing without trying to get too smart and overriding the default, the linker will use "mainCRTStartup" (for console programs) or "winmainCRTStartup" (for GUI programs) as the entry point. These functions are defined in the CRT library. They use various Windows APIs like GetCommandLine and GetStartupInfo to figure out the command line and Show command, then they call your program’s "main" or "WinMain" with the results. They also do nice things like initialize the CRT, call your static initializers, call static destructors at program exit, and shut down the CRT.

    So contrary to MSDN, "main" and "WinMain" are not actually process entry points. They’re just established convention that is aided by the currently available C Runtime.

  35. Tom_ says:

    "What if you want a GUI program with a console ? (e.g. for debug output while developing) And what happens to the programs stdin/stderr/stdout if there is no console ? Are they still connected to something ?"

    To create a console, use AllocConsole. Or just make a console program, and have it create windows in the usual fashion.

    (The debug builds of my GUI programs are routinely console apps. They call WinMain from main, after constructing a suitable command line from argc/argv and retrieving the instance handle via GetModuleHandle(0). I do this purely because the console provides a handy scrollable colourized TTY that you can manipulate whilst your program is frozen in the debugger.)

    If you have a non-console program, stdout/stderr/stdin don’t appear to be attached to anything useful. I guess the C runtime doesn’t set them up "properly" in that case. You can however use WriteConsole with GetStdHandle(STD_OUTPUT_HANDLE) — or STD_ERROR_HANDLE — and no doubt stdin has equivalent functionality too — and have it interact with the console as you might expect. These handles also work just as you’d imagine in an ordinary console program too, so you can write code that works the same in both situations.

    (There *is* some cleverness you can do in the non-console case to manually point stdout/stderr/stdin to your newly-created console window, but I don’t recall offhand what it is, as I personally haven’t yet had the need to do this.)

  36. Ulric says:

    dcook :

    > The "EntryPoint" symbol is provided via

    >an offset in the executable’s header, and

    >that is set by the linker based on the /ENTRY > parameter.

    True for Win32 PE executables, but this blog entry is about 16-bit Windows.

    Also, the blog entry above links to an entry from Raymond that already said "The name WinMain is just a convention"

    > So contrary to MSDN, "main" and "WinMain" are not actually process entry points.

    MSDN says its "the WinMain function is the conventional name for the user-provided entry point"

    Tom_:

    >If you have a non-console program, stdout/stderr/stdin don’t appear to be

    >attached to anything useful.

    if you need to make an app that has a console, write a console app (linker setting: /subsystem:console) and get one for free.  It can create UI, call MFC, etc.

    If you want to create the console yourself with AllocConsole, you’ll have to use this code to remap STDIO to it:

    int hCrtOut = _open_osfhandle( (INT_PTR) GetStdHandle(STD_OUTPUT_HANDLE),  _O_TEXT );

    *stdout = *( _fdopen( hCrtOut, "w" ) );

    int hCrtErr = _open_osfhandle( (INT_PTR) GetStdHandle(STD_ERROR_HANDLE),  _O_TEXT );

    *stderr = *( _fdopen( hCrtErr, "w" ) );

  37. CornedBee says:

    I think the main criticism of WinMain today is really that it makes cross-platform GUI programs just that bit more awkward.

  38. oj says:

    "How do 16-bit programs start up?"

          …who the f*** cares any more?

    Except maybe to prove that the OS was just as f***ed up by its designers as Vista is today.

    oj

  39. John Topley says:

    "But there were no Windows Executable files in windows 3.11, it used COM files."

    What are you talking about? As Mark Steward said, Windows 3.1 used the NE (New Executable) format.

    "This is Windows 3.1 that we’re talking about, a GUI environment that ran on top of DOS, and dos executable. It wasn’t an operating system."

    Actually, Windows 3.x ran MS-DOS in V86 mode within a hidden System VM. Go and read Andrew Schulman/Matt Pietrek…

  40. MS says:

    ""How do 16-bit programs start up?"

         …who the f*** cares any more?

    Except maybe to prove that the OS was just as f***ed up by its designers as Vista is today.

    oj"

    Oh boy, lets all jump on the hate parade for Vista! /snark

    You’re probably too young to realize the benefit of studying the past.  Or maybe you just don’t appreciate learning as much as you should.

  41. Just as a side note to the "WinMain vs. main" debate that seems to be happening — Windows wasn’t the only environment to provide its own equivalent to main.  DRI’s GEM used "void GEMAIN()" as the C entrypoint — the (vanilla DOS) executable’s entry point was a small chunk of assembly that did some tidying up, checked for the presence of GEM’s AES (~ Windows’s USER) by making sure the INT EFh handler had a certain signature, and then jumped to GEMAIN.

  42. SRS says:

    …and what happened to the return value in AL once int 21/4c got called? From what I remember there was no way to pick up a process exit code in win3.x api.

  43. rdamiani says:

    I thought main vs. WinMain came from the need for an MS-DOS stub at the beginning of Windows programs. I seem to recall that there were a small number of programs (Sidekick?) that actually did useful work with the stub when in MS-DOS, and did different things when run in Windows.

    [If that were true, then console apps couldn’t have stubs. -Raymond]
  44. Ulric says:

    I think the main criticism of WinMain today

    is really that it makes cross-platform

    GUI programs just that bit more awkward.

    if that’s the ‘main criticism’, I have never heard it. :)  I have heard only nit pickers say, "main is the C standard!"  Winmain is often hidden in toolkits, and is an issues we run into with cross-plateform GUI apps.

    > "This is Windows 3.1 that we’re talking

    > about, a GUI environment that ran on top

    > of DOS, and dos executable. It wasn’t

    >an operating system."

    Actually, Windows 3.x ran MS-DOS in V86 mode >within a hidden System VM. Go and read

    Andrew Schulman/Matt Pietrek…

    nit picking?

    WinMain dates from the first Windows, a graphic toolkit onto DOS and it’s not a full OS.  Windows 3.x is used to signify anything prior to  Win95.

  45. John Topley says:

    I prefer to think of it as accuracy rather than nitpicking.

    "Windows 3.x is used to signify anything prior to Win95."

    Oh right. Silly me. I didn’t realise that Windows 3.x encompassed Windows 1.0, Windows 2.0, Windows/286 etc. Where was that rule defined again?

  46. Ken Hagan says:

    "Actually, Windows 3.x ran MS-DOS in V86 mode within a hidden System VM. Go and read Andrew Schulman/Matt Pietrek…"

    Good advice. Windows 3.x actually contained two products. The first was a DOS extender which turned real mode DOS into an arbitrary number of (DOS-extended) VMs. The second was Windows, *all of which* which ran as a single DOS process in the first of those VMs.

    Windows itself could be considered two products as well. There was the kernel, which offered a truly amazing way of managing memory in real-mode or protected mode 16-bit x86 using the NE file format, and the GUI which was a Windowing system built on top.

    Either or both of the DOS extender or the kernel could have been sold as separate products, since they delivered real benefits compared to ordinary DOS. However, it made more commercial sense to bundle the lot.

  47. SuperKoko says:

    "This is Windows 3.1  that we’re talking about, a GUI environment that ran on top of DOS, and dos executable. It wasn’t an operating system."

    NE executable files were used from the first version of Windows 1.0. IIRC, there was a backwards compatibility break somewhere that made Windows 3.11 for worksgroup unable to execute Windows 1.0 executables, even though they were NE executables (maybe something related to real mode vs 16 bits protected mode).

    Moreover, Windows 3.1 is not synonymous of Windows 1.0 or Windows 2.0.

  48. pietje says:

    About nCmdShow: it’s the integer to pass to ShowWindow so you’re doing what the user wants. Try opening the properties of a shortcut to an application and change on the 2nd tab (where the icon and path can be changed) the combobox with "Normal", "Minimized" and "Maximized". If the app behaves correctly then you can make it start minimised or maximised if you like.

    But you really don’t need it anymore, because the first call to ShowWindow *ALWAYS* uses this default, as if you wrote ShowWindow(hwnd, SW_SHOWDEFAULT). If you’re using mainCRTStartup or WinMainCRTStartup, you can get this int by calling GetStartupInfo. But since there’s SW_SHOWDEFAULT calling GetStartupInfo for this is pointless unless you’re writing a runtime library and need to call the users WinMain function correctly.

    In my program, I’m doing this after creating the main window (and making sure its size is correct, setting the icon, etc.):

    ShowWindow(hwnd, SW_SHOWDEFAULT);

    ShowWindow(hwnd, SW_SHOW);

    The SW_SHOWDEFAULT is merely there to point out that it’s using the default there.

  49. ender says:

    IIRC, the only difference between Windows 1.0 and 2.0 executable files was the resource format – if you opened a Windows 1.0 executable file in a resource editor that supported it, and then saved it with resources converted to new format, the file would still run in XP (32bit of course) – except that the program would appear titlebar-sized at top-left of the screen, since in Win1.0 the window manager would take care of window sizes automatically.

  50. Igor Levicki says:

    I mean, that’s four sectors of I/O off a floppy disk!

    Priceless!

  51. Pete says:

    Actually, in your discussion of “main()”, you neglected the “envp” parameter which was introduced either in (1) System II v7, (2) System III, or (3) BSD Unix (I don’t remember which; in fact, maybe it was even earlier).  So the actual defininiton of main() was

    void main(int argc, char *argv[], char *envp[])

    where envp was a null-terminated array of pointers to the environment strings.

    I know “envp” pre-dates Win 1.0, and I think it pre-dates MS-DOS, too.

    [I linked to envp in the article. -Raymond]
  52. Carl says:

    [I don’t know what you folks are talking about. There was no such thing as a ‘console program’ in 16-bit Windows. -Raymond]

    While true, it was possible to write an MS-DOS application that called the Windows API (I remember writing such an application in Borland’s Turbo Pascal).

    From the end-user’s perspective, this is identical to a console program in more modern versions of Windows.

    [How can an MZ program call, say, user!CreateWindow? I’m guessing you’re just talking about MS-DOS programs running in a virtual machine and a handful of int 2F calls. That’s hardly calling the “Windows API”. -Raymond]
  53. John Elliott says:

    The other thing that may stop Windows 1.x programs running under recent Windowses is that they don’t have the subsystem field populated in the NE header, causing the loader to try and run them under the OS/2 subsystem rather than the Win16 one.

  54. Yuhong Bao says:

    BTW, what was the version of the linker that came with the Windows 1.0 SDK?

  55. Yuhong Bao says:

    It appears from the DDKs that a Windows 95 16-bit SDK could be created to allow 16-bit programs to use some of the new features of Windows 95.

    [16-bit Windows programs (not 16-bit MS-DOS programs) can use thunks to access 32-bit functions in Windows 95. -Raymond]

Comments are closed.