312 lines
8.7 KiB
C++
312 lines
8.7 KiB
C++
#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;
|
|
}
|
|
}
|