6 ms·
I'm not a Windows programmer to a significant degree. But, I did a tiny bit of digging and discovered at WinMain is just a function called by main() in Win32 ex
by corysama 3y ago
I'm not a Windows programmer to a significant degree. But, I did a tiny bit of digging and discovered at WinMain is just a function called by main() in Win32 executables. The implicit main() is pretty trivial and WinMain is largely unnecessary.
hInstance can be retrieved from GetModuleHandle(nullptr). hPrevInstance is always NULL. lpCmdLine can be retrieved from GetCommandLine(). nCmdShow can be retrieved from GetStartupInfo() StartupInfo.wShowWindow.
And, so you can just do
#include <Windows.h>
#pragma comment(lib, "user32.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
int main(int argc, const char *argv[]) {
MessageBox(NULL, "Hello, Welcome to Win32 Programming",
"Hello, World", MB_OK | MB_ICONINFORMATION);
}
and it works the same as the code in the article. Not a significant difference. But, I like peeling back magic code to make things just a little simpler.
Can someone who knows better tell me why this is a bad idea? Or, is WinMain just an idea from 30 years ago that didn't actually go anywhere?
- andrewf 3y agohPrevInstance meant something in Win16, which Win32 tried to maintain source compatibility with. https://devblogs.microsoft.com/oldnewthing/20040615-00/?p=38873 https://devblogs.microsoft.com/oldnewthing/20040615-00/?p=38... As you point out, what really matters is the entry point the linker puts into the executable's header, it's all just convention from there.
- tom_ 3y agoWinMain and main are both called by invoke_main: https://github.com/ojdkbuild/tools_toolchain_vs2017bt_1416/blob/d3cdb9a6cb3d92f6081290849f4386a9ff2ce30a/VC/Tools/MSVC/14.16.27023/crt/src/vcruntime/exe_common.inl#L100C37-L100C37 https://github.com/ojdkbuild/tools_toolchain_vs2017bt_1416/b... (that does not look like an official repo, but it's the best I could find in terms of a github link. The file pretty much matches c:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.29.30133\crt\src\vcruntime\exe_common.inl on my PC.)
- deleted 3y ago[deleted]
- projektfu 3y agoIt's fine, but you really should call MessageBoxA if you're not going to use _T() around your strings.
- jeroenhd 3y agoWith the standard, retro compiler as used in the 90s Developer Pack example, the compiler will assume you're calling the ASCII methods unless you manually configure it to use Unicode by default. During modern Windows development you should probably default to wide characters, of course.
- garaetjjte 3y agoCan we finally go back to using *A functions, but with UTF-8?
- NavinF 3y agoYes, see sibling comment. > Until recently, Windows has emphasized "Unicode" -W variants over -A APIs. However, recent releases have used the ANSI code page and -A APIs as a means to introduce UTF-8 support to apps. If the ANSI code page is configured for UTF-8, then -A APIs typically operate in UTF-8. This model has the benefit of supporting existing code built with -A APIs without any code changes.
- skissane 3y agoUnfortunately there are still cases where the A API has bugs that the W API lacks, see e.g: https://stackoverflow.com/q/67427569 https://stackoverflow.com/q/67427569 Also, some APIs use buffers with a fixed maximum size, and the A API's buffer isn't big enough to handle the maximum possible data size for the W API. And some APIs are Unicode-only, and don't have A versions. For maximum reliability, better to use W APIs only and do the UTF-8<->UTF-16 conversion yourself. However, in practice, if you just use A APIs only and let Windows do the UTF-8 conversion for you, it is going to work 95% of the time. But maybe at some point you'll hit that 5% where it doesn't. It is more likely if you are using more obscure APIs (like my example at the start of manipulating Windows Terminal Server sessions). More likely for system-level stuff than application-level.
- zabzonk 3y agoi had a blog about this many years ago, if anyone is interested - but not about WinMain, which i must admit i have never really understood - something of an artefact of the MS linker, i think. anyway, my blog about using windows dialogs from the command line is here: https://latedev.wordpress.com/2011/10/06/simple-windows-dialogs-from-c-part-1/ https://latedev.wordpress.com/2011/10/06/simple-windows-dial...
- skissane 3y ago> 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.
- TeMPOraL 3y ago> 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. Speaking of - in GP's example code: MessageBox(NULL, "Hello, Welcome to Win32 Programming", "Hello, World", MB_OK | MB_ICONINFORMATION); 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... -- [0] - https://www.libsdl.org/ https://www.libsdl.org/
- jeroenhd 3y agoNeither main/WinMain/wmain/wWinMain are actually the methods called on program launch. Both are preceded by various setup calls (i.e. the CRT in your mainCRTStartup linker flag). The pre-main runtime doesn't do much, but it does initialize the WinMain parameters in not that a fashion not that dissimilar from your example. You could just do all that work yourself, but really, why should you? On most platforms you can get argc and argv[] through the right system calls and file I/O as well, but it's much easier to have the conventional runtime do all that stuff for you. WinMain is just a convention (https://devblogs.microsoft.com/oldnewthing/20061204-01/?p=28853 https://devblogs.microsoft.com/oldnewthing/20061204-01/?p=28...) for your C/C++/etc runtime to start executing the code you write. Linux isn't all that different, adding runtime code before the main() method where necessary.
- amluto 3y ago> On most platforms you can get argc and argv[] through the right system calls Not (reliably) on Linux or, as far as I know, on similar systems. argv, environ, and the aux vector come from a horrible data structure the kernel creates on the stack.
- ithkuil 3y agoThe structure may be "horrible" but why so you say it's unreliable? It's part of the kernel/user space contract. The same information is also exposed via the /proc FS FWIW
- amluto 3y agoI’m saying you can’t reliably get the information from syscalls. The runtime (i.e. whatever implements the actual entry point declared in the ELF headers) can get it reliably, as can any other code to which the runtime gives an appropriate pointer. You can’t assume that /proc is procfs if you’re writing a low level runtime library.
- ithkuil 3y ago