> Or, is WinMain just an idea from 30 years ago that didn't actually go anywhere?
I believe it is a leftover from 16-bit Windows, where (at least originally) it actually had to be WinMain. Although even there it all depends on the compiler and runtime library.
Windows 3.x had no console subsystem, so Windows applications had to be graphical; command line apps were DOS-only. Then Microsoft introduced "QuickWin", which was a wrapper which turned simple (plain text-only) command line apps into graphical Windows apps. In QuickWin, you'd supply the main(), and QuickWin would supply the WinMain(), and Windows would call QuickWin's WinMain(), and then QuickWin would call your main(). Windows NT introduced a proper console subsystem, and then Windows 95 introduced this bizarre abomination in which 32-bit console apps were supported, but their IO was routed via a DOS program called CONAGENT.EXE. So your Win32 console app would call some DLL, which would spawn CONAGENT.EXE in a DOS Box, and then there was some VXD which the DLL and CONAGENT.EXE used to communicate, so your IO would go via the DOS Box.
In Windows NT (and descendants), WinMain is no longer necessary, but is still (partially) supported for backward compatibility. I guess a lot of people stick with it just by reason of tradition. It does help communicate (to a person reading the code) that you are dealing with a graphical app rather than a console one, so maybe not totally useless.
> Windows 95 introduced this bizarre abomination in which 32-bit console apps were supported, but their IO was routed via a DOS program called CONAGENT.EXE.
Windows 95 used the 16-bit COMMAND.COM as its default command-line shell, so doing it this way was probably necessary to make 32-bit console applications interoperate with the command shell (and support eg. piping and redirection between 16/32-bit executables).
> Windows 95 used the 16-bit COMMAND.COM as its default command-line shell, so doing it this way was probably necessary to make 32-bit console applications interoperate with the command shell (and support eg. piping and redirection between 16/32-bit executables).
I think that's got the arrow of causation reversed.
Windows 9x did it this way because it didn't have a 32-bit console subsystem. They couldn't have easily ported NT's 32-bit console subsystem to 9x/Me, because it was deeply tied in to how DOS Boxes are implemented (NTVDM), and that's radically different between NT and 9x/Me (which have basically the same architecture in that regard as Windows 3.1 in 386 Enhanced Mode). And also deeply tied into NT architecture components that 9x/Me lacked (CSRSS.EXE and LPCs)
And they used 16-bit COMMAND.COM as the primary shell, because without a 32-bit console subsystem, the value of adopting CMD.EXE was rather limited. It would have allowed some more advanced batch files.
Actually Microsoft did port CMD.EXE to Windows 95 and 98, but unclear if they ever officially released it. It was shipped in some Windows betas and beta SDKs, and some people got it from there and redistributed it (might not be technically legal but I doubt that anyone at Microsoft really cares, especially by now) – http://cygutils.fruitbat.org/consize/index.html
COMMAND.COM did piping using temporary files. I think even in NT versions, redirection works when starting a 32-bit console executable from a DOS app. I wish I had a 32-bit Windows VM handy to test that with. (Pity pcjs.org has Windows 95 but no NT versions, not even NT 3.1/3.5/3.51/4.0)
> I guess a lot of people stick with it just by reason of tradition.
Or nostalgia, or muscle memory. I kinda learned this one by heart as a kid, and could still recite even in my sleep: "int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, ... " - well, I forgot about nCmdShow. And don't ask me about WINAPI vs. APIENTRY (which is used in the article) - I've seen both used, and I'm pretty sure I picked the former from Petzold (i.e. Programming Windows, edition ${whichever it was that I found in a library in around 2002).
There was also the other one, let's see if I remember. "Something something WndProc(HWND hWnd, UINT uMsg, WPARAM, LPARAM)"? That one I usually copy-pasted between projects - as all I ever needed is a window to draw on with OpenGL.
On that note, WinMain() isn't the only case I've seen of substituting main(). MSVC/Windows had at some point _tmain(), as in:
int _tmain(int argc, _TCHAR* argv[])
It's there so you could write code generic wrt. short/long character strings - which, in the usual Microsoft fashion, was achieved by #define-s - depending on a flag, _tmain() expands to main() or wmain(), then _TCHAR expands to char or wchar_t, etc.
The function MessageBox() also doesn't exist - actual system DLLs provide you with MessageBoxA() and MessageBoxW(), and MessageBox is again a #define, in the style of _tmain().
Anyway, back to overriding main() - SDL[0] also encouraged you to write an SDL_main() instead of main().
Thanks for the morning nostalgia trip and for filling back specifics of APIs I learned as a kid, without understanding much of them.
> Windows 95 introduced this bizarre abomination in which 32-bit console apps were supported, but their IO was routed via a DOS program called CONAGENT.EXE. So your Win32 console app would call some DLL, which would spawn CONAGENT.EXE in a DOS Box, and then there was some VXD which the DLL and CONAGENT.EXE used to communicate, so your IO would go via the DOS Box.
You mean 16-bit console apps running in 32-bit OS, or 32-bit console apps using 16-bit I/O?
Funny how those problems never die. I only recently learned about "DLL surrogates" and Windows mechanisms for mixing 32-bit and 64-bit code by having 32-bit DLL proxied by a system-provided executable. I even wrote a DLL-proxying executable once, because the nature of legacy proprietary code is that sometimes you really need that 32-bit DLL the vendor, for whatever reason, refuses to rebuild for 64-bits...
> You mean 16-bit console apps running in 32-bit OS, or 32-bit console apps using 16-bit I/O?
I mean on Win9x/Me, 32-bit console apps are forced to use 16-bit I/O. The 32-bit console APIs are implemented using 16-bit MS-DOS IO via the VCOND VXD and CONAGENT.EXE. There was never a 16-bit console API for Windows, so all 16-bit console apps were either DOS or OS/2 1.x. (NT originally could run OS/2 1.x console apps, although the support was withdrawn at some point; 9x/Me never could, except for family mode executables.)
> I only recently learned about "DLL surrogates" and Windows mechanisms for mixing 32-bit and 64-bit code by having 32-bit DLL proxied by a system-provided executable. I even wrote a DLL-proxying executable once, because the nature of legacy proprietary code is that sometimes you really need that 32-bit DLL the vendor, for whatever reason, refuses to rebuild for 64-bits...
That’s something I’d be interested to know more about. Microsoft put a lot of effort into ensuring interoperability between 16-bit and 32-bit code, but when it came to do the same for 32-bit/64-bit, decided not to
I believe it is a leftover from 16-bit Windows, where (at least originally) it actually had to be WinMain. Although even there it all depends on the compiler and runtime library.
Windows 3.x had no console subsystem, so Windows applications had to be graphical; command line apps were DOS-only. Then Microsoft introduced "QuickWin", which was a wrapper which turned simple (plain text-only) command line apps into graphical Windows apps. In QuickWin, you'd supply the main(), and QuickWin would supply the WinMain(), and Windows would call QuickWin's WinMain(), and then QuickWin would call your main(). Windows NT introduced a proper console subsystem, and then Windows 95 introduced this bizarre abomination in which 32-bit console apps were supported, but their IO was routed via a DOS program called CONAGENT.EXE. So your Win32 console app would call some DLL, which would spawn CONAGENT.EXE in a DOS Box, and then there was some VXD which the DLL and CONAGENT.EXE used to communicate, so your IO would go via the DOS Box.
In Windows NT (and descendants), WinMain is no longer necessary, but is still (partially) supported for backward compatibility. I guess a lot of people stick with it just by reason of tradition. It does help communicate (to a person reading the code) that you are dealing with a graphical app rather than a console one, so maybe not totally useless.