温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Redrain duilib中事件委托存在的问题

发布时间:2020-07-29 15:57:15 来源:网络 阅读:1001 作者:watertoeast 栏目:开发技术

在Redrain duilib中,委托模式将事件发送与事件处理进行了解耦,并预定义了六个事件处理函数的原型,具体如下(对应源文件UIDelegate.h):

typedef bool (*FunVoid)(void* pParam,LPARAM lParam,WPARAM wParam);
typedef bool (*FunTEvent)(TEventUI* pTEventUI,LPARAM lParam,WPARAM wParam);
typedef bool (*FunTNotify)(TNotifyUI* pTNotifyUI,LPARAM lParam,WPARAM wParam);
typedef bool (T::*CMFunVoid)(void* pParam,P lParam,WPARAM wParam);
typedef bool (T::*CMFunTEvent)(TEventUI* pTEventUI,P lParam,WPARAM wParam);
typedef bool (T::*CMFunTNotify)(TNotifyUI* pTNotifyUI,P lParam,WPARAM wParam);


如果利用如下代码给pCtrl控件的OnNotify添加一个委托函数:

pCtrl->OnNotify += MakeDelegate<CTestWnd, CTestWnd, LPARAM>(this, &CTestWnd::OnTest);

其中CTestWnd::OnTest的定义如下:

bool CTestWnd::OnTest(void *pParam, LPARAM lParam, WPARAM wParam)
{
    return true;
}


分析下起处理流程:

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::operator() (TNotifyUI* pTNotifyUI) 
	{
		for( int i = 0; i < m_aDelegates.GetSize(); i++ ) {
			CDelegateBase* pObject = m_aDelegates.GetAt(i);

			if( pObject && !pObject->Invoke(pTNotifyUI,pObject->GetLParam(),pObject->GetWParam()) ) return false;
		}
		return true;
	}

而不是bool CEventSource::operator() (void* param) 和bool CEventSource::operator() (TEventUI* pTEventUI)。因为pMsg的类型是TNotifyUI。

5、第4部中调用Invoke函数如下:

virtual bool Invoke(TNotifyUI* pTNotifyUI,LPARAM lParam = NULL,WPARAM wParam = NULL)
{
    O* pObject = (O*) GetObj();
    if(pObject && GetNotifyTypeName().IsEmpty())
        return (pObject->*m_pCMFunTNotify)(pTNotifyUI,(P)GetLParam(),GetWParam());
    else if(pObject && pTNotifyUI && pTNotifyUI->sType == GetNotifyTypeName())
        return (pObject->*m_pCMFunTNotify)(pTNotifyUI,(P)GetLParam(),GetWParam());
        
    return true;
};

他会调用pObject->*m_pCMFunTNotify。


问题来了,最开始我们调用

pCtrl->OnNotify += MakeDelegate<CTestWnd, CTestWnd, LPARAM>(this, &CTestWnd::OnTest);

MakeDelegate根据传递的参数以及CTestWnd::OnTest的函数原型,通过构成函数

CDelegate(O* pObj, CMFunVoid pCMFunVoid,P lParam = NULL,WPARAM wParam = 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,进而导致崩溃。


从上面的分析看,

typedef bool (*FunVoid)(void* pParam,LPARAM lParam,WPARAM wParam);
typedef bool (*FunTEvent)(TEventUI* pTEventUI,LPARAM lParam,WPARAM wParam);
typedef bool (T::*CMFunVoid)(void* pParam,P lParam,WPARAM wParam);
typedef bool (T::*CMFunTEvent)(TEventUI* pTEventUI,P lParam,WPARAM wParam);

是不能使用的,除非对相关代码进行进一步修改。


另外,对菜单项不要使用委托模式。如果使用了,在菜单项对应的函数中弹出对话框时,会出现异常情况。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI