首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我不能绑定winproc?

为什么我不能绑定winproc?
EN

Stack Overflow用户
提问于 2013-06-30 00:13:30
回答 4查看 1K关注 0票数 3

我正在尝试使用C++11来解决我最喜欢的指针问题

代码语言:javascript
复制
LRESULT CALLBACK renderMan::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
//some code
WNDPROC crazy = bind(&renderMan::WindowProc,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3,std::placeholders::_4);

错误

代码语言:javascript
复制
1>renderman.cpp(50): error C2440: 'initializing' : cannot convert from 'std::_Bind<_Forced,_Ret,_Fun,_V0_t,_V1_t,_V2_t,_V3_t,_V4_t,_V5_t,<unnamed-symbol>>' to 'WNDPROC'
1>          with
1>          [
1>              _Forced=true,
1>              _Ret=LRESULT,
1>              _Fun=std::_Pmf_wrap<LRESULT (__cdecl glSurface::* )(HWND,UINT,WPARAM,LPARAM),LRESULT,glSurface,HWND,UINT,WPARAM,LPARAM,std::_Nil,std::_Nil,std::_Nil>,
1>              _V0_t=glSurface *const ,
1>              _V1_t=std::_Ph<1> &,
1>              _V2_t=std::_Ph<2> &,
1>              _V3_t=std::_Ph<3> &,
1>              _V4_t=std::_Ph<4> &,
1>              _V5_t=std::_Nil,
1>              <unnamed-symbol>=std::_Nil
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-07-14 07:02:27

问题所在

WNDPROC是使用__stdcall调用约定指向具有特定签名的函数的指针。

bind返回一个函数对象,而不是函数指针。尽管在源代码级别上,函数对象可以与函数指针几乎相同地使用,但事实仍然是,它是完全不同的类型。再多的强制转换也解决不了这个问题。

由于您将this指针与renderMan::WindowProc方法绑定在一起,因此显然renderMan::WindowProc不是一个静态成员函数,因此它使用thiscall调用约定。因此,即使你可以获得一个指向它的指针并将它传递给Windows,Windows也不会使用正确的调用约定来调用它。

解决方案

处理此问题的最常见方法是将一个非成员函数(或静态成员函数)注册为WNDPROC。该函数查找与窗口关联的this指针,并将其转发给您的成员函数。

代码语言:javascript
复制
LRESULT CALLBACK WndProcHelper(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
  renderMan *that = LookUpPointer(hwnd);
  assert(that != nullptr);
  return that->WndProc(hwnd, msg, wp, lp);
}

LookUpPointer的细节将根据您的方法而有所不同。听起来你只需要使用一个全局变量就可以解决这个问题,因此只需要一个窗口实例。

如果需要从该窗口类实例化多个窗口,则可以维护一个将HWND映射到renderMan指针的全局(或类静态)表。当您创建一个新实例时,您将其添加到表中,然后您就有了一个简单的LookUpPointer实现:

代码语言:javascript
复制
std::map<HWND, renderMan*> g_windows;

renderMan *LookUpPointer(HWND hwnd) {
  // If there isn't an entry for hwnd in the map, then this will
  // will create one, associating it with nullptr.
  return g_windows[hwnd];
}

然而,这可能会有一些鸡和蛋的问题,因为在CreateWindow调用期间,在你拿回HWND并有机会添加它和指向地图的指针之前,你会收到一些消息。例如:

代码语言:javascript
复制
// ... inside a renderMan constructor ...
m_hwnd = CreateWindow(L"renderMan", /* ... */);
g_windows[m_hwnd] = this;  // already too late for some messages

另一种解决鸡和蛋问题的常用方法是在创建参数中存储renderMan指针,并在创建期间在WndProcHelper中使用特殊逻辑将其添加到映射中。

代码语言:javascript
复制
// ... inside the renderMan constructor ...
m_hwnd = CreateWindow(L"renderMan", /* ... */, reinterpret_cast<LPVOID>(this));

// ... and then ...
LRESULT CALLBACK WndProcHelper(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
  static std::map<HWND, renderMan*> windows;  // still static, but not global :-)
  LRESULT result = 0;
  renderMan *that = nullptr;
  if (msg == WM_NCCREATE) {
    auto pCreateStruct = reinterpret_cast<const CREATESTRUCT*>(lp);
    that = reinterpret_cast<renderMan*>(pCreateStruct->lpCreateParams);
    windows[hwnd] = that;
  } else {
    that = windows[hwnd];
  }
  if (that != nullptr) {
    result = that->WndProc(hwnd, msg, wp, lp);
  }
  if (msg == WM_NCCDESTROY) {
    windows.erase(hwnd);
  }
  return result;
}

注意:这是简单的代码,没有足够的错误检查来说明这个想法。一个干净和完整的实现会更优雅地处理错误,记录意外的事情(比如丢失的条目)等等。

票数 4
EN

Stack Overflow用户

发布于 2017-05-30 19:31:09

带有回调的C API几乎总是为此提供一个“用户数据”字段--将您自己的上下文结构或对象(即' this‘指针)传递给回调。在这种情况下,userdata确实存在,这意味着不需要任何std::map恶作剧,但是它是隐藏的。您正在寻找的API包括:

代码语言:javascript
复制
SetWindowLongPtr( hwnd, GWLP_USERDATA, your_user_data )
your_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA )
票数 1
EN

Stack Overflow用户

发布于 2013-06-30 01:55:24

WNDPROC是一个函数指针,而bind的结果是一个函数对象。正如编译器所说,它不能被转换成WNDPROC。

你可以这样做:

代码语言:javascript
复制
auto crazy = bind(.....)
std::function<LRESULT CALLBACK(HWND, UINT, WPARAM, LPARAM)> crazy = bind(...)

但我想这并不能解决你的问题。我认为没有免费的功能是无法做到这一点的。可能是这样的:

代码语言:javascript
复制
std::function<LRESULT CALLBACK(HWND, UINT, WPARAM, LPARAM)> crazy;

LRESULT CALLBACK myWindowProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
    if(!crazy)
        return (LRESULT)nullptr;

    return crazy(hwnd,Msg,wParam,lParam)
}

//and then somewhere in your renderMan:
crazy = bind(&renderMan::WindowProc,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3,std::placeholders::_4);

// wherever you want:
SetWindowLongA( hwnd, GWL_WNDPROC, ( LONG )myWindowProc );
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17382174

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档