Question

First, let me say I'm new to using WinAPI, and I'm trying to learn the basics. That said, I'm trying to create a few objects to make my future work with WinAPI much easier. One of those is a Window Class...class. The other is, of course, a Window class.

I'm trying to make message processing into something simple, like myClassInst.addHandler(WM_PAINT, PaintFunction). For that, I figured I'd use a map that maps uints to function pointers. That seems fine.

But now, the messages aren't being processed. After some debugging, I found that whenever I try to use the map in any way inside my class's WndProc handler, it just silently fails. I get no compile errors, no runtime errors, no crashes; the function just immediately ends there until the next message comes in. I can't for the life of me figure out why. Maybe someone can help me?

Below is a snippet of my code, all in the private section of my WinClass class. The static dummyProc message-passer is the only way I found to allow a class-specific WndProc, so that's there. That's working, as the first part of my WndProc myHandler does, in fact, work, as evidenced by the debug message's output. It just stops the second I try and use the map, even just to get its size.

*Note: WindowFunction is a typedef to a pointer to a function taking an HWND parameter.

  std::map<unsigned int, WindowFunction> messageMap;

  static LRESULT CALLBACK dummyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    WinClass* context=(WinClass*)GetWindowLong(hwnd, GWL_USERDATA);
    return context->myHandler(hwnd, msg, wParam, lParam);
  }

  LRESULT CALLBACK myHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    std::cout<<"Message: "<<msg<<std::endl; // This gets output
    std::cout<<"  Contains "<<messageMap.size()<<" items"<<std::endl; // This does not
    std::cout<<"  And me!"<<std::endl; // Nothing works below the map usage.

    for (std::map<unsigned int, WindowFunction>::iterator it=messageMap.begin(); it!=messageMap.end(); ++it) {
      std::cout<<"  Have: "<<it->first<<std::endl;
      if (msg==it->first) {
        (it->second)(hwnd);
        break;
      }
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
  }

EDIT So, after some further testing, I've discovered some more information. The context value is always 0, and while I don't understand how that would even allow the myHandler to get called, it is a problem. So I've changed my code to try and set the GWL_USERDATA from the WM_CREATE message...and what I've found is that my dummyProc is never getting the WM_CREATE message.

Below is the new dummyProc, complete with debugging output:

  static LRESULT CALLBACK dummyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    std::cout<<"Create would be "<<WM_CREATE<<std::endl;
    std::cout<<"The message was "<<msg<<std::endl;
    if (msg==WM_CREATE) {
      SetLastError(0);
      SetWindowLong(hwnd, GWL_USERDATA, lParam);
      std::cout<<"ERROR: "<<GetLastError()<<std::endl;
    }
    long thelong=GetWindowLong(hwnd, GWL_USERDATA);
    std::cout<<"Long: "<<thelong<<std::endl;
    WinClass* context=(WinClass*)thelong;
    std::cout<<"Context: "<<context<<std::endl;
    return context->myHandler(hwnd, msg, wParam, lParam);
  }

What it says is that WM_CREATE has a value of 1, but the messages come in starting from 36, then 129 and 130. What happened to my WM_CREATE message?

Here's the code that creates the window itself:

hand = CreateWindowEx(WS_EX_CLIENTEDGE, myClass.myName, title, WS_OVERLAPPEDWINDOW, x, y, w, h, parent, NULL, inst, &myClass);

Most of those parameters are arguments passed to the Window class's constructor (where this code is). The myClass parameter is a WinClass instance. All WinClass instances set dummyProc as their WndProc handler. Testing shows that &myClass is indeed a valid non-NULL pointer when this is called.

So what's preventing the WM_CREATE message?

Was it helpful?

Solution

As @Ben Voigt says, you're not initialising your GWLP_USERDATA correctly, and you're also not handling the case when it hasn't yet been initialized.

Note that even your revised code is not correct. On WM_CREATE the lParam value is not your user data pointer. It's a pointer to a CREATESTRUCT, one of the members of which is your user data value.

Try the following to replace your dummyProc function:

static LRESULT CALLBACK dummyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    if (uMsg == WM_NCCREATE)
        SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>( reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams ));

    WinClass* context=(WinClass*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (!context) return DefWindowProc(hwnd, msg, wParam, lParam);
    return context->myHandler(hwnd, msg, wParam, lParam);
  }

OTHER TIPS

You haven't shown the window creation or where the pointer gets stored into GWL_USERDATA. However, irregardless of how you do that, I promise you that there's no way to set up GWL_USERDATA before the first message arrives, because the arrival of the first message is the first time you actually know what HWND your new window was assigned.

Your window procedure isn't protecting against context not being set yet, and then tries to call a member function on an invalid pointer, which fails in undefined ways.

Add to that the fact that GWL_USERDATA isn't big enough to store a pointer; you should use using SetWindowLongPtr and GetWindowLongPtr with GWLP_USERDATA.

Normally the raw window procedure has a hand in setting GWLP_USERDATA in the first place. Have a look at Raymond Chen's C++ scratch program.

Notice that WM_NCCREATE is handled inside the raw window procedure, because there's no stored pointer to dispatch to yet (it's gotten from the WM_NCCREATE message arguments, not from the window). And that isn't even the first message (you'll get WM_GETMINMAXINFO first in many cases), although it is the first message where the pointer can be obtained without tricks like thread-local storage.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top