5

First a caveat, I only very recently started learning about the WinAPI. I'm sure this question has been asked many times before, but for some reason I can't find it anywhere online. The question is simply this; why bother with the initial call to ShowWindow() in the body of WinMain() before the execution of the message loop? Why not simply set the window to be initially visible through use of the WS_VISIBLE flag?

I also have some questions as to the mechanics of the ShowWindow() function. Which messages does it actually send? In MSDN it states that:

If a window has the WS_VISIBLE style when it is created, the window receives this message [WM_SHOWWINDOW] after it is created, but before it is displayed. A window also receives this message when its visibility state is changed by the ShowWindow or ShowOwnedPopups function.

Does this mean the primary means of communication between the ShowWindow() function and Windows is through the WM_SHOWWINDOW message? It also states that:

The WM_SHOWWINDOW message is not sent under the following circumstances:

  • When a top-level, overlapped window is created with the WS_MAXIMIZE or WS_MINIMIZE style.

  • When the SW_SHOWNORMAL flag is specified in the call to the ShowWindow function.

The MSDN also states that:

The first time an application calls ShowWindow, it should use the WinMain function's nCmdShow parameter as its nCmdShow parameter.

Petzold states that the argument passed to this nCmdShow parameter will be either SW_SHOWNORMAL, SW_SHOWMAXIMIZED or SW_SHOWMINNOACTIVE. Am I to take from this that the only time the ShowWindow() function does not send a WM_SHOWWINDOW message, is when we make that very first initial call to it in Winmain()? If so, how does it then get the window to display? Also, how does all of this relate to the actual painting of the window?

I'm sorry if my question is bit of a jumbled mess, but the mechanics of showing a window kind of confuse me, and for some reason it's hard to find clear answers to these questions online (as opposed to just bits and pieces of information). Any help in clarifying all of this will be greatly appreciated!

Xantium
  • 11,201
  • 10
  • 62
  • 89
PvtWitt
  • 79
  • 4
  • If you use `WS_VISIBLE` style on the main window, then the system implicitly calls `ShowWindow` (the win32k function in the kernel, of course). It defaults to `SW_SHOW` unless `x` is `CW_USEDEFAULT` and `y` is not `CW_USEDEFAULT`, i.e. `y` is some `nCmdShow` value. – Eryk Sun May 29 '18 at 04:22
  • The first time `ShowWindow` is called for the main window, either implicitly or explicitly, and *only the first time*, then, if `nCmdShow` is `SW_SHOW`, `SW_SHOWNORMAL`, or `SW_SHOWDEFAULT`, then the system instead uses either the `STARTUPINFO` `wShowWindow` value if the `STARTF_USESHOWWINDOW` flag is set or otherwise `SW_SHOWNORMAL` if the flag is not set. The `STARTUPINFO` value is only used for the *first* call. Subsequent calls to `ShowWindow` that specify `SW_SHOWDEFAULT` will actually use `SW_SHOWNORMAL`. – Eryk Sun May 29 '18 at 04:23
  • About `[w]WinMain` -- of course its value of `nCmdShow` is just the `STARTUPINFO` `wShowWindow` value if the `STARTF_USESHOWWINDOW` flag is set, and otherwise `SW_SHOWDEFAULT`. – Eryk Sun May 29 '18 at 04:30
  • Also, an answer below implies a process could somehow not have `STARTUPINFO`. That makes no sense. WinAPI `STARTUPINFO` is an amalgamation of NT's `RTL_USER_PROCESS_PARAMETERS` and creation attributes (i.e. `STARTUPINFOEX` `lpAttributeList`). A pointer to the `ProcessParameters` always exists in the Process Environment Block. This includes the WinAPI `STARTUPINFO` record, including `WindowFlags`, `ShowWindowFlags`, `WindowTitle`, `DesktopInfo`, `ShellInfo`, `StandardInput`, etc. It's what WinAPI `GetStartupInfo` use to reconstruct the `STARTUPINFO` record. – Eryk Sun May 29 '18 at 04:32

2 Answers2

2

The idea behind the nCmdShow parameter to WinMain is that it gives Windows a chance to let your application know how Windows would like it to show the window. That mechanism is probably no longer useful, but maybe there are edge cases. In any case, you should pass it on to whatever you consider your main window after you have created it. Creating it hidden allows you to create any child windows without flicker, so that's what most people do.

I think the logic behind when WM_SHOWWINDOW is and isn't sent is to let you use it to catch calls to ShowWindow (hWnd, SW_HIDE) and ShowWindow (hWnd, SW_SHOW) in your window proc, since it's likely that you might want to take some action at that time (such as stop playing audio, for example). And maybe also SW_MINIMIZE, SW_MAXIMIZE and SW_RESTORE, I guess it all depends.

Does that help at all?

Edit

Well, quite a lot of information has been posted to this thread so I thought I would try to summarise it as best I understand it. Here goes.

  1. The nCmdShow parameter to WinMain appears to be historical. Instead, the first call to ShowWindow acts as if you have passed it this value, whether you like it or not, so that call had better be to your main window. Still, that said, you might as well play the game and pass it on, you never know.

  2. Read and understand Hans Passant's comment to this post. That will tell you where in the Windows UI this value most commonly comes from.

  3. Just FYI, it's OK to create your child windows with WS_VISIBLE set. You won't see them until you show the main window.

OK, I'm done. Sometimes less is more.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • 1
    It is still useful, set by the Run property of the desktop shortcut. Albeit that SW_HIDE is intentionally omitted, that's a malware option :) – Hans Passant May 28 '18 at 12:18
  • @HansPassant Oh yes, it's still there, just like in the good old days. – Paul Sanders May 28 '18 at 13:11
  • Thanks, it does help. So the idea behind calling `ShowWindow()` is to be able to actually define additional elements of the client area (such as child windows), before the main window is actually displayed to prevent the eventual occurrence of a certain “lag” when the main window is already displayed but certain elements of its client area are still being created? Also, you are probably right about the use of `WS_SHOWWINDOW` (though i did find out it is only sent when the window visibility changes, for minimizing/maximizing/restoring a `WM_SIZE` message is sent instead). – PvtWitt May 29 '18 at 09:37
  • Oh, OK, that [WM_SIZE] makes sense. I see also from re-reading the [documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632645%28v=vs.85%29.aspx) that there are other circumstances where `WM_SHOWWINDOW` is sent. And about `ShowWindow ()`, mainly, yes. – Paul Sanders May 29 '18 at 10:36
  • If you don't use `WS_VISIBLE` and thus explicitly call `ShowWindow`, you don't have to use the `nCmdShow` value that's passed to `WinMain` by the real C/C++ entry point. This is a redundancy from legacy Windows programming. `nCmdShow` is from the `ProcessParameters` if the `STARTF_USESHOWWINDOW` flag is set and otherwise `SW_SHOWDEFAULT`. The window manager already defaults to using this value the first time `ShowWindow` is called for the main window with `SW_SHOW`, `SW_SHOWNORMAL`, or `SW_SHOWDEFAULT`. Everything about `WinMain` can be ignored. It's just cruft. – Eryk Sun May 29 '18 at 22:12
1

why bother with the initial call to ShowWindow() in the body of WinMain() before the execution of the message loop?

The answer is in the ShowWindow() documentation:

nCmdShow

Controls how the window is to be shown. This parameter is ignored the first time an application calls ShowWindow, if the program that launched the application provides a STARTUPINFO structure. Otherwise, the first time ShowWindow is called, the value should be the value obtained by the WinMain function in its nCmdShow parameter.

If the app is started by the user, there is no STARTUPINFO, and nCmdShow from WinMain() should be used to determine how your main UI should be displayed (or not).

If the app is started by the system, or by another app, there is likely to be a STARTUPINFO, so you should ignore nCmdShow from WinMain() and use the nCmdShow from the STARTUPINFO instead.

Calling ShowWindow() handles both conditions for you. But if you force the window visible with VS_VISIBLE, you are not respecting how the caller wishes your app to appear (or not) at startup.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks for your answer. According to Petzold, the use of the `nCmdShow` argument from `WinMain()` only really pertains to "the preferences the user set when adding the program to the Start Menu". I'm not even sure if that's possible anymore. It seems to me it either defaults to `SW_SHOWNORMAL` (so based on the program specs), or `SW_SHOWDEFAULT` (when a parent process is involved). I think the omission of the `WS_VISIBLE` flag has much more to do with the answer Paul Sanders gave above though. – PvtWitt May 29 '18 at 10:17
  • @PvtWitt "*I'm not even sure if that's possible anymore.*" - of course it is still possible. And shortcuts can be created anywhere on the file system, not just on the Start Menu. – Remy Lebeau May 29 '18 at 14:51
  • 1
    @PvtWitt, the OS doesn't call `[w]WinMain`. It calls the executable entry point with a pointer to its Process Environment Block (PEB), which itself is a redundant parameter since a thread can easily get the TEB and PEB. For a Microsoft C/C++ application, the real entry point calls `GetStartupInfo` to reconstruct the `STARTUPINFO` record from the PEB's `ProcessParameters`, which every process *must have*. If the `STARTF_USESHOWWINDOW` flag isn't set, the C/C++ entry point defaults to `SW_SHOWDEFAULT` when it calls your `[w]WinMain` function. – Eryk Sun May 29 '18 at 21:59
  • @RemyLebeau My apologies, you are absolutely correct. Thanks for your answers! – PvtWitt Jun 06 '18 at 12:47