> This might make you realize why MDI (or Multiple Document Interface, where there are multiple small per-document "windows" inside one big window) was so popular back then. The MDI "windows" weren't actually windows — they didn't have their own WndProc. They were just controls, like a tab view is a control. Only the big container window was a real window, and so all the resources within that big window were shared between all the virtual windows. MDI was a memory-saving trick!
MDI may have saved some memory - I can't say one way or the other on that - but the mechanism you describe is incorrect.
Every MDI child window was a window of its own with its own WndProc. Every control inside those windows was also a window with its own WndProc. Every dialog box was also - yes - a window with its own WndProc.
You wouldn't always be aware of the WndProc in your code, but it was there.
If you ran WinSight or Spy++, you could see the entire window hierarchy including all the child windows, child control windows, and so on.
Later on, a few applications implemented "windowless controls" to save memory, but this was uncommon, especially in the early days. For example, there was an optional windowless version of the Rich Edit control:
Fun fact: an early informal name for MDI was "Mac in a box", because if you maximized the top level window, you had a somewhat Mac-like environment, with one menu bar at the top that was shared by the child windows.
Source: I was the author of WinSight and the VBX (Visual Basic eXtension) API.
Interesting; through the fog of time, I may have misremembered some "tip" I was given for Windows 3.1 programming efficiency, as being a more definitive statement about the internal structure of Windows than it really was.
That tip, as I recall it, was that the developer should minimize the number of top-level windows they create, because each top-level window in the system gets opted automatically into various things that regular controls don't (including having a bunch of default child controls that probably keep a lot of state and pump a lot of messages.) But MDI child windows don't have the same window-class as top-level windows, and are instead pruned down to be much "lighter", both in doing things like:
- not having a some child controls (e.g. a menu bar) by default (you're supposed to attach the menu bar to the MDI frame instead, and change it to reflect the menu of the active child — as you say, it's "a Mac in a box" behavior);
- implementing behaviors that seem like their own child controls, but which are actually parts of the MDI child's own geometry, to reduce the number of event loops that need to be pumped. (I believe an MDI child's default-styled title bar might be this way, only becoming a full-on child control tree if the MDI child is given non-default styles.)
- "Sharing" child controls, where the control (I think an MDI child window's caption buttons might be this way?)
- routing messages to only the active child, with the others essentially frozen except when they need to redraw
Digging around, evidence to corroborate this is pretty scant, but there is some:
- https://jeffpar.github.io/kbarchive/kb/095/Q95578/ is a bug that is evidence of the MDI child window class having most of its behaviors programmed separately, rather than there being any code reuse between top-level windows and MDI child windows
Thanks for the interesting discussion. Yes, the fog of time affects us all.
Funny you mention caption buttons (min/max/close/etc.) and menus and such. I meant to add a "Fun fact #2" in my first comment. So here we go...
In traditional Windows applications, none of those are child windows (or child controls, same thing). They are all part of the "non-client area". You may recall there being a whole series of WM_NC... messages like WM_NCMOUSEMOVE and WM_NCPAINT, with the same names as your usual messages except for the NC prefix. Your WndProc would receive all of these messages, but generally would just pass them along to DefWindowProc which handled this "non-client" activity.
Now here is the fun fact. OS/2 Presentation Manager took a different and cleaner approach. It removed the "non-client area" concept entirely, along with all those messages. Instead, all of those window doo-dads were child windows. The minimize button was a child window. Your "client area" was a child window. And so on. It was all child windows.
On this point:
> implementing behaviors that seem like their own child controls, but which are actually parts of the MDI child's own geometry, to reduce the number of event loops that need to be pumped.
To be clear, a typical application had only one event loop. It was your classic GetMessage/TranslateMessage/DispatchMessage loop. DispatchMessage would pass the message off to whatever WndProc it should be directed to.
MDI may have saved some memory - I can't say one way or the other on that - but the mechanism you describe is incorrect.
Every MDI child window was a window of its own with its own WndProc. Every control inside those windows was also a window with its own WndProc. Every dialog box was also - yes - a window with its own WndProc.
You wouldn't always be aware of the WndProc in your code, but it was there.
If you ran WinSight or Spy++, you could see the entire window hierarchy including all the child windows, child control windows, and so on.
Later on, a few applications implemented "windowless controls" to save memory, but this was uncommon, especially in the early days. For example, there was an optional windowless version of the Rich Edit control:
https://learn.microsoft.com/en-us/windows/win32/controls/win...
Fun fact: an early informal name for MDI was "Mac in a box", because if you maximized the top level window, you had a somewhat Mac-like environment, with one menu bar at the top that was shared by the child windows.
Source: I was the author of WinSight and the VBX (Visual Basic eXtension) API.