Why doesn’t RealGetWindowClass return the real window class for my superclass?


A customer was reporting that the Real­Get­Window­Class function was not reporting the base window class of their superclass. (Error checking has been elided for expository purposes.)

// Get the static window class window procedure
WNDCLASS wc;
GetClassInfo(NULL, TEXT("static"), &wc);
WNDPROC StaticWndProc = wc.lpfnWndProc;

// Build our derived class
wc.lpfnWndProc = AwesomeWndProc;
wc.hInstance = g_hinst;
wc.lpszClassName = TEXT("AwesomeWindow");
RegisterClass(&wc);

LRESULT CALLBACK AwesomeWndProc(HWND hwnd, UINT uMsg,
                                WPARAM wParam, LPARAM lParam)
{
    TCHAR szClass[128];
    RealGetWindowClass(hwnd, szClass, 128);
    ASSERT(strcmp(szClass, TEXT("static")) == 0);

    switch (uMsg) { ... }

    return CallWindowProc(StaticWndProc, hwnd, uMsg, wParam, lParam);
}

The customer found that the assertion fails, returning a window class name of "AwesomeWindow" instead of "static". "I thought the point of RealGetWindowClass was to dig through the superclassing to find the base class. But it's not returning the base class."

That's right, because you haven't told it what the base class is yet!

"What do you mean I haven't told it? It's right there at the end of my function: CallWindowProc(StaticWndProc)."

Yeah, but that line of code hasn't executed yet. The external behavior of your program is like this:

WNDCLASS wc;
wc.style = (something);
wc.lpfnWndProc = AwesomeWndProc;
wc.cbClsExtra = (something);
wc.cbWndExtra = (something);
wc.hInstance = g_hinst;
wc.hIcon = (something);
wc.hCursor = (something);
wc.hbrBackground = (something);
wc.lpszMenuName = (something);
wc.lpszClassName = TEXT("AwesomeWindow");
RegisterClass(&wc);


LRESULT CALLBACK AwesomeWndProc(HWND hwnd, UINT uMsg,
                                WPARAM wParam, LPARAM lParam)
{
    TCHAR szClass[128];
    RealGetWindowClass(hwnd, szClass, 128);
    ASSERT(strcmp(szClass, TEXT("static")) == 0);

    // ... it doesn't matter what goes here
    // because it hasn't executed yet ...

The window manager isn't clairvoyant. It doesn't know that AwesomeWndProc is going to do a CallWindowProc(StaticWndProc) in the future. All it knows is that somebody registered a class, and then in response to its very first message, that class asked, "Hey, you're so smart, tell me what my base class is."

The window manager says, "Dude, you haven't shown me any base class yet. So I'm just going to say that you are your own base class."

Since anything can go into the "... it doesn't matter what goes here ...", we can demonstrate that the window manager cannot possibly know what you're going to pass to CallWindowProc by rewriting it like this:

// Get the static window class window procedure
WNDCLASS wc;
GetClassInfo(NULL, TEXT("static"), &wc);
WNDPROC StaticWndProc = wc.lpfnWndProc;

// Build our class
wc.lpfnWndProc = AwesomeWndProc;
wc.hInstance = g_hinst;
wc.lpszClassName = TEXT("AwesomeWindow");
RegisterClass(&wc);

LRESULT CALLBACK AwesomeWndProc(HWND hwnd, UINT uMsg,
                                WPARAM wParam, LPARAM lParam)
{
    TCHAR szClass[128];
    RealGetWindowClass(hwnd, szClass, 128);
    ASSERT(strcmp(szClass, TEXT("static")) == 0);

    switch (uMsg) { ... }

    // Psych! You thought that when I asked for StaticWndProc
    // I was going to be a superclass of "static", but in fact
    // I'm just a regular boring window class.
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

If you felt really crazy, you could do this:

// Get the button window class procedure
WNDCLASS wcButton;
GetClassInfo(NULL, TEXT("button"), &wcButton);
WNDPROC ButtonWndProc = wcButton.lpfnWndProc;

// Get the static window class window procedure
WNDCLASS wc;
GetClassInfo(NULL, TEXT("static"), &wc);
WNDPROC StaticWndProc = wc.lpfnWndProc;

// Build our class
wc.lpfnWndProc = AwesomeWndProc;
wc.hInstance = g_hinst;
wc.lpszClassName = TEXT("AwesomeWindow");
wc.cbWndExtra = max(wc.cbWndExtra, wcButton.cbWndExtra);
wc.cbClsExtra = max(wc.cbClsExtra, wcButton.cbClsExtra);
RegisterClass(&wc);

LRESULT CALLBACK AwesomeWndProc(HWND hwnd, UINT uMsg,
                                WPARAM wParam, LPARAM lParam)
{
    TCHAR szClass[128];
    RealGetWindowClass(hwnd, szClass, 128);
    ASSERT(strcmp(szClass, TEXT("static")) == 0);

    switch (uMsg) { ... }

    // Decide at the last moment what we are.
    static WNDPROC BaseClass = nullptr;
    if (BaseClass == nullptr)
    {
        BaseClass = rand() % 2 ? StaticWndProc : ButtonWndProc;
        // Or if you are particularly perverse,
        // BaseClass = radioactive_decay_has_occurred() ?
        //                StaticWndProc : ButtonWndProc;
    }
    return CallWindowProc(BaseClass, hwnd, uMsg, wParam, lParam);
}

Since the code to decide the base class hasn't run yet, the window manager will have to use that time machine that the research division has been working on.

Comments (41)
  1. Cesar says:

    An even better example: if it says your base class is "static", do the "boring window class" thing; if it says your base class is not "static", make it be "static". Even with a time machine, you cannot make that work. Related to the liar paradox and in particular the halting problem: if you have a magic machine which reads your code and says "it does X", just call the magic machine and do the opposite of what it said you would do.

  2. Matt says:

    @Cesar: I call this magic function NtTerminateProcess(). It has never yet returned the wrong value.

  3. Joshua says:

    @Cesar: I am sitting on a proof that reads in summary:

    There exists a set of laws of physics that describe a universe in which a machine can be built that can emulate a Turing machine (infinite tape and all) for which a DoesProgramHalt() function for its own programs is "return true;".

    Ergo, something is wrong with Gödel

  4. Matt says:

    @Joshua: There is nothing wrong with Godel, just your understanding of his theory.

    The requirement for Godel is that the system cannot be both CONSISTENT and COMPLETE. That is a function

    BOOL DoesFunctionReturn(FARPROC fp) { return TRUE; } is COMPLETE, but not CONSISTENT.

    BOOL DoesFunctionReturn(FARPROC fp) { NtTerminateProcess(); } is CONSISTENT but not COMPLETE.

    Suppose AwesomeDoesFunctionReturn is both CONSISTENT and COMPLETE. Then I construct the godel function to your system:

    BOOL GodelPwnsYou() { while(AwesomeDoesFunctionReturn(GodelPwnsYou)); }.

    Either AwesomeDoesFunctionReturn is INCOMPLETE (since it refuses to give me an answer) or it is WRONG.

  5. JM says:

    There must be an equivalent to Godwin's law for Gödel: as a discussion on computational impossibilities proceeds, the probability of someone misrepresenting Gödel's incompleteness theorems approaches 1.

    Furthermore, it can be shown that such discussions are incomplete or inconsistent or both.

  6. Joshua says:

    @Matt: Your function returns false. "while(1);" would also have returned false.

  7. Matt says:

    @Joshua –

    If AwesomeDoesFunctionReturn(GodelPwnsYou) returns FALSE, then GodelPwnsYou returns immediately – hence the function is wrong since it should have returned TRUE because the function does return.

    If AwesomeDoesFunctionReturn(GodelPwnsYou) returns TRUE,  then GodelPwnsYou never returns – hence the function is wrong since it should have returned FALSE because the function doesn't return.

    Hence, there is a function (GodelPwnsYou) for which AwesomeDoesFunctionReturn cannot ever return a correct value. That's kind of the whole point of Godel's incompleteness theorem.

  8. JohnQ says:

    I can't really blame the user for being confused.  Your original article on this function said that it returns your class's base class.  But window classes don't really have base classes.  The Window manager infers this relationship from the call to CallWindowProc.  I'm also curious about your last example.  What will RealGetWindowClass return in this case?  Will it return a value based on the first call to CallWindowProc or the most recent one?  (And no, I won't write a test program to find out–I don't care that much.)

  9. Joshua says:

    then GodelPwnsYou never returns

    I don't think you understand. Infinite loops return false.

  10. @Joshua says:

    If AwesomeDoesFunctionReturn(GodelPwnsYou) returns FALSE, then GodelPwnsYou doesn't have an infinite loop and GodelPwnsYou returns immediately (follow the function through in your head)

  11. JEAC says:

    @Joshua

    If AwesomeDoesFunctionReturn(GodelPwnsYou) returns FALSE, then GodelPwnsYou doesn't have an infinite loop and GodelPwnsYou returns immediately (follow the function through in your head)

    In which case, since GodelPwnsYou did manage to return, AwesomeDoesFunctionReturn(GodelPwnsYou) should have returned TRUE not FALSE (it's checking if the function would return).  But if AwesomeDoesFunctionReturn(GodelPwnsYou) returns TRUE, then GodelPwnsYou never returns, so AwesomeDoesFunctionReturn(GodelPwnsYou) should have returned FALSE, in which case…

  12. Infinite loops don't return false.  They just don't return.

    @Matt your quip "I call this magic function NtTerminateProcess(). It has never yet returned the wrong value." was very clever.

  13. Joshua says:

    @JEAC: It is not physically possible to write a loop that does not return on this architecture due to time contraction. Remember it begins "There exists a set of laws of physics…"

  14. JEAC says:

    @Joshua

    There is nothing even in our own laws of physics that guarantees an "end of time" situation.  Going beyond that, "return" is a different concept to "computer dissociates into dust" or "power fails"; in any such situation, the function has not "returned".

    So it is extremely easy to write a loop that does not return.

  15. Joshua says:

    @JEAC: Since when was the current set of laws of physics listed as an assumption in Gödel's work?

  16. AC says:

    @Joshua

    Remember it begins "There exists a set of laws of physics…"

    Your "Turing machine" isn't a real Turing machine, though. As a Turing machine can compute partial functions, the set of functions which can be computed by your machine is a strict subset of Turing-computable functions.

    To put it another way: A requirement for Gödel's theorem that a system cannot be both consistent and complete is that the system has to be of a certain complexity, namely being able to express arithmetic. Likewise, your system is not complex enough to simulate a Turing machine.

  17. Joshua says:

    @AC: My construct is a Turing machine mounted inside a time-compressed region of space and given the ability as a primitive operation to construct more like it.

  18. Adrian says:

    Can someone elaborate on what mechanism RealGetWindowClass uses to determine the base class?

    I've read the MSDN description and Raymond's earlier post about this function, but I still have no idea how it would work.  I could imagine that a special query message is passed to the window, which propagates up through the base classes and that the common Windows controls know to respond with their own types, but that wouldn't explain how a custom window class that merely calls DefWindowProc would be detected.

    [Um, see today's article. -Raymond]
  19. What I learnt today, there is a universe where

    bool fun(){while(1){}return false;}

    actually returns. Reality is vast and scary.

  20. Yuhong Bao says:

    For more technical details.

  21. Arlie says:

    I'm so glad I don't do any Win32 programming any more.  All this window class craziness…  just a thing of the past…

  22. Neil says:

    @Matt I recently discovered a PC on which two competing sets of anti-virus software were installed simultaneously. On such a PC, NtTerminateProcess incorrectly returns ERROR_SUCCESS. I was hoping to get the PC into a state where NtTerminateProcess didn't return but Task Manager wouldn't let me attempt to terminate csrss.exe and now the PC is basically paralysed and will need to be physically restarted.

  23. ulric says:

    i do not understand why that function would be necessary. does some service in windows need this to detect edit controls or something for accessibility?

    [Explained two years ago. -Raymond]
  24. jeronimo says:

    I found all that text to look like garbage

  25. voo says:

    @Joshua Since a Turing Machine has to have an infinite tape by definition, how would that fit together with your definition of the universe? If the "turing machine" is only physically able to read a finite number of tape states I'm pretty sure that violates its definition. If it can read infinitely many states from the tape – how exactly could such a program ever return?

  26. @Joshua says:

    If your laws of physics don't allow for an infinite loop for *whatever* reason, they're not Turing-complete.  Don't tell me that it's an infinite loop for one set of purposes, but not an infinite loop for another.  Either it is or it isn't.  If we're working within this "time compressed field" or whatever it is, fine, GodelPwnsYou does, in fact, pwn you.  If we're working outside this field, it's not Turing complete.  Pick one.

  27. #INF says:

    I find the conclusion of infinity hard to understand. Some mathematicians don't help much either from a tl;dl perspective, other than to describe the idea. video.ias.edu/The-Mathematical-Infinity

  28. getchar says:

    As soon as you depend on user input, it usually undecidable if the function returns.

  29. AndyCadley says:

    @Joshua: Obeying the known laws of physics are pretty much an unspoken assumption in every (non theoretical physics) theory.

    Since there could exist a universe in which the laws of physics were such that the state of every atom at all points in time is entirely deterministic, there therefore exists a possible set of the laws of physics in which the halting problem is fundamentally solvable.

  30. Danny says:

    "Since the code to decide the base class hasn't run yet, the window manager will have to use that time machine that the research division has been working on."</quote>

    Hey Ray, I thought you made it clear, very clear actually – your research division is working on a time machine that would be used to go into past only. Now you tell us they are working on another time machine that can go into future?

    [Their time machine will predict the future. -Raymond]
  31. ErikF says:

    @Danny, would *you* want to be stuck in the 80's? 'Nuff said! ;-)

    Seriously, I had never considered writing a window procedure that could call different base classes, but there are a couple of times that it could be useful (dynamic loading/plug-ins and back compat being two that come to mind right away).

  32. alegr1 says:

    therefore exists a possible set of the laws of physics in which the halting problem is fundamentally solvable

    Halting theorem doesn't rely on the laws of physics.

  33. Joshua says:

    @INF: What's after the infinite loop doesn't execute. The machine's calling convention wraps functions in a time-compressed field and failure to return (which can be done by finite timer on the outside) is translated into return false.

  34. AndyCadley says:

    @Alegr1: Halting theorem doesn't rely on the laws of physics.

    It assumes cause leads to effect, which is a result of the laws of physics. If you can redefine the laws such that cause and effect are unrelated, you can solve the halting problem.

  35. @AndyCadley says:

    No it doesn't. I suggest you read Matt's function above. It doesn't rely on cause and effect – it conditionally relies on your function and chooses its behaviour to be the opposite behaviour that your function predicted – hence forcing your function to be wrong.

    The Halting Theorem is independent of causality, the universe, time-constraints and quantum uncertainty, it merely requires a formal logic that allows recursion.

  36. Matt says:

    @Joshua

    What infinite loop? GodelPwnsYou has a conditional loop. It doesn't have an infinite loop. The conditional loop just happens to be taken every time if your function thinks GodelPwnsYou will terminate you, and is never taken if your function thinks GodelPwnsYou will infinitely loop.

    Hence, whatever your functions is, and whatever it decides about GodelPwnsYous' decidability, I can construct a function where your function is wrong about GodelPwnsYou.

  37. Ben Voigt says:

    @Crescens2k: Not only is there such a scary universe, it is compliant with the C++11 Standard.  Section 1.10p24: "The implementation may assume that any thread will eventually do one of the following:

    —  terminate,

    —  make a call to a library I/O function,

    —  access or modify a volatile object, or

    —  perform a synchronization operation or an atomic operation.

    [ Note:  This is intended to allow compiler transformations such as removal of empty loops, even when termination cannot be proven.  — end note ]"

  38. alegr1 says:

    If you can redefine the laws such that cause and effect are unrelated, you can solve the halting problem.

    I though that cause and effect are related by definition.

  39. Joshua says:

    @alegrl: They are, but cause before effect is not true by definition.

  40. Adrian says:

    @Yuhong Bao:  Thanks for that link.  It answered my questions.

Comments are closed.

Skip to main content