使用地图时,WinAPI WndProc会无声地失败

时间:2014-03-02 21:03:02

标签: c++ windows class winapi wndproc

首先,让我说我是使用WinAPI的新手,我正在尝试学习基础知识。也就是说,我正在尝试创建一些对象,以便我将来使用WinAPI更容易。其中一个是Window Class ...类。另一个当然是Window类。

我正在尝试将消息处理变成简单的事情,比如myClassInst.addHandler(WM_PAINT,PaintFunction)。为此,我想我会使用一个将uints映射到函数指针的映射。这看起来很好。

但是现在,邮件没有被处理。经过一些调试后,我发现每当我尝试在我的类的WndProc处理程序中以任何方式使用地图时,它都会默默地失败。我没有编译错误,没有运行时错误,没有崩溃;该函数只是立即结束,直到下一条消息进来。我不能为我的生活找出原因。也许有人可以帮助我?

下面是我的代码片段,全部在我的WinClass类的私有部分。静态dummyProc消息传递是我发现允许特定于类的WndProc的唯一方法,所以就在那里。这是有效的,因为我的WndProc myHandler的第一部分确实可以正常工作,调试消息的输出就是证明。它只是停止第二次尝试并使用地图,甚至只是为了得到它的大小。

*注意:WindowFunction是指向获取HWND参数的函数的指针的typedef。

  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);
  }

编辑因此,经过一些进一步的测试后,我发现了更多信息。上下文值始终为0,虽然我不明白这将如何调用myHandler,但这是一个问题。所以我改变了我的代码,尝试从WM_CREATE消息中设置GWL_USERDATA ......我发现我的dummyProc永远不会收到WM_CREATE消息。

下面是新的dummyProc,带有调试输出:

  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);
  }

它说的是WM_CREATE的值为1,但是消息从36开始,然后是129和130.我的WM_CREATE消息发生了什么?

以下是创建窗口本身的代码:

hand = CreateWindowEx(WS_EX_CLIENTEDGE,                                   myClass.myName,                                   标题,                                   WS_OVERLAPPEDWINDOW,                                   x,y,                                   w,h,                                   父母,                                   空值,                                   研究所,                                   &安培; MyClass的);

这些参数中的大多数是传递给Window类的构造函数的参数(此代码所在的位置)。 myClass参数是一个WinClass实例。所有WinClass实例都将dummyProc设置为其WndProc处理程序。测试显示&amp; myClass在调用它时确实是一个有效的非NULL指针。

那是什么阻止了WM_CREATE消息?

2 个答案:

答案 0 :(得分:2)

您尚未显示窗口创建或指针存储到GWL_USERDATA的位置。但是,无论你如何做到这一点,我都保证在第一条消息到达之前无法设置GWL_USERDATA ,因为第一条消息的到达是第一条消息实际知道你的新窗口分配了HWND的时间

您的窗口过程无法防止context尚未设置,然后尝试在无效指针上调用成员函数,该指针以未定义的方式失败。

除此之外,GWL_USERDATA不足以存储指针的事实;您应该将SetWindowLongPtrGetWindowLongPtrGWLP_USERDATA一起使用。

通常,原始窗口程序首先设置GWLP_USERDATA。看看Raymond Chen's C++ scratch program

请注意,WM_NCCREATE是在原始窗口过程中处理的,因为还没有存储的指向调度的指针(它来自WM_NCCREATE消息参数,而不是来自窗口)。这甚至不是第一条消息(在许多情况下你会先得到WM_GETMINMAXINFO),虽然这是第一条消息,可以在没有线程本地存储等技巧的情况下获得指针。

答案 1 :(得分:2)

正如@Ben Voigt所说,你没有正确地初始化你的GWLP_USERDATA,而且你还没有处理它尚未初始化的情况。

请注意,即使修改后的代码也不正确。在WM_CREATE lParam值为您的用户数据指针。它是指向CREATESTRUCT的指针,其中一个成员是您的用户数据值。

请尝试以下操作来替换dummyProc功能:

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);
  }