在Redrain duilib中,委托模式将事件发送与事件处理进行了解耦,并预定义了六个事件处理函数的原型,具体如下(对应源文件UIDelegate。h):
typedef bool (* FunVoid) (void *, pParam, LPARAM lParam, WPARAM 按钮); typedef bool (* FunTEvent) (TEventUI *, pTEventUI, LPARAM lParam, WPARAM 按钮); typedef bool (* FunTNotify) (TNotifyUI *, pTNotifyUI, LPARAM lParam, WPARAM 按钮); typedef bool (T:: * CMFunVoid) (void *, pParam, P lParam, WPARAM 按钮); typedef bool (T:: * CMFunTEvent) (TEventUI *, pTEventUI, P lParam, WPARAM 按钮); typedef bool (T:: * CMFunTNotify) (TNotifyUI *, pTNotifyUI, P lParam, WPARAM 按钮),
如果利用如下代码给pCtrl控件的OnNotify添加一个委托函数:
pCtrl→OnNotify +=, MakeDelegate(,,, CTestWnd::击打);
其中CTestWnd::击打的定义如下:
bool CTestWnd:击打(void * pParam, LPARAM lParam,, WPARAM 按钮) { ,,,return 真实; }
分析下起处理流程:
1,用户操作导致pCtrl发送某个事件;
2,调用CPaintManagerUI:: MessageHandler;
3,在CPaintManagerUI:: MessageHandler函数内部调用pMsg→pSender→OnNotify (pMsg)。这里的pMsg→pSender是上面所说的pCtrl;
4, OnNotify是pCtrl的一个成员变量,对应的类是CEventSource,该类对()进行了操作符重载,第3步中pMsg→pSender→OnNotify (pMsg),实际调用的是:
bool CEventSource::操作符(),(TNotifyUI *, pTNotifyUI), { (,int 小姐:=,0;,小姐:& lt;, m_aDelegates.GetSize();,我+ +,),{ 时间=CDelegateBase *, pObject m_aDelegates.GetAt(我); 如果,pObject ,,, ! pObject→调用(pTNotifyUI, pObject→GetLParam (), pObject→GetWParam ()),), return 假; } return 真实; }
而不是bool CEventSource::操作符()(void *参数)和bool CEventSource::操作符()(TEventUI * pTEventUI)。因为pMsg的类型是TNotifyUI。
5,第4部中调用调用函数如下:
virtual bool 调用(TNotifyUI *, pTNotifyUI, LPARAM LPARAM =, NULL, WPARAM WPARAM =, NULL) { 啊,,,*,pObject =, (O *), GetObj (); ,,,如果(pObject ,,, GetNotifyTypeName () .IsEmpty ()) ,,,,,,,return (pObject→* m_pCMFunTNotify) (pTNotifyUI, (P) GetLParam (), GetWParam ()); ,,,else 如果(pObject ,,, pTNotifyUI ,,, pTNotifyUI→sType ==, GetNotifyTypeName ()) ,,,,,,,return (pObject→* m_pCMFunTNotify) (pTNotifyUI, (P) GetLParam (), GetWParam ()); ,,,,,,, ,,,return 真实; };
他会调用pObject→* m_pCMFunTNotify。
问题来了,最开始我们调用
pCtrl→OnNotify +=, MakeDelegate(,,, CTestWnd::击打);
MakeDelegate根据传递的参数以及CTestWnd::击打的函数原型,通过构成函数
CDelegate (O *, pObj,, CMFunVoid pCMFunVoid, P lParam =, NULL, WPARAM WPARAM =, NULL) ,:CDelegateBase (pObj, * (FunVoid *), pCMFunVoid, (LPARAM) LPARAM按钮) ,,m_pCMFunVoid (pCMFunVoid) ,,m_pCMFunTEvent(空) ,,m_pCMFunTNotify(空) 以前,{}>得到一个委托对象,并添加到pCtrl→OnNotify中。很显然,构造的这个委托对象的m_pCMFunTNotify为零,而在第第五步中却调用了m_pCMFunTNotify,进而导致崩溃。
从上面的分析看,
typedef bool (* FunVoid) (void *, pParam, LPARAM lParam, WPARAM 按钮); typedef bool (* FunTEvent) (TEventUI *, pTEventUI, LPARAM lParam, WPARAM 按钮); typedef bool (T:: * CMFunVoid) (void *, pParam, P lParam, WPARAM 按钮); typedef bool (T:: * CMFunTEvent) (TEventUI *, pTEventUI, P lParam, WPARAM 按钮),是不能使用的,除非对相关代码进行进一步修改。
另外,对菜单项不要使用委托模式。如果使用了,在菜单项对应的函数中弹出对话框时,会出现异常情况。
Redrain duilib中事件委托存在的问题