WinMain is just the conventional name for the Win32 process entry point


WinMain is the conventional name for the user-provided entry point in a Win32 program. Just like in 16-bit Windows, where the complicated entry point requirements were converted by language-provided startup code into a call to the the user's WinMain function, the language startup code for 32-bit programs also does the work of converting the raw entry point into something that calls WinMain (or wWinMain or main or _wmain).

The raw entry point for 32-bit Windows applications has a much simpler interface than the crazy 16-bit entry point:

DWORD CALLBACK RawEntryPoint(void);

The operating system calls the function with no parameters, and the return value (if the function ever returns) is passed to the ExitThread function. In other words, the operating system calls your entry point like this:

...
  ExitThread(RawEntryPoint());
  /*NOTREACHED*/

Where do the parameters to WinMain come from, if they aren't passed to the raw entry point?

The language startup code gets them by asking the operating system. The instance handle for the executable comes from GetModuleHandle(NULL), the command line comes from GetCommandLine, and the nCmdShow comes from GetStartupInfo. (As we saw before, the hPrevInstance is always NULL.)

If you want to be hard-core, you can program to the raw entry point. Mind you, other parts of your program may rely upon the work that the language startup code did before calling your WinMain. For example, the C++ language startup code will run global constructors before calling into WinMain, and both C and C++ will initialze the so-called security cookie used as part of stack buffer overrun detection. Bypass the language startup code at your peril.

Bonus chatter: Notice that if you choose to return from your entry point function, the operating system passes the return value to ExitThread and not ExitProcess. For this reason, you typically don't want to return from your raw entry point but instead want to call ExitProcess directly. Otherwise, if there are background threads hanging around, they will prevent your process from exiting.

Comments (35)
  1. Adam Rosenfield says:

    I think you mean "stack buffer overrun detection", not "stack overflow detection". Stack overflow detection is a completely different problem, which isn't always even possible if you try to allocate such a ridiculously large amount of stack data (e.g. an enormous array) such that the stack pointer moves far beyond the maximum possible amount of stack space and into an otherwise valid piece of memory.

    [Fixed, thanks. -Raymond]
  2. JoeG says:

    The latest version of Visual Studio ships with the source to the C++ runtimes, so you can go take a look at what's happening in between the entry point and WinMain. It's pretty interesting.

  3. Joshua says:

    @Adam: it actually does detect that in 64 bit. There is an assembly routine that gets called during function prologue if you have more than 4k of automatic variables that grows the stack via writing one byte every 4k from the old top of stack to the new.

  4. S says:

    @Joshua: 32-bit does that too. The reason being that the stack is commited dynamically as you use it, even on 32-bit, so you have to touch the pages in order so the stack gets committed anyway. Of course that is really a compiler feature, not an OS feature – there is nothing to stop you doing add esp, [Big Number]; push eax; – that overflow may not be detected.

  5. Mike says:

    @Joshua, @S

    Raymond wrote a blog post some time ago about that mechanism, in describing the problem with checking pointers to see if they pointed to valid space. (I don't recall enough detail to easily find the URL, though)

  6. Falcon says:

    @Mike:

    I believe the post you're thinking of is blogs.msdn.com/…/773741.aspx

  7. SimonRev says:

    @Mike,

    IIRC, the conclusion to that series was that yes you could do that, but if you ever get an invalid buffer, your program has experienced a severe programming error and the only safe thing to do is crash anyway (possibly logging on the way out).  

  8. rs says:

    The nice thing about

       DWORD CALLBACK RawEntryPoint(void);

    is that it is equivalent to the C standard

       int main(void);

    (except for the signed/unsigned difference which doesn't matter in ExitThread). The difference in calling conventions is irrelevant because of the empty argument list.

  9. Mike says:

    @Falcon, SimonRev: Indeed.

  10. Alex Grigoriev says:

    @rs:

    Except it's different calling convention, which may or may not matter. For IA64 it might.

    Also, main() is not an actual raw entry point anyway.

  11. Evan says:

    @rs

    Constructor invocations and other static initialization take place before main() starts. From Raymond's article, that takes place after RawEntryPoint().

    So no, they really aren't equivalent.

  12. Richard says:

    I thought this title sounded familiar!

    blogs.msdn.com/…/1205831.aspx

  13. Humus says:

    One actual use of defining your own custom entry point is for 1K or 4K demos, because all the cruft for startup and tear down takes about 20-30KB.

  14. Joshua says:

    I've actually used the raw entry point before when not linking against any libc. I didn't know what it was called or what arguments, so declared and called it WinMain, ignored the parameters, and called ExitProcess at the bottom.

    The linker is very nice and requires the entry point to exist and if none is declared it uses the first symbol in the first object file provided.

    Since I wasn't using libc I didn't need its startup code to run.

    @Mike: You know about the guard page at top of stack right?

  15. 640k says:

    > Why isn't it called main?

    > [Because that name was already taken. I can't believe I had to write that. -Raymond]

    main() isn't an entry point in gui programs. main() could have been used.

    [The name main() is reserved by the standard. The underlined text is missing from the standard: "5.1.2.2.1.2 The parameters to the main function shall obey the following constraints, unless it's a GUI program, in which case the following rules do not apply." -Raymond]
  16. Evan says:

    @640K

    See the link Richard posted (blogs.msdn.com/…/1205831.aspx), and in particular mikeb's speculation and Tom_'s comment. That page has a lot of discussion about main vs WinMain.

  17. Random832 says:

    I think I posted this before and it ate my comment.

    Is the raw entry point WinMainCrtStartup [or whichever], or is it literally called RawEntryPoint, or is it called something else, and what's the relationship between it and WinMainCrtStartup if it's not the same? What is __ImageBase? If it's not actually called RawEntryPoint, how do you tell the compiler that some function you provide is meant to be the raw entry point?

    [RawEntryPoint is a placeholder name. The operating system doesn't know what name you called your entry point function. It just calls the function whose address is given in the PE header as the entry point. -Raymond]
  18. Tergiver says:

    @Random832: You tell the linker (not the compiler) what the "raw entry point" is. The linker is defaulted to some compiler-specific runtime library method (like WinMainCrtStartup) if you don't change it.

  19. Crescens2k says:

    Random832:

    In addition to what Tergiver said, the linker checks the type of application, console or GUI, then links in the correct startup object, one. There are 4 in total for user mode applications, mainCrtStartup, wmainCrtStartup, WinMainCrtStartup and wWinMainCrtStartup. If there is no entry point entry in the linker options, it links in one of those and then puts that as the entry point.

  20. avakar says:

    Isn't the entry pointer actually called with a single parameter pointing to the PEB?

  21. Ben Voigt [Visual C++ MVP] says:

    The documentation ( msdn.microsoft.com/…/f9t8842e(v=VS.90).aspx ) describes a different signature:

    "The /ENTRY option specifies an entry point function as the starting address for an .exe file or DLL.

    The function must be defined with the __stdcall calling convention. The parameters and return value must be defined as documented in the Win32 API for WinMain (for an .exe file) or DllEntryPoint (for a DLL)."

    Newer versions of the documentation instead say "The parameters and return value depend on if the program is a console application, a windows application or a DLL."

    Of course, not using any parameters and not returning is safest, because then the calling convention doesn't matter.  Your code doesn't care about the stack layout when it starts, and the caller doesn't get a chance to care about the stack layout after your function runs.

  22. Florian Kruegel says:

    So WinMain doesn't really exist from the operating system point of view; it's a C runtime library callback function provided by Visual C++. I don't quite understand why it's documented as an API callback function then (the current documentation has some hints in the right direction, but I remember that previous versions of the documentation clearly implied that WinMain was an API callback function).

    As I understand it, WinMain used to be an API callback function in Windows 3. As you said, for compatibility the hPrevInstance parameter was retained, even though it no longer does anything. So this has to be entirely about source code compatibility.

    In my eyes, along the introduction of Windows 95, WinMain should have been deleted from the documentation (or marked as obsolete), but retained in the header files for source code compatibility. Instead the raw entry point should have been documented (together with the remark that code for it is usually provided by runtime libraries). Then maybe runtime libraries could have implemented improved startup code that calls extended versions of WinMain, for example, to provide advanced command line parsing like it's done for main. Is there a specific reason why this route was not followed?

    In a similar spirit, Structured Exception Handling does not seem to be documented from the operating system point of view; only how you access it from Visual C++.

    [As I noted in the linked article, WinMain was a conventional name even in 16-bit Windows. And the runtime libraries have already done what you suggest (for example, wWinMain). But removing WinMain from the MSDN docs would be counterproductive. Most people who read MSDN are not compiler writers. -Raymond]
  23. Robert W. says:

    In "Programming Applications for Microsoft Windows 4th Edition" Jeffrey Richter states (p197) that BaseProcessStart looks something like this:

    VOID BaseProcessStart(PPROCESS_START_ROUTINE pfnStartAddr) {

    __try {
    
        ExitThread((pfnStartAddr)());
    
    }
    
    __except(UnhandledExceptionFilter(GetExceptionInformation())) {
    
        ExitProcess(GetExceptionCode());
    
    }
    
    // NOTE: We never get here.
    

    }

    This is completely consistent with Raymond's post.

    However in the next edition "Windows via C/C++ 5th Edition" Richter has a revised description, stating that a process' primary thread begins in the same function as a secondary thread. He states (p157) that this function RtlUserThreadStart basically does the following:

    VOID RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {

    __try {
    
        ExitThread((pfnStartAddr)(pvParam));
    
    }
    
    __except(UnhandledExceptionFilter(GetExceptionInformation())) {
    
        ExitProcess(GetExceptionCode());
    
    }
    
    // NOTE: We never get here.
    

    }

    If this is indeed where the primary thread starts executing, then the situation is that the raw entry point is passed a pointer-sized parameter, but its value is undefined.

    I wonder why Richter changed the description in the 5th Edition. It could just have been an editorial decision to save space (by avoiding discussing the primary thread start function separately from the secondary thread start function). Maybe he was happy to gloss over the pvParam parameter inconsistency. Or maybe he was reflecting a change made in more recent versions of Windows?

  24. Crescens2k says:

    Robert:

    Process start is different from a thread start. A process start just starts the process and starts executing at the entry point in the executable header. A thread start occurs as a result to CreateThread or similar, and if you look at the signature, is identical to the ThreadProc callback function (msdn.microsoft.com/…/ms686736(VS.85).aspx).

  25. yuhong2 says:

    For IA64 it might.

    IA64 and x64 only have a single calling convention unlike x86.

  26. Joseph Koss says:

    @Yuhong Bao:

    The AMD64 hardware does not actually impose any calling convention specifics at all. Note the differences between Microsoft's and Apple's (aka BSD) AMD64 conventions.

    We are merely lucky that others havent imposed themselves.

  27. Neil says:

    In 16-bit windows you could bypass most of the CRT by linking in the stub library snocrtw. My smallest useful Win16 executable was 608 bytes, but I hadn't checked for obscure options that might have reduced this further.

  28. 640k says:

    > [The name main() is reserved by the standard. The underlined text is missing from the standard: "5.1.2.2.1.2 The parameters to the main function shall obey the following constraints, unless it's a GUI program, in which case the following rules do not apply." -Raymond]

    You could have implemented the standard main() for gui programs. You could have implemented command args parser, just as console program do, you already got the code in your compiler! But you dont want to, you don't want portable code. There's your real problem.

    [If you've decided to #include <windows.h> you've already accepted that you're writing code that's not portable to non-Windows systems. Last time I checked, windows.h is not part of the C standard. -Raymond]
  29. Medinoc says:

    @640k: Raymond already explained that portable code takes memory. And with a screen name like yours, you of all people should know how memory was important back then.

  30. asdbsd says:

    @640k

    You could have implemented the standard main() for gui programs. […] But you dont want to, you don't want portable code.

    Look, everyone, here's someone who talks about Windows GUI code portability. Ha-ha-ha!

  31. Evan says:

    @640K: You also ignored my post, pointing you to discussions about how at least old programs probably had a (standard) main() function that set things up then dispatched to WinMain(). This was so they could do something reasonable under DOS.

  32. 640k says:

    main could have been used instad of WinMain. Not instead of RawEntryPoint.

  33. 640k says:

    @Medinoc: Raymond already explained that portable code takes memory. And with a screen name like yours, you of all people should know how memory was important back then.

    Calling the application source code entry point main instead of WinMain takes no more memory than for console apps, it should take less in fact.

  34. Pawel says:

    This is very good when a small utility application with window need to be created.

    The raw entry point I used is:

    void main ( void )

    {

    ExitProcess(DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_APP),NULL,DialogProc));
    

    }

    And the executable file size in Release mode is 3KB, or less if I remove program icon, which is included in the resource.

  35. James says:

    @640k:

    You could have implemented the standard main() for

    gui programs. You could have implemented command args

    parser, just as console program do, you already got

    the code in your compiler!

    There's nothing stopping you from writing a program that uses main() with argc and argv, that creates windows and runs a message loop, and linking it as a SUBSYTEM:WINDOWS application.  You can even change the SUBSYSTEM type on an existing  binary with editbin.

Comments are closed.