The name WinMain is just a convention

Date:December 4, 2006 / year-entry #400
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20061204-01/?p=28853
Comments:    37
Summary:Although the function WinMain is documented in the Platform SDK, it's not really part of the platform. Rather, WinMain is the conventional name for the user-provided entry point to a Windows program. The real entry point is in the C runtime library, which initializes the runtime, runs global constructors, and then calls your WinMain function (or...

Although the function WinMain is documented in the Platform SDK, it's not really part of the platform. Rather, WinMain is the conventional name for the user-provided entry point to a Windows program.

The real entry point is in the C runtime library, which initializes the runtime, runs global constructors, and then calls your WinMain function (or wWinMain if you prefer a Unicode entry point).


Comments (37)
  1. Anonymous says:

    Where’s the documentation for the actual OS entrypoint?

  2. ac says:

    @Anonymous

    At least on VC 6, to make a Unicode program, entry had to be defined manually. Search for

    /ENTRY:wWinMainCRTStartup for examples. I don’t know how that is done on more recent VC versions.

  3. maine says:

    Why isn’t it called main?

    [Because that name was already taken. I can’t believe I had to write that. -Raymond]
  4. Two years ago I had quite a severe Stroke, my short memory is badly damaged & I am 75 years old.  Though I do have computer qualifications, my memory is awful, so please help me!

  5. Carlos says:

    "Where’s the documentation for the actual OS entrypoint?"

    Here:

    http://msdn2.microsoft.com/en-us/library/f9t8842e.aspx

    Although I think it’s wrong for EXEs.  It says the entrypoint should have the same signature as WinMain, but the OS doesn’t actually supply any parameters.

  6. This excellent article has source for a tiny C runtime:

    http://msdn.microsoft.com/msdnmag/issues/01/01/hood/

  7. Mike Dimmick says:

    Carlos: I’m fairly sure the OS actually does supply the parameters, but the CRT doesn’t currently make use of them. To avoid faults surrounding misaligned stack, due to the incorrect signature (and I think also incorrect calling convention, the entry point function should be __stdcall), the CRT calls ExitProcess rather than returning from xxxCRTStartup.

    This isn’t a problem, because that’s all that kernel32!BaseProcessStart is going to do after the executable’s entry point function returns.

  8. mikeb says:

    I think that question of why main() isn’t used instead of WinMain() is a valid one.  Sure, main() is taken, but in the same way that WinMain() is taken – it’s just the name of a function that the CRT calls when it’s finished initializing.  So the CRT could just as easily call main() instead of WinMain() for Windows programs (which is what C runtimes do for pretty much every other target environment).

    So, why doesn’t the MS C compiler for Windows simply use the long-standing convention of calling main for the user’s program?

    I’d guess the answer is because the first Windows programs were really DOS programs that ran in a new, funky, graphical environment, and the compiler and CRT had no idea that Windows was the program’s target. So the first Windows programs had both a main() and a WinMain() (which was called by main()).  

    Also, WinMain() takes different parameters that main(), but that issue could be handled some other way (requiring the Windows program to call some sort of WinInit() API to get that information or something).  

    As a matter of fact, Andrew Schulman and David Maxey wrote a library (WINIO) that let programmers write C programs using standard I/O functions in Win16 programs (and those programs had a main() entry point – as far as the end-user programmer was concerned).  WINIO used globals to let the programmer access the WinMain() parameters.

  9. Carlos says:

    @Mike Dimmick: On entry to the app the stack just seems to contain some random cruft from the OS process initialisation.  It definitely doesn’t have the WinMain parameters (e.g. there’s no command-line pointer).  If the stack cruft does mean something, I can’t fathom what.

  10. >The real entry point is in the C runtime library

    Does the above refer to the CRT used by Windows or used by the application? (I’m a bit confused, what if the application wasn’t written in C/C++?).

    Best regards,

    Burak

    [In the context of what happens if you use WinMain, the real entry point is the C runtime library. If you don’t use WinMain then the real entry point is whatever your programming framework uses. -Raymond]
  11. DriverDude says:

    The entry point for Win32 console apps is main. Just like every other command-line C environment. And of course the real entry point is still in the C runtime, which zeros globals, etc.

    Is is true that a Win32 console apps can do everything a "GUI" Win32 program can – but the programmer has to do more work?

  12. Tom_ says:

    As far as I can tell, yes, console programs have all the power of the normal Windows ones. Indeed virtually all my programs are console ones in debug builds, so I can use printf for debugging messages. (This is not advice. Just information.)

    The amount of work required is minimal.

    You do need a main() function, that calls WinMain. I just say "return WinMain(0,0,0,0);".

    You don’t actually need real WinMain arguments, because you can get them elsewhere. GetModuleHandle(0) retrieves you hInstance, hPrevInstance is always 0, and GetCommandLine or COmmandLineToArgvW will suffice for the arguments (though they are not quite in the same form as lpCmdLine). You might not need do anything about nShowCmd, because ShowWindow handles that for you automatically the first time it’s called, but if you need it later you can use GetStartupInfo to find it.

    Look in the C runtime source code (crtexe.c); the difference between calling main and calling WinMain is not great.

  13. Random Reader says:

    The WinMain signature as documented in the PSDK is valid though.  The Windows loader expects to find a pointer to a function of that style in the PE header’s EntryPoint slot for exe files.  You can use the MS linker’s /ENTRY switch to set that whatever function you want; I do that when not linking with the CRT.  (Why don’t I link with the CRT?  Because I’m writing a small utility that uses only the bare Windows API, and I don’t want the CRT’s size overhead in my final tiny executable.)

    DriverDude, the only difference between console and GUI applications at the OS level is one flag in the PE header that tells the Windows loader to pre-create a console for it (and attach to the caller’s console, if appropriate).  Everything else is identical.

    Note the CRT does much more initialization work for console apps, to provide the standard C environment everyone expects.

  14. Sven Groot says:

    "Is is true that a Win32 console apps can do everything a "GUI" Win32 program can – but the programmer has to do more work?"

    Yes this is true. You don’t even need to do more work, really. The only difference is that a console app will show a console, and that console doesn’t close until the app finishes (or if the user closes the console, it kills the program). If a console app that shows UI is started from the console, that console won’t return to the prompt until the app is finished unlike what it does for a GUI app.

    This is different from most *nixes which don’t make that distinction. An app launched from e.g. the Gnome panel doesn’t get a console, period, and a GUI app started from a console will always block the console unless it’s started in background mode (or suspended and then backgrounded). The GUI app can also still write to that console, whereas printf/cout statements from a Windows GUI app end up in a void unless a debugger is attached or CreateProcess is used to redirect those streams.

    (I’m doing a lot of this from memory, feel free to correct me if I got some details wrong).

  15. Dean Harding says:

    I would be interested in finding out how the

    entry point gets selected.

    It depends on what you select for the /SUBSYSTEM linker parameter. If you don’t specify a /SUBSYSTEM parameter, the linker works it out depending on which of main() or WinMain() is defined (if both are defined I don’t know what it does – perhaps an error, or perhaps it just chooses one).

  16. Igor says:

    I prefer to build my executables with /NOENTRY linker switch and then craft their resources in such a way that Windows Explorer crashes while trying to extract an icon resulting in buffer overflow which in turn starts the executable.

    Signed,

    Evil Hax0r

  17. Robert says:

    It says the entrypoint should have the same signature as WinMain, but the OS doesn’t actually supply any parameters.

    I believe the signature of the real entry point function you specify with the /ENTRY switch is DWORD __stdcall main(void) or int __cdecl main(void) (both produce exactly the same code on a 32bit compiler). Anyway the only thing that happens after the function returns is calling ExitThread with the return value, so the state of the stack doesn’t matter much.

  18. Craig Ringer says:

    mikeb:

    <pre>

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

    int WinMain(      

       HINSTANCE hInstance,

       HINSTANCE hPrevInstance,

       LPSTR lpCmdLine,

       int nCmdShow

    );

    </pre>

    … it seems pretty clear to me why they used different names. In C it’s not even legal to have overloads, and the linkage of WinMain() / main() must be C-style, for one thing. Of course, you’re not really overloading if only one prototype and definition are visible to the compiler, but really … why complicate things by using the same name for two functions with different signatures?

    This way there are no confusing crashes in main() because it’s being called by the runtime with the WinMain arguments (but you expect traditional C-style arguments), and things generally make more sense.

    Remember that Windows and the compiler/linker are perfectly happy with a main() function. Much of my code uses just that, because I often don’t need the extra arguments from WinMain and since I write portable code all the awful Windows typedefs drive me nuts.

    I would be interested in finding out how the entry point gets selected. I’ve always assumed it’s the linker doing the job, but I just don’t understand the Windows runtime linking process and app startup as well as the *nix one. Anyone know?

  19. Craig Ringer says:

    Sven,

    You’re quite right re the behaviour of the console on *nix. After all, on UNIX apps the console is just another application that reads an app’s stdout and stderr and writes any input to its stdin . The Win32 console seems to provide the same service for Windows console apps, but it’s quite special to the application and there’s API to manipulate it. That’s just not true on *nix – if you want to manipulate the console, you use (unfortunately highly variable depending on the specific *nix flavour) escape sequences.

    Panel apps can have a console, though. The .desktop file can specify that the app should be launched in a console, in which case an xterm/gnome-terminal will be spawned with instructions to exec the program its self. Again that’s very different to Windows where the console is innate to the application and is spawned by the runtime (?) on app startup.

  20. Norman Diamond says:

    Monday, December 04, 2006 8:02 PM by Craig Ringer

    > In C it’s not even legal to have overloads,

    > and the linkage of WinMain() / main() must be

    > C-style, for one thing.

    In C it’s not legal for a program to do overloading, but implementations are required to provide two overloads for main.  Implementations have to accept programs that conform to either of the following prototypes:

    int main()

    or

    int main(int, char**)

    Implementations could provide other overloads for main if they wished.  For example some implementations allow this:

    void main()

    and/or this:

    void main(int, char**)

    Portable programs don’t take advantage of such implementation extensions.  Non-portable programs (such as PE executables) can freely use whatever an implementation offers to them.

    > I would be interested in finding out how the

    > entry point gets selected.

    Visual Studio has defaults which you can override.  Visual Studio informs the linker of whatever the setting is.

  21. Dean Harding says:

    > In C it’s not legal for a program to do overloading, but

    > implementations are required to provide two overloads for main.

    That’s not true, main() is declared as __cdecl (*), which means the caller is meant to clean up the stack. This means that if you declare your main() as:

    int main();

    The C runtime will still pass the argc and argv parameters to you, your function just ignores them. The __cdecl declaration means the C runtime is responsible for cleaning up those parameters.

    Now, some implementation DO allow you to declare main() as returning void, but this is a special case – the compiler will insert an implicit "return 0;" at the end of your function.

    This does not mean main() is an overload of main(int, char **), and it does not mean that you could just as easily provide "different overloads" of main() – just that either of those two SPECIFIC definitions will work, given the semantics of __cdecl.

    * Note: __cdecl is the Microsoft compiler specific terminology, other compilers may express the same decoration in different ways, I’m not sure.

  22. Norman Diamond says:

    Monday, December 04, 2006 9:27 PM by Dean Harding

    [Norman Diamond:]

    >> In C it’s not legal for a program to do

    >> overloading, but implementations are

    >> required to provide two overloads for main.

    >

    > That’s not true, main() is declared as

    > __cdecl (*),

    Which page of the standard gives the definition of __cdecl?  Oh wait a minute, that (*) wasn’t part of your prototype…

    > * Note: __cdecl is the Microsoft compiler

    > specific terminology

    Then try taking another look at the standard.  Implementations are required to provide two overloads for main…

    > That’s not true

    Liar.  And to think there are people calling me a troll.

  23. BryanK says:

    Sven:

    > An app launched from e.g. the Gnome panel doesn’t get a console, period

    Actually, it does — it gets whichever console you ran "startx" on, if you start X like that.

    If you start X from xdm, kdm, or gdm, then it gets whichever console xdm, kdm, or gdm is running on.  This is usually the console inherited from init, because init spawns the *dm process.  init, in turn, gets its console from the kernel at boot time — this is almost always VT 1.  It *may* be possible for it to be a serial console, but I’m not sure how well X would work if it’s started from a serial console, so I’ll ignore that.  ;-)

    (Back when I ran KDE — several years ago — I crashed some program that I’d started from the menu system.  When I exited X and went back to the console, I saw the segfault message there.)

    Craig:

    > After all, on UNIX apps the console is just another application that reads an app’s stdout and stderr and writes any input to its stdin

    Well, sort of, but not really.  Terminal emulator programs like xterm, gnome-terminal, or Eterm are actually running a virtual console.  The shell runs on this virtual console (provided by the kernel), and the terminal emulator program interprets all the output that the shell sends to the console (including escape sequences, etc.).  The emulator also takes X keysyms and does whatever to them to make the virtual console provide the corresponding characters as input to the shell.

    But if the shell is running on a real console (e.g. /dev/tty0), then there’s no program to manage this console.  All that processing is done by the kernel, interfacing directly with the screen and keyboard.

  24. Jules says:

    Norman: "Implementations are required to provide two overloads for main…"

    Calling this an "overload" is missing the point of what overloading is.  In almost all implementations of C, no extra action is required here, because of how C’s function call semantics work.  However to support both

    int main (int argc, char ** argv)

    and

    LRESULT main (HINSTANCE hInstance, HINSTANCE hInstPrev, LPCSTR szCmdLine, int nCmdShow)

    in the same implementation would require some substantial hackery.

  25. Todd Greer says:

    They could easily have used a signature like:

    int main(int argc, char** argv, HINSTANCE hInstance, HINSTANCE hInstPrev, int nCmdShow)

    Given the __cdecl calling convention, this would have automatically supported the two required signatures. It would have been a perfectly reasonable and fully compliant C extension and would have avoided the confusion of having two entry points on Windows.

    Does anyone know why they chose otherwise? mikeb’s suggestion that it was done so that an executable could have both seems reasonable, but I’m curious if the reason is known.

  26. BryanK says:

    I am just guessing here, but I’d suspect it had something to do with Win16 having to fit inside 640K of memory.

    It would have taken n more pointers (where n is the number of command-line args) to give WinMain the array-of-strings that the C standard specified.  (They could have used the same storage for the strings themselves, they just would have had to replace spaces that actually separate args with 0 bytes, and then created another array of n pointers.)  *Plus* you’d have to add all the parsing code — command.com (or whatever) got the command line as a single string, so it would have had to do the splitting on its own.

    And then Win32 kept source-code compatibility whenever possible, so they didn’t break programs where it didn’t matter.

    Now this may or may not be a good excuse (it certainly did hurt *nix compatibility a bit, though the issues are easily worked around), but it might be the reason.

  27. Norman Diamond says:

    Tuesday, December 05, 2006 8:39 AM by BryanK

    >Sven:

    >> An app launched from e.g. the Gnome panel

    >> doesn’t get a console, period

    >

    > Actually, it does […] If you start X from

    > xdm, kdm, or gdm, then it gets whichever

    > console xdm, kdm, or gdm is running on.  This

    > is usually the console inherited from init,

    Oh neat.  Then an app launched from the Gnome panel gets a console that it doesn’t have permission to either read or write, right?

    Tuesday, December 05, 2006 10:50 AM by Jules

    >> Norman: "Implementations are required to

    >> provide two overloads for main…"

    >

    > Calling this an "overload" is missing the

    > point of what overloading is.

    In a way I agree, because C programs aren’t allowed to define both overloads (which makes me want to try it in C++ ^_^).  Maybe the requirement for implementations to accept either version needs a different word, maybe overprototype?

    > In almost all implementations of C, no extra

    > action is required here,

    Whether or how much extra work is required are irrelevant to what the standard requires.  (Sometimes they’re relevant in persuading the committee to remove or adjust a requirement in the standard but that’s a separate issue.)

    > However to support both

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

    > and

    >   LRESULT main (HINSTANCE hInstance,

    >   HINSTANCE hInstPrev, LPCSTR szCmdLine,

    >   int nCmdShow)

    > in the same implementation would require some

    > substantial hackery.

    I’ll bet that in the general case it would only require a small amount of hackery.  I’ll bet that in some cases where implementations already provide definitions of LRESULT, HINSTANCE, etc., it wouldn’t require any additional hackery at all.

  28. BryanK says:

    Oh neat.  Then an app launched from the Gnome panel gets a console that it doesn’t have permission to either read or write, right?

    I’m pretty sure it does have permission, although I don’t use any *dm so I don’t know for sure.  I know that when I log in (using /sbin/agetty and /bin/login), the permissions on the controlling terminal are set so that I can read and write to it, and this terminal is the one that X programs get when started from a menu/panel/whatever.

  29. Ulric says:

    Geewhiz, what’s the point of arguing that WinMain should have been just ‘main’?  There is nothing a "standard" C program could do in that main, no stdin or stdout.  It’s totally not standard to begin with!! Who does that benifit?  Make a console app if you want to compile standard C programs on Windows!

    Think of defining a windows app as defining a plug-in for a toolkit, and the callback you need to define is to get called when it hands the program flow to you is.. WinMain.  In other toolkits, it’s something else.

  30. Norman Diamond says:

    Wednesday, December 06, 2006 8:11 AM by BryanK

    [Norman Diamond:]

    >> Oh neat.  Then an app launched from the

    >> Gnome panel gets a console that it doesn’t

    >> have permission to either read or write,

    >> right?

    >

    > I’m pretty sure it does have permission,

    > although I don’t use any *dm so I don’t know

    > for sure.

    The following observation also isn’t proof, just enough to lend a hunch.  When shutting down a Linux system, when the display reverts to the first VT in text mode, I’ve never seen any output from programs than ran during the session.  I ought to try deliberately accessing that VT though.  (/dev/tty01?  I’ll have to check.)

    > I know that when I log in (using /sbin/agetty

    > and /bin/login), the permissions on the

    > controlling terminal are set so that I can

    > read and write to it, and this terminal is

    > the one that X programs get when started from

    > a menu/panel/whatever.

    Yes, but that’s because permissions to your tty were set when you logged in through that tty, and you started X from a shell controlled by that tty.  This provides no evidence one way or the other about logins through a *dm.

  31. BryanK says:

    Yes, but that’s because permissions to your tty were set when you logged in through that tty, and you started X from a shell controlled by that tty.

    Right, that’s why I don’t know about any *dm.

    But here’s something you could do to see what TTY your X programs are running on:  start up a terminal from the panel, figure out what the terminal’s PID is, and look at /proc/<pid>/fd/0 (and 1, and 2).  The target of these virtual symlinks is where the corresponding FD is pointing.

    I suspect it’ll be /dev/tty0 or (if that doesn’t exist) /dev/tty1, but it might actually be nothing.  I don’t know.

  32. Neil says:

    On Win16 the OS used to pass the startup parameters in registers. On Win32 the OS doesn’t appear to pass the startup parameters at all, if crt0.c is anything to go by:

    mainret = WinMain(GetModuleHandleA(NULL), NULL, lpszCommandLine, StartupInfo.dwFlags & STARTF_USESHOWWINDOW ? StartupInfo.wShowWindow : SW_SHOWDEFAULT);

    Oh, and you’re also allowed to write int main(int argc, char **argv, char **envp)

  33. Owen says:

    > But here’s something you could do to see what TTY

    > your X programs are running on:  start up a

    > terminal from the panel, figure out what the

    > terminal’s PID is, and look at /proc/<pid>/fd/0

    > (and 1, and 2).  The target of these virtual

    > symlinks is where the corresponding FD is pointing.

    I grabbed my Opera instance’s PID (Started by KWin when I logged in) and found fd0 to be pointed at /dev/null. 1 and 2 are pipes

  34. KJK::Hyperion says:

    Oh, it’s very simple, actually: the entry point for an EXE must expect no parameters. None at all. It must also not return and not leak exceptions (unless it’s a mixed-mode .NET program written in Managed C++, but let’s not get silly).

    It just so happens, but don’t quote me on this, that in Windows NT a single parameter *may* be passed on the stack, a pointer to the PEB, but it’s not like you _need_ it, the PEB is pointed by a field of the TEB. It’s not mandatory either, in fact creating a process in Windows NT is a pretty liberal affair, in that the caller gets to decide exactly how many threads the process should start with, and their exact registers and stacks

    Basically, a Windows process starts with exactly zero arguments, there’s no “arguments”, only “environment”. You start from the thread’s environment, and you can reach everything else from there (command line, environment strings, DLLs, etc.), with enough indirections. This requires zero APIs, it’s 100% pointer indirections, and of course 100% undocumented – just use GetStartupInfo, GetModuleHandle, etc. and spare yourself pain and grief!

    [There are no parameters. What you see on the stack is your caller’s local variables (and it’s not the PEB as far as I can tell). This is of course subject to change at any time, and it’ll certainly be different on x64! -Raymond]
  35. BryanK says:

    Owen — that just means that somebody set Opera’s stdin to /dev/null (not quite sure why, it won’t get any input anyway…) and set stdout/stderr to pipes — in other words, somebody ran:

    opera </dev/null 2>&1 | other-program

    (Except they might have set stderr to a different pipe than stdout, so they can capture the stuff sent to each FD separately.  Doesn’t really affect anything though.)

    If I had to guess, I’d say that this is probably so that KDE can capture any error output from the program and display it in a "your program printed this error out:" type window.  Just like it does when a KDE program segfaults, except the segfault dialog has e.g. stack backtraces and such.

    What’s on the other end of those pipes?  fuser and/or lsof may be helpful.  I’d guess it’s probably dcopserver or some process like that.

    (I should have said that this kind of thing may be changed by your DE.  I’d like to know what the setup is without the DE changing anything, though; maybe I just need to patch KDE so it doesn’t do this redirection, install the patched version, start a program, and find out.)

  36. Norman Diamond says:

    Tuesday, December 12, 2006 7:39 AM by KJK::Hyperion

    > but don’t quote me on […]

    OK, at least I get to quote you on words other than […].

    > the caller gets to decide exactly how many

    > threads the process should start with, and

    > their exact registers and stacks

    Yikes.  MSDN says PROCESS_INFORMATION contains:

    *  hThread

    *   Handle to the primary thread of the newly

    *   created process.

    "THE" primary thread?  Who says there’s only one?  So then, if the caller decides that the process will start with 6 threads, how do 6 handles get shoved into hThread?

    *  dwThreadId

    *   Value that can be used to identify a thread.

    Whew, a narrow escape on that one.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index