Redrain duilib中事件委托存在的问题
在Redrain duilib中,委托模式将事件发送与事件处理进行了解耦,并预定义了六个事件处理函数的原型,具体如下(对应源文件UIDelegate.h):
typedefbool(*FunVoid)(void*pParam,LPARAMlParam,WPARAMwParam);typedefbool(*FunTEvent)(TEventUI*pTEventUI,LPARAMlParam,WPARAMwParam);typedefbool(*FunTNotify)(TNotifyUI*pTNotifyUI,LPARAMlParam,WPARAMwParam);typedefbool(T::*CMFunVoid)(void*pParam,PlParam,WPARAMwParam);typedefbool(T::*CMFunTEvent)(TEventUI*pTEventUI,PlParam,WPARAMwParam);typedefbool(T::*CMFunTNotify)(TNotifyUI*pTNotifyUI,PlParam,WPARAMwParam);
如果利用如下代码给pCtrl控件的OnNotify添加一个委托函数:
pCtrl->OnNotify+=MakeDelegate<CTestWnd,CTestWnd,LPARAM>(this,&CTestWnd::OnTest);
其中CTestWnd::OnTest的定义如下:
boolCTestWnd::OnTest(void*pParam,LPARAMlParam,WPARAMwParam){returntrue;}
分析下起处理流程:
1、用户操作导致pCtrl发送某个事件;
2、调用CPaintManagerUI::MessageHandler;
3、在CPaintManagerUI::MessageHandler函数内部调用pMsg->pSender->OnNotify(pMsg)。这里的pMsg->pSender是上面所说的pCtrl;
4、OnNotify是pCtrl的一个成员变量,对应的类是CEventSource,该类对()进行了操作符重载,第3步中pMsg->pSender->OnNotify(pMsg),实际调用的是:
boolCEventSource::operator()(TNotifyUI*pTNotifyUI){for(inti=0;i<m_aDelegates.GetSize();i++){CDelegateBase*pObject=m_aDelegates.GetAt(i);if(pObject&&!pObject->Invoke(pTNotifyUI,pObject->GetLParam(),pObject->GetWParam()))returnfalse;}returntrue;}
而不是bool CEventSource::operator() (void* param) 和bool CEventSource::operator() (TEventUI* pTEventUI)。因为pMsg的类型是TNotifyUI。
5、第4部中调用Invoke函数如下:
virtualboolInvoke(TNotifyUI*pTNotifyUI,LPARAMlParam=NULL,WPARAMwParam=NULL){O*pObject=(O*)GetObj();if(pObject&&GetNotifyTypeName().IsEmpty())return(pObject->*m_pCMFunTNotify)(pTNotifyUI,(P)GetLParam(),GetWParam());elseif(pObject&&pTNotifyUI&&pTNotifyUI->sType==GetNotifyTypeName())return(pObject->*m_pCMFunTNotify)(pTNotifyUI,(P)GetLParam(),GetWParam());returntrue;};
他会调用pObject->*m_pCMFunTNotify。
问题来了,最开始我们调用
pCtrl->OnNotify+=MakeDelegate<CTestWnd,CTestWnd,LPARAM>(this,&CTestWnd::OnTest);
MakeDelegate根据传递的参数以及CTestWnd::OnTest的函数原型,通过构成函数
CDelegate(O*pObj,CMFunVoidpCMFunVoid,PlParam=NULL,WPARAMwParam=NULL):CDelegateBase(pObj,*(FunVoid*)&pCMFunVoid,(LPARAM)lParam,wParam),m_pCMFunVoid(pCMFunVoid),m_pCMFunTEvent(NULL),m_pCMFunTNotify(NULL){}
得到一个委托对象,并添加到pCtrl->OnNotify中。很显然,构造的这个委托对象的m_pCMFunTNotify为NULL,而在第5步中却调用了m_pCMFunTNotify,进而导致崩溃。
从上面的分析看,
typedefbool(*FunVoid)(void*pParam,LPARAMlParam,WPARAMwParam);typedefbool(*FunTEvent)(TEventUI*pTEventUI,LPARAMlParam,WPARAMwParam);typedefbool(T::*CMFunVoid)(void*pParam,PlParam,WPARAMwParam);typedefbool(T::*CMFunTEvent)(TEventUI*pTEventUI,PlParam,WPARAMwParam);
是不能使用的,除非对相关代码进行进一步修改。
另外,对菜单项不要使用委托模式。如果使用了,在菜单项对应的函数中弹出对话框时,会出现异常情况。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。