#include "stdafx.h" #include "Lava.h" #include "HWndHelp.h" /***************************************************************************\ ***************************************************************************** * * WindowProc thunks provide a mechanism of attaching a new WNDPROC to an * existing HWND. This does not require you to derive from any classes, * does not use any HWND properties, and can be applied multiple times on the * same HWND. * * Taken from ATLWIN.H * ***************************************************************************** \***************************************************************************/ ///////////////////////////////////////////////////////////////////////////// // WindowProc thunks class CWndProcThunk { public: _AtlCreateWndData cd; CStdCallThunk thunk; void Init(WNDPROC proc, void* pThis) { thunk.Init((DWORD_PTR)proc, pThis); } }; #define DUSERUNSUBCLASSMESSAGE "DUserUnSubClassMessage" class WndBridge { // Construction public: WndBridge(); ~WndBridge(); static HRESULT Build(HWND hwnd, ATTACHWNDPROC pfnDelegate, void * pvDelegate, BOOL fAnsi); HRESULT Detach(BOOL fForceCleanup); // Operations public: static LRESULT CALLBACK RawWndProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam); // Data protected: CWndProcThunk m_thunkUs; ATTACHWNDPROC m_pfnDelegate; void * m_pvDelegate; HWND m_hwnd; WNDPROC m_pfnOldWndProc; BOOL m_fAnsi; UINT m_msgUnSubClass; private: ULONG AddRef(); ULONG Release(); LONG m_cRefs; BOOL m_fAttached; }; //------------------------------------------------------------------------------ WndBridge::WndBridge() { m_thunkUs.Init(RawWndProc, this); m_msgUnSubClass = RegisterWindowMessage(DUSERUNSUBCLASSMESSAGE); m_cRefs = 0; m_fAttached = TRUE; m_pvDelegate = m_pfnDelegate = NULL; } //------------------------------------------------------------------------------ WndBridge::~WndBridge() { AssertMsg(!m_fAttached, "WndBridge still attached at destruction!"); } //------------------------------------------------------------------------------ HRESULT WndBridge::Build(HWND hwnd, ATTACHWNDPROC pfnDelegate, void * pvDelegate, BOOL fAnsi) { WndBridge * pBridge = ProcessNew(WndBridge); if (pBridge == NULL) { return E_OUTOFMEMORY; } else if (pBridge->m_msgUnSubClass == 0) { return HRESULT_FROM_WIN32(GetLastError()); } pBridge->m_pvDelegate = pvDelegate; pBridge->m_pfnDelegate = pfnDelegate; pBridge->m_hwnd = hwnd; pBridge->m_fAnsi = fAnsi; WNDPROC pProc = (WNDPROC)(pBridge->m_thunkUs.thunk.pThunk); WNDPROC pfnOldWndProc = NULL; if (fAnsi) { pfnOldWndProc = (WNDPROC)::SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LPARAM)pProc); } else { pfnOldWndProc = (WNDPROC)::SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LPARAM)pProc); } if (pfnOldWndProc == NULL) { // // Didn't have a previous WNDPROC, so the call to SWLP failed. // ProcessDelete(WndBridge, pBridge); return E_OUTOFMEMORY; } pBridge->m_pfnOldWndProc = pfnOldWndProc; // // Once successfully created, the reference count starts at 1. // pBridge->m_cRefs = 1; return S_OK; } //------------------------------------------------------------------------------ LRESULT WndBridge::RawWndProc(HWND hwndThis, UINT nMsg, WPARAM wParam, LPARAM lParam) { WndBridge * pThis = (WndBridge *) hwndThis; // // Addref the WndBridge object so that we keep it around while we are // processing this message. // pThis->AddRef(); // // Cache these values because we may delete our WndBridge object during // the processing of certain messages. // HWND hwnd = pThis->m_hwnd; WNDPROC pfnOldWndProc = pThis->m_pfnOldWndProc; BOOL fAnsi = pThis->m_fAnsi; LRESULT lRet = 0; BOOL fHandled = FALSE; if (nMsg == pThis->m_msgUnSubClass) { // // We received our special message to detach. Make sure it is intended // for us (by matching proc and additional param). // if (wParam == (WPARAM)pThis->m_pfnDelegate && lParam == (LPARAM)pThis->m_pvDelegate) { lRet = (S_OK == pThis->Detach(FALSE)) ? TRUE : FALSE; fHandled = TRUE; } } else { // // Pass this message to our delegate function. // if (pThis->m_pfnDelegate != NULL) { fHandled = pThis->m_pfnDelegate(pThis->m_pvDelegate, hwnd, nMsg, wParam, lParam, &lRet); } // // Handle WM_NCDESTROY explicitly to forcibly clean up. // if (nMsg == WM_NCDESTROY) { // // The fact that we received this message means that we are still // in the call chain. This is our last chance to clean up, and // no other message should be received by this window proc again. // It is OK to force a cleanup now. // pThis->Detach(TRUE); // // Always pass the WM_NCDESTROY message down the chain! // fHandled = FALSE; } } // // If our delegate function didn't handle this message, pass it on down the chain. // if (!fHandled) { if (fAnsi) { lRet = CallWindowProcA(pfnOldWndProc, hwnd, nMsg, wParam, lParam); } else { lRet = CallWindowProcW(pfnOldWndProc, hwnd, nMsg, wParam, lParam); } } // // Release our reference. The WndBridge object may evaporate after this. // pThis->Release(); return lRet; } //------------------------------------------------------------------------------ // S_OK -> not attached // S_FALSE -> still attached HRESULT WndBridge::Detach(BOOL fForceCleanup) { HRESULT hr = S_FALSE; BOOL fCleanup = fForceCleanup; // // If we have already detached, return immediately. // if (!m_fAttached) { return S_OK; } // // When we detach, we simply break our connection to the delegate proc. // m_pfnDelegate = NULL; m_pvDelegate = NULL; if (!fForceCleanup) { // // Get the pointers to our thunk proc and the current window proc. // WNDPROC pfnThunk = (WNDPROC)m_thunkUs.thunk.pThunk; WNDPROC pfnWndProc = NULL; if (m_fAnsi) { pfnWndProc = (WNDPROC)::GetWindowLongPtrA(m_hwnd, GWLP_WNDPROC); } else { pfnWndProc = (WNDPROC)::GetWindowLongPtrW(m_hwnd, GWLP_WNDPROC); } AssertMsg(pfnWndProc != NULL, "Must always have a window proc!"); // // If the current window proc is our own thunk proc, then we can // clean up more completely. // fCleanup = (pfnWndProc == pfnThunk); } if (fCleanup) { if (m_fAnsi) { ::SetWindowLongPtrA(m_hwnd, GWLP_WNDPROC, (LPARAM)m_pfnOldWndProc); } else { ::SetWindowLongPtrW(m_hwnd, GWLP_WNDPROC, (LPARAM)m_pfnOldWndProc); } m_fAttached = FALSE; Release(); hr = S_OK; } return hr; } //------------------------------------------------------------------------------ ULONG WndBridge::AddRef() { return InterlockedIncrement(&m_cRefs); } //------------------------------------------------------------------------------ ULONG WndBridge::Release() { ULONG cRefs = InterlockedDecrement(&m_cRefs); if (cRefs == 0) { ProcessDelete(WndBridge, this); } return cRefs; } //------------------------------------------------------------------------------ HRESULT GdAttachWndProc(HWND hwnd, ATTACHWNDPROC pfnDelegate, void * pvDelegate, BOOL fAnsi) { return WndBridge::Build(hwnd, pfnDelegate, pvDelegate, fAnsi); } //------------------------------------------------------------------------------ HRESULT GdDetachWndProc(HWND hwnd, ATTACHWNDPROC pfnDelegate, void * pvDelegate) { UINT msgUnSubClass = RegisterWindowMessage(DUSERUNSUBCLASSMESSAGE); if (msgUnSubClass == 0) { return HRESULT_FROM_WIN32(GetLastError()); } if (SendMessage(hwnd, msgUnSubClass, (WPARAM) pfnDelegate, (LPARAM) pvDelegate)) { return S_OK; } else { PromptInvalid("Unable to find subclass."); return E_FAIL; } }