The secret life of GetWindowText

Date:August 21, 2003 / year-entry #29
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20030821-00/?p=42833
Comments:    3
Summary: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. How windows manage their text There are two ways window...

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.

How windows manage their text

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:

  • Default handling of the WM_NCCREATE message takes the lpWindowName parameter passed to CreateWindow/Ex and saves it in a "special place".
  • Default handling of the WM_GETTEXT message retrieves the string from that "special place".
  • Default handling of the WM_SETTEXT message copies the string to that "special place".

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

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.

  • If you are trying to GetWindowText() from a window in your own process, then GetWindowText() will send the WM_GETTEXT message.
  • If you are trying to GetWindowText() from a window in another process, then GetWindowText() will use the string from the "special place" and not send a message.

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."

What if I don't like these rules?

If the second rule bothers you because you need to get text from a custom control in another process, then you can send the WM_GETTEXT message manually. Since you are not using GetWindowText(), you are not subject to its rules.

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.

Can you give an example where this makes a difference?

Consider this control:

SampleWndProc(...)
{
    case WM_GETTEXT:
        lstrcpyn((LPTSTR)lParam, "Booga!", (int)wParam);
        return lstrlen((LPTSTR)lParam);
    case WM_GETTEXTLENGTH: return 7; // lstrlen("Booga!") + null
    ...
}

Now consider application A that does

hwnd = CreateWindow("Sample", "Frappy", ...);

Now consider process B that gets the handle to the window created by application A (by whatever means).

TCHAR szBuf[80];
GetWindowText(hwnd, szBuf, 80);

This will return szBuf = "Frappy" because it is getting the window text from the "special place". However,

SendMessage(hwnd, WM_GETTEXT, 80, (LPARAM)szBuf);

will return szBuf = "Booga!"


Comments (3)
  1. Raymond Chen says:

    Correction: The documentation does give the full story. I should have known, because I was the one who wrote that part of the documentation!

  2. John Schroedl says:

    Thanks for clearing this up! I’ve wondered about it in the past. Now I can update my little focus-spy utility app to try:

    GetWindowText()
    if (text length == 0)
    SendMessageTimeout( … WM_GETTEXT … );

    …just did it and it works beautifully. Thanks for sharing!

  3. Raymond Chen says:

    Commenting on this article has been closed.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index