您的位置:首页 > 其它

从ATL窗口销毁想到的对象生命周期管理

2012-04-22 20:35 344 查看
使用ATL窗口时,经常会手动销毁窗口,大致的代码如下:

DestroyWindow();

m_hWnd = NULL;

DestoryWindow()调用是同步的,函数返回时窗口已经被销毁。做为一个微软的好公民,手动将m_hWnd置为空是一个好习惯。

但是,这种做法是不合理的。因为m_hWnd是父类的成员,它的值应该由父类控制。其实,在某些情况下这种做法会产生一个隐蔽的bug。假设这段代码出现在该窗口的消息响应函数中,同时由于某种原因,在退出这个消息响应函数前又再次利用同一个对象创建窗口。那么,等这个消息响应函数返回后,m_hWnd不是保存新窗口的句柄,而是等于0。之所以如此,是因为ATL::CWindowImplBaseT::WndProc在消息响应函数返回之后修改了m_hWnd的值。通过在BEGIN_MSG_MAP之后增加一个自定义的宏就可以解决这个BUG:

#define FIX_NULL_WND_BUG(classname) \

{\

static s_classname##_hwnd_copy = NULL;\

if ( uMsg == WM_CREATE ) \

s_classname##_hwnd_copy = m_hwnd;\

if ( m_hWnd == NULL && s_classname##_hwnd_copy != NULL ) \

m_hWnd = s_classname##_hwnd_copy;\

}

不过,这个办法治标不治本。本质上,破坏窗口对象与窗口句柄的一一映射关系才是导致这个bug的根本原因。用同一个窗口对象管理不同的窗口,往往会因为成员变量的值被无意修改而产生各种bug。避免这种bug的一种方法是,维持窗口对象与窗口句柄的一一映射关系,在需要时动态创建窗口对象和创建窗口;在不需要时,销毁窗口和释放窗口对象。这种做法也有一种潜在的风险,如果在消息响应函数里面销毁自己,会导致函数返回时父类继续访问m_hWnd成员而发生崩溃。解决之道:引入引用计数。在不需要时,只是减少一个计数,而不是销毁。但是,要同时修改ATL的源码,CWindowImplBaseT在调用子类的ProcessWindowsMessage时,要增加一个计数,退出WndProc时要释放一次。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: