ATL 对象释放自身的安全位置
Posted
技术标签:
【中文标题】ATL 对象释放自身的安全位置【英文标题】:Safe place for an ATL object to release itself 【发布时间】:2021-08-19 23:24:56 【问题描述】:是否有一种策略可以安全地允许 ATL 对象释放自身以响应 Windows 消息或接收器事件?
换句话说,假设您有一个 ATL 类,它继承了某些窗口(使用消息映射)和/或接收来自 COM 对象的事件(使用接收器映射)。并且您希望课程在给定特定消息或事件时释放自己。例如,您可能希望在特定子类窗口收到WM_CLOSE
时释放,或者您正在下沉DWebBrowserEvents2
并希望在DISPID_ONQUIT
时释放。
我认为问题在于,如果您在消息或事件处理程序的中间释放,ATL 框架之后可能仍有一些处理工作要做(即使您,比如说,做类似bHandled = TRUE
的事情)。如果你的对象在那个时候被释放/删除,那么坏事就会接踵而至。
有人知道解决这个问题的方法吗?感谢您的任何意见。
【问题讨论】:
【参考方案1】:根据documentation for CWindowImpl::OnFinalMessage:
OnFinalMessage
的默认实现不执行任何操作,但您可以覆盖此函数以在销毁窗口之前进行清理。如果你想在窗口销毁时自动删除你的对象,你可以在这个函数中调用delete this;
。
所以看起来就是这样做的地方。
【讨论】:
非常感谢,但我认为这并不能解决问题。因为我主要关心子类窗口,而不是我自己的,以及接收事件。如果我自己的窗口被破坏,OnFinalMessage
就会被触发。
@user15025873 - 成功调用UnsubclassWindow
后,您需要通过m_dwState |= WINSTATE_DESTROYED;
将自己的窗口标记为已销毁,结果OnFinalMessage
将在您的类对象上调用【参考方案2】:
你可以做下一个实现
class MySubClass : public CWindowImplBaseT<>
ULONG dwRefCount = 1;
BEGIN_MSG_MAP(MySubClass)
MESSAGE_HANDLER(WM_CHAR, OnChar)
END_MSG_MAP()
LRESULT OnChar(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
bHandled = FALSE;
// for instance unsublass when ESC pressed
// but possible do this on any event
if (uMsg == WM_CHAR && wParam == VK_ESCAPE)
UnsubclassWindow(FALSE);
return 0;
virtual void OnFinalMessage(_In_ HWND hWnd)
__super::OnFinalMessage(hWnd);
Release();
~MySubClass()
DbgPrint("%s<%p>\n", __FUNCTION__, this);
public:
BOOL SubclassWindow(_In_ HWND hWnd)
if (__super::SubclassWindow(hWnd))
AddRef();
return TRUE;
return FALSE;
HWND UnsubclassWindow(_In_ BOOL bForce /*= FALSE*/)
if (HWND hwnd = __super::UnsubclassWindow(bForce))
// mark window as destroyed
m_dwState |= WINSTATE_DESTROYED;
m_hWnd = hwnd;
return hwnd;
return 0;
void AddRef()
dwRefCount++;
void Release()
if (!--dwRefCount)
delete this;
;
void DoSubclass(HWND hwnd)
if (MySubClass* pObj = new MySubClass)
pObj->SubclassWindow(hwnd);
pObj->Release();
所以关键点 - 对对象有引用计数(ULONG dwRefCount
- 这里我假设对象只能从单个 UI 线程访问,否则需要使用原子/互锁操作)。当dwRefCount
变为0 时删除Release()
中的对象。当我们对窗口进行子类化时 - 添加额外的引用,并在 UnsubclassWindow - 将WINSTATE_DESTROYED
设置为状态位(m_dwState |= WINSTATE_DESTROYED
) - 结果OnFinalMessage
将在最后一条消息之后被调用在这里我们已经调用了Release
。或者如果我们在窗口被销毁之前不直接取消子类 - 无论如何 atl 实现最终都会调用 OnFinalMessage
我们释放自我的地方。
【讨论】:
以上是关于ATL 对象释放自身的安全位置的主要内容,如果未能解决你的问题,请参考以下文章