Holy cow, I wrote a book!
GetWindowText() is more complicated than you think. The documentation tries to explain
its complexity with small words, which is great if you don't understand long words,
but it also means that you're not getting the full story.
Here's an attempt to give the full story.
There are two ways window classes can manage their text. They can either do it manually
or they can let the system do it. The default is to let the system do it.
If a window class lets the system manage its text, the system will do the following:
On the other hand, if a window class manages its window text manually, the system
will not do any special handling, and it is the window class's responsibility to respond
to the WM_GETTEXT/WM_SETTEXT messages and return/save the strings explicitly.
Frame windows typically let the system manage their window text. Custom controls typically
manage their window text manually.
GetWindowText has a problem: Window text needs to be readily available without hanging.
FindWindow() needs to get window text in order to find a window. Task-switching applications
need to get window text so they can display the window title in the switcher window.
It should not be possible for a hung application to clog up other applications. This
is particularly true of the task switcher scenario.
This argues against sending WM_GETTEXT messages, because the target
window of the WM_GETTEXT might be hung. Instead, GetWindowText should use the "special
place" since that cannot be affected by hung applications.
On the other hand, GetWindowText is used to retrieve text from controls on a dialog,
and those controls frequently employ custom text management. This argues for sending
WM_GETTEXT messages, because that is the only way to retrieve custom-managed text.
So GetWindowText strikes a compromise.
According to the first rule, if you are trying to get text from a window in your own
process, and the window is hung, then GetWindowText() will also hang. But since the
window belongs to your process, it's your own fault and you deserve to lose. Sending
the WM_GETTEXT message ensures that text from windows that do custom text management
(typically, custom controls) are properly retrieved.
According to the second rule, if you are trying to get text from a window in another
process, then GetWindowText() will not send a message; it will just retrieve the string
from the "special place". Since the most common reason for getting text from a window
in another process is to get the title of the frame, and since frame windows typically
do not do custom window text manipulation, this usually gets the right string.
The documentation simplifies this as "GetWindowText() cannot retrieve text from a
window from another application."
Note, however, that if the target window is hung, your application will also hang
since SendMessage() will not return until the target window responds.
Note also that since WM_GETTEXT is in the system message range (0 to WM_USER-1), you
do not need to do any parameter marshalling (and in fact, you shouldn't). USER will
do the marshalling for you.
lstrcpyn((LPTSTR)lParam, "Booga!", (int)wParam);
case WM_GETTEXTLENGTH: return 7; // lstrlen("Booga!") + null
hwnd = CreateWindow("Sample", "Frappy", ...);
GetWindowText(hwnd, szBuf, 80);
SendMessage(hwnd, WM_GETTEXT, 80, (LPARAM)szBuf);
At the Windows 95 Launch and at various other marketing events, guests were given
a copy of "Windows 95 Special Edition". What is so special about the box?
Answer: The box.
The contents of the box are exactly the same as a regular copy of Windows 95. The
only thing special about it is the box itself.
Of course there is.
"Bear" is the name of the Windows 3.1 mascot, a stuffed teddy bear seemingly-obsessively
carried around by Dave, one of the most senior programmers on the team. If he came
into your office, he might bounce Bear on your monitor to get your attention. As a
prank, we would sometimes steal Bear and take him on "vacation", in the same way people
take garden gnomes on vacation and send back postcards.
If you play the Windows 3.1 easter egg, one of the pictures you will see is a cartoon
Bear took a lot of abuse. He once had the power cord to a Tempest video game run through
his head between his ears. Neil Konzen tried to stick a firecracker up Bear's butt.
(Presumably not while it had the power cord in its head.)
By Windows 95, Bear was in pretty bad repair. (The children of one of the program
managers once took pity on Bear and did a very nice job of of getting Bear back in
So Bear was retired from service and replaced with a pink bunny rabbit, named Bunny.
We actually had two of them, a small one called "16-bit Bunny" and a big one called
"32-bit Bunny". Two bunnies means twice as many opportunities for theft, of course,
and the two bunnies had their own escapades during the Windows 95 project. (When Dave
got married, we helped 32-bit Bunny crash the party and sent back pictures of Bunny
drunk on wine.)
Dave was primarily responsible for the GUI side of things, so you'll see the BEAR
and BUNNY functions in the DLLs responsible for the GUI. On the kernel side, Mike
had a Piglet plush toy (from Winnie the Pooh). So when we needed to name an internal
kernel function, we chose PIGLET. Piglet survived the Windows 95 project without a
I got up at 5 this morning to spend the day at Product Support Services answering
phones: today was the day the Blaster
worm launched its second wave. And by a startling coincidence, the person at the
station next to me was Michael
Howard our Senior Security Program Manager and author of Writing
Secure Code. Getting Michael Howard to help you secure your computer is like
getting Lance Armstrong to help you change a flat tire on your bicycle.
As enlightening yet humbling experiences go, for a software designer, it's hard to
top (1) watching a usability session, and (2) answering product support
calls. You get to observe users -- customers, the people your job it is to make more
productive -- struggle with the software you helped create.
Usability sessions are particularly frustrating since you are hidden behind a one-way
mirror, watching somebody struggle to accomplish something you designed to be the
most obvious thing on the planet. It's a hard lesson to learn: Not everybody is a
geek like you. (Watching a usability session is a lot like being a member of the studio
audience at The Price Is Right trying to help the contestant on stage guess the price
of a new car.)
Windows 95 will fail to boot if you have more than around 480MB of memory. (This was
considered an insane amount of memory back then. Remember, Windows 95's target machine
was a 4MB 386SX and a powerful machine had 16MB. So according to Moore's law, that
gave us seven years before we had to do something about it. One of my friends got
96MB of memory on his machine to test that we didn't tank under "insanely huge memory
configurations" and we all drooled.)
Windows 98 bumped the limit to 1GB because there existed a vendor (who shall remain
nameless) who was insane enough to want to sell machines with 1GB of RAM and preinstall
Windows 98 instead of the much more suitable Windows NT.
One of the first things that happens in the Windows 95 boot process once you have
transitioned into 32-bit mode is to initialize the 32-bit memory manager. But now
you have a chicken-and-egg problem: The memory manager needs to allocate some memory
in order to keep track of the memory it is managing. (Keeping track of which pages
are paged in and which are paged out, that sort of thing.) But it can't allocate memory
until the memory manager is initialized. Eek!
The solution is to initialize the memory manager twice.
The first time the memory manager is initialized, it gets all its memory from a fixed
block of memory preallocated in the init-data segment. It sets up this fixed block
as the memory manager heap. So now there is a heap available to satisfy memory allocations.
Next, the memory manager starts looking for the real memory in the system,
and when it finds some, it allocates memory (from the initial fixed block) to keep
track of the real memory.
After the memory manager has found all the real memory in the system, it's time to
initialize the memory manager a second time: It carves out a chunk of that real memory
to use as the "real heap" and copies the information from the heap that it has been
using so far (the fixed-sized heap) to the "real heap".
Once everything has been copied and all the pointers fixed up, the global memory manager
heap pointers are changed to point at the new ("real") heap and the original heap
The memory consumed by the original heap is reclaimed when the init-data segment is
discarded (which happens at the end of system initialization).
The total RAM limitation occurs because the size of the fixed block in the init-data
segment needs to be large enough to satisfy all the memory allocations performed during
the memory scan. If you have too much memory, an allocation during the memory scan
fails and the system halts.
The size of the init-data segment was chosen to balance two factors. The larger you
make it, the more memory you can have in the system before hitting an allocation failure
during the memory scan. But you can't make it too large or machines with small amounts
of memory won't even be able to load VMM into memory.
Enforcing integrality solves the fractional-line problem, but notice that when you
grab the top or bottom edge and resize the window, the resize feedback doesn't match
the actual window you get back if you drag the window to a non-integral size. (You
may need to turn off full window drag to see this effect more clearly.)
The WM_SIZING message lets us adjust the feedback during window resizing. We
will adjust the rectangle to match the rectangle that will result when you let go
of the mouse.
void OnSizing(HWND hwnd, WPARAM wmsz, LPRECT prc)
AdjustSizeRectangle(hwnd, wmsz, prc);
/* Add to WndProc */
case WM_SIZING: OnSizing(hwnd, wParam, (LPRECT)lParam); return TRUE;
Observe that now, when you resize the window, the resizing feedback accurately represents
the resulting size of the window. As you drag the mouse vertically or horizontally,
the rectangle skips in integral units.
Exercise: If we are enforcing integrality during resizing, why do we also need to
enforce integrality in WM_WINDOWPOSCHANGING, too?
"Why can't I remove 'for test/evaluation purposes only'? I know I'm running an evaluation
edition, I don't need it rubbed in my face."
This prevents unscrupulous OEMs from selling machines with the evaluation edition
of the product instead of the retail version. (Yes, this has happened before. Many
The "For test purposes only" tag prevents unscrupulous OEMs from selling machines
with uncertified drivers. (Driver-cheating has been a big problem, especially video
drivers, since... well... forever. Those of you who are gamers know all about driver
cheating.) To install an uncertified driver without a warning prompt, you need to
install the test root certificate. The presence of the test root certificate causes
the "For test purposes only" tag to appear.
We have also had many cases of corporate customers (and technology reporters!) who
have had machines expire because they had forgotten that they were running the evaluation
edition. When the machines expire on them, they are stuck with thousands
of machines that don't work. This makes them extremely upset.