从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时要释放一次。
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时要释放一次。
相关文章推荐
- [原创]我所理解的Remoting (2) :远程对象的生命周期管理-Part II
- ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理
- spring生命周期管理-初始化与销毁
- 我所理解的Remoting (2) :远程对象的生命周期管理[下篇]
- Destroying Window Objects(销毁窗口对象)
- 自定义Unity对象生命周期管理集成ADO.NET Entity Framework
- 自定义Unity对象生命周期管理集成ADO.NET Entity Framework
- ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理
- 第四章 Spring.Net 如何管理您的类___对象的生命周期链
- 自定义Unity对象生命周期管理集成ADO.NET Entity Framework
- 第十一章 管理类型(In .net4.5) 之 管理对象的生命周期
- C#基础知识回顾:1.由WeakReference想到对象的创建与销毁
- C++线程安全的对象生命周期管理
- 线程安全的对象生命周期管理
- netty的对象的生命周期管理——引用计数
- MFC DestroyWindow窗口对象和窗口句柄的销毁
- 使用Java Annotations来管理对象的生命周期
- Technical Notes 17 : Destroying Window Objects(销毁窗口对象)
- Container:容器,对象生命周期管理的基石——学习总结篇
- LINQ那些事儿(6)-对象生命周期管理