这似乎只是一个Windows问题,但我的问题的本质实际上是C ++ 11(或MS C ++ 0x TR1)。它是关于传递std::function
个对象及其生命周期。
我想通过Windows PostMessage
API建立一个通用的异步执行框架。这种情况迫使我退出当前的消息处理并在消息队列中注册任务。
框架工作正常,指向静态函数(在WPARAM
中)和指向上下文的指针(在LPARAM
中),其中包含'this'指针以及其他上下文。
我想将它带到下一级并使用绑定和函数结构。我在Visual Studio 2010上(即std::function
存在,但TR1)。
到目前为止,这就是我所说的:
我正在通过教科书注册WM_ASYNC_TASK
a,我的Windows程序(我实际上正在使用WTL)工作正常。 PostMessage
工作正常,邮件最终会onAsyncTask
和wparam
lparam
正确且符合预期。
const UINT WM_ASYNC_TASK = RegisterWindowMessage(L"Async-Task");
我有CPlugin
,这是我要发送任务的地方。我CHiddenWindow
是获取消息的Window实现。我希望onAsyncTask
将函数转发回CPlugin
。 hwnd
是窗口的句柄。
在CPlugin
我:
void CPlugin::ShowMessageBox( void* arg ) {
wchar_t* text = (wchar_t*)arg;
MessageBox( NULL, text, L"Title", MB_OK )
}
void CPlugin::Sender( ) {
std::function<void(void*)> f = std::bind( &CPlug::ShowMessagebox,
this,
std::placeholders::_1 );
PostMessage( hwnd, WM_ASYNC_TASK, (WPARAM)f, (LPARAM)"Hello!!" );
}
在WM_ASYNC_TASK消息处理程序的CHiddenWindow中我已经:
void CHiddinWindow::onAsyncTask( WPARAM wparam, LPARAM lparam, /* more arguments */ )
{
std::function<void(void*)> f = (std::function<void(void*)>)wparam;
void* arg = lparam;
f( arg );
}
问题:
PostMessage
内WPARAM
基本上很长。)f
的地址(WPARAM)&f
,我很好。&f
时(将f
保留在Sender()
之外 - 不是我的愿望),我在onAsyncHandler
尝试解除引用时遇到访问权限{{1} }。f
以某种方式控制吗?std::shared_ptr
对象的文本部分,但失败的语法。正如我所说,我在变量模板之前使用VS 2010。 Microsoft具有C ++ 0x的TR1实现。我正在寻找一种不包括增强功能的解决方案,因为它现在不适用于我。
谢谢!
答案 0 :(得分:2)
std::function
是非平凡的对象,无法转换为实际为WPARAM
的{{1}}。unsigned int
的续航时间绝对由f
管理。当它与CPlugin
和CPlugin
成员函数绑定时,它与this
生命周期密切相关。CPlugin
在超出f
函数范围时(当它返回时)被销毁。由于异步消息传递,这在Sender()
处理消息之前发生。 CHiddenWindow
指向CHiddenWindow::onAsyncTask
的指针已无效。f
是强制执行共享所有权的好工具(这是您的情况)但您仍无法通过shared_ptr
传递。WPARAM
;?此类框架的可能解决方案:
std::bind(&CPlugin::ShowMessagebox, this, "Hello!")
和CPlugin
可以访问它CHiddenWindow
将任务置于队列中,并将异步消息发布到CPlugin::Sender()
,任务ID为CHiddenWindow
。 WPARAM/LPARAM
还将此任务ID保留在Sender()
实例中 - 如果CPlugin
实例在对等方处理完所有消息之前被销毁,则应将其从队列中删除。CPlugin
获取任务ID,在队列中搜索相应的任务,并在找到任务时运行任务函子。然后它从队列中删除任务。当然,您可以使用一些简单(和错误)的解决方案,例如:
CHiddenWindow::onAsyncTask
但是这个解决方案有两个缺陷:
typedef std::function<void(void*)> Func;
void CPlugin::Sender()
{
Func* f = new Func(std::bind(&CPlugin::ShowMessagebox, this, "Hello!"));
PostMessage(hwnd, WM_ASYNC_TASK, (WPARAM)f, 0);
}
void CHiddenWindow::onAsyncTask(WPARAM wparam, LPARAM lparam)
{
Func* f = (Func*)wparam;
(*f)();
delete f;
}
对象将被泄露,浪费你的记忆。Func
实例在接收者处理邮件之前被销毁,该怎么办?在这种情况下,您将在CPlugin
内调用f()
时遇到未定义的行为(很可能是访问冲突/崩溃)。因此,只有在您有充分保证所有发布的消息都将由接收方处理且发送方在处理完所有发布的消息之前不会被销毁时,您才会考虑此类解决方案。