How to subclass a window multiple times
Иногда бывает необходимо привязаться к окну и добавить логики по обработке оконных сообщений. Механизм имеет всем известное название сабклассинг. MFC, как и многие библиотеки, поддерживает сабклассинг окон. Проблема лишь в том, что сделать это можно лишь один раз. А если надо много раз? А если к нам попадает хэндл окна и мы понятия не имеем кем оно было создано? Проблемка, однако. Но на помощь как всегда приходит могучий ATL.
#pragma once #include <atlwin.h> class CWindowSubclass { public: CWindowSubclass(CMessageMap *pObject, DWORD msgMapID = 0); CWindowSubclass(HWND hWnd, CMessageMap *pObject, DWORD msgMapID = 0); ~CWindowSubclass(); public: void SwitchMessageMap(DWORD msgMapID); BOOL SubclassWindow(HWND hWnd); HWND UnsubclassWindow(bool force = false); HWND GetHWnd() const; operator bool() const; private: static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); private: HWND m_hWnd; CWndProcThunk m_thunk; WNDPROC m_pfnSuperWindowProc; CMessageMap *m_pObject; DWORD m_msgMapID; };
#include "stdafx.h" #include "WindowSubclass.h" CWindowSubclass::CWindowSubclass(CMessageMap *pObject, DWORD msgMapID/* = 0*/) : m_hWnd(NULL), m_pObject(pObject), m_msgMapID(msgMapID), m_pfnSuperWindowProc(NULL) { } CWindowSubclass::CWindowSubclass(HWND hWnd, CMessageMap *pObject, DWORD msgMapID/* = 0*/) : m_hWnd(NULL), m_pObject(pObject), m_msgMapID(msgMapID), m_pfnSuperWindowProc(NULL) { SubclassWindow(hWnd); } CWindowSubclass::~CWindowSubclass() { if (m_hWnd) { UnsubclassWindow(TRUE); } } HWND CWindowSubclass::GetHWnd() const { return(m_hWnd); } CWindowSubclass::operator bool() const { return m_hWnd != NULL; } void CWindowSubclass::SwitchMessageMap(DWORD msgMapID) { m_msgMapID = msgMapID; } LRESULT CWindowSubclass::DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { #ifdef STRICT return ::CallWindowProc(m_pfnSuperWindowProc, m_hWnd, uMsg, wParam, lParam); #else return ::CallWindowProc(reinterpret_cast<FARPROC>(m_pfnSuperWindowProc), m_hWnd, uMsg, wParam, lParam); #endif } LRESULT CWindowSubclass::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CWindowSubclass * const pThis = reinterpret_cast<CWindowSubclass *>(hWnd); ATLASSERT(pThis); if (!pThis) { return 0; } ATLASSERT(pThis->m_hWnd); ATLASSERT(pThis->m_pObject); if (!pThis->m_hWnd || !pThis->m_pObject) { return 0; } LRESULT lRes = 0; BOOL bRet = pThis->m_pObject->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, pThis->m_msgMapID); if (!bRet) { if (uMsg != WM_NCDESTROY) lRes = pThis->DefWindowProc(uMsg, wParam, lParam); else { const LONG_PTR pfnWndProc = ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC); lRes = pThis->DefWindowProc(uMsg, wParam, lParam); if (pThis->m_pfnSuperWindowProc && ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC) == pfnWndProc) { ::SetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(pThis->m_pfnSuperWindowProc)); } pThis->m_hWnd = NULL; } } return lRes; } BOOL CWindowSubclass::SubclassWindow(HWND hWnd) { ATLASSUME(m_hWnd == NULL); ATLASSERT(::IsWindow(hWnd)); const BOOL result = m_thunk.Init(WindowProc, this); if (result == FALSE) return result; const WNDPROC pProc = m_thunk.GetWNDPROC(); const WNDPROC pfnWndProc = reinterpret_cast<WNDPROC>(::SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(pProc))); if (pfnWndProc == NULL) return FALSE; m_pfnSuperWindowProc = pfnWndProc; m_hWnd = hWnd; return TRUE; } HWND CWindowSubclass::UnsubclassWindow(bool force/* = false*/) { ATLASSUME(m_hWnd != NULL); const WNDPROC pOurProc = m_thunk.GetWNDPROC(); const WNDPROC pActiveProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtr(m_hWnd, GWLP_WNDPROC)); HWND hWnd = NULL; if (force || pOurProc == pActiveProc) { if (!::SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_pfnSuperWindowProc))) return NULL; m_pfnSuperWindowProc = NULL; hWnd = m_hWnd; m_hWnd = NULL; } return hWnd; }
Использовать этот класс проще простого, достаточно передать наследника ATL::CMessageMap
в качестве параметра конструктора вместе с дескриптором окна. Плюс привычная для ATL конструкция:
BEGIN_MSG_MAP(MyCMessageMapDerivedClass) MESSAGE_HANDLER(WM_MESSAGE, OnMessage) END_MSG_MAP()
Класс можно использовать множество раз для одного окна. За это спасибо механизму переходников ATL.
0 коммент.:
Отправить комментарий