【问题标题】:Safe place for an ATL object to release itselfATL 对象释放自身的安全位置
【发布时间】:2021-08-19 23:24:56
【问题描述】:

是否有一种策略可以安全地允许 ATL 对象释放自身以响应 Windows 消息或接收器事件?

换句话说,假设您有一个 ATL 类,它继承了某些窗口(使用消息映射)和/或接收来自 COM 对象的事件(使用接收器映射)。并且您希望课程在给定特定消息或事件时释放自己。例如,您可能希望在特定子类窗口收到WM_CLOSE 时释放,或者您正在下沉DWebBrowserEvents2 并希望在DISPID_ONQUIT 时释放。

我认为问题在于,如果您在消息或事件处理程序的中间释放,ATL 框架之后可能仍有一些处理工作要做(即使您,比如说,做类似bHandled = TRUE 的事情)。如果你的对象在那个时候被释放/删除,那么坏事就会接踵而至。

有人知道解决这个问题的方法吗?感谢您的任何意见。

【问题讨论】:

    标签: c++ winapi atl


    【解决方案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 我们释放自我的地方。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-25
      • 1970-01-01
      • 1970-01-01
      • 2016-12-31
      • 1970-01-01
      • 2011-01-30
      • 1970-01-01
      相关资源
      最近更新 更多