ATL 对象释放自身的安全位置
Safe place for an ATL object to release itself
是否有一种策略可以安全地允许 ATL 对象释放自身以响应 Windows 消息或接收器事件?
换句话说,假设您有一个 ATL class 子classing 某些 windows(使用消息映射)and/or 来自 COM 对象的下沉事件(使用汇图)。您希望 class 在收到特定消息或事件时自行释放。例如,您可能希望在特定子 classed window 收到 WM_CLOSE
时发布,或者您正在下沉 DWebBrowserEvents2
并希望在 DISPID_ONQUIT
时发布.
我认为问题在于,如果您在消息或事件处理程序的中间释放,ATL 框架之后可能仍有一些处理要做(即使您,比如说,做类似 bHandled = TRUE
).如果那时你的对象已经released/deleted,那么坏事就会接踵而至。
有谁知道解决这个问题的方法吗?感谢您的任何输入。
根据 documentation for CWindowImpl::OnFinalMessage:
The default implementation of OnFinalMessage
does nothing, but you can override this function to handle cleanup before destroying a window. If you want to automatically delete your object upon the window destruction, you can call delete this;
in this function.
所以这看起来是个合适的地方。
你可以做下一个实现
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 线程访问,否则需要使用 atomic/interlocked 操作)。当 dwRefCount
变为 0 时删除 Release()
中的对象。当我们对 window 进行子类化时 - 添加附加引用,并在 UnsubclassWindow 中 - 在状态中设置 WINSTATE_DESTROYED
位( m_dwState |= WINSTATE_DESTROYED
) - 结果 OnFinalMessage
将在最后一条消息后被调用,这里我们已经调用了 Release
。或者如果我们在 window 被销毁之前不直接取消子类 - atl 实现无论如何最终调用 OnFinalMessage
我们释放自我的地方。
是否有一种策略可以安全地允许 ATL 对象释放自身以响应 Windows 消息或接收器事件?
换句话说,假设您有一个 ATL class 子classing 某些 windows(使用消息映射)and/or 来自 COM 对象的下沉事件(使用汇图)。您希望 class 在收到特定消息或事件时自行释放。例如,您可能希望在特定子 classed window 收到 WM_CLOSE
时发布,或者您正在下沉 DWebBrowserEvents2
并希望在 DISPID_ONQUIT
时发布.
我认为问题在于,如果您在消息或事件处理程序的中间释放,ATL 框架之后可能仍有一些处理要做(即使您,比如说,做类似 bHandled = TRUE
).如果那时你的对象已经released/deleted,那么坏事就会接踵而至。
有谁知道解决这个问题的方法吗?感谢您的任何输入。
根据 documentation for CWindowImpl::OnFinalMessage:
The default implementation of
OnFinalMessage
does nothing, but you can override this function to handle cleanup before destroying a window. If you want to automatically delete your object upon the window destruction, you can calldelete this;
in this function.
所以这看起来是个合适的地方。
你可以做下一个实现
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 线程访问,否则需要使用 atomic/interlocked 操作)。当 dwRefCount
变为 0 时删除 Release()
中的对象。当我们对 window 进行子类化时 - 添加附加引用,并在 UnsubclassWindow 中 - 在状态中设置 WINSTATE_DESTROYED
位( m_dwState |= WINSTATE_DESTROYED
) - 结果 OnFinalMessage
将在最后一条消息后被调用,这里我们已经调用了 Release
。或者如果我们在 window 被销毁之前不直接取消子类 - atl 实现无论如何最终调用 OnFinalMessage
我们释放自我的地方。