hookwnd.cpp
来自「一个FTP下载的源代码。代码质量非常高」· C++ 代码 · 共 444 行
CPP
444 行
/*
Module : HOOKWND.CPP
Purpose: Defines the implementation for an MFC class to implement message hooking before CWnd gets there
Created: PJN / 24-02-1999
History: PJN / 21-03-2003 1. Significant rework following comprehensive testing with the Tray icon class.
2. Fixed reported resource leaks caused by SetProp thanks to BoundsChecker
PJN / 29-03-2003 1. A number of functions now use return values instead of using VERIFY internally
and not returning an error code.
2. Made the Hook and UnHook methods virtual
3. HWND -> CWnd mapping which uses the SetProp method now uses ATOMs. This
makes the lookup much faster.
4. Made additional public methods of the class thread safe.
5. Fixed a bug in SizeOfHookChain, FirstInChain, LastInChain and MiddleOfChain
which would unnecessarily ASSERT if called for a non hooked window
6. Made destructor of the class virtual
7. Fixed another SetProp resource leak in the "Remove" method
8. Addition of OnHook and OnUnHook virtual functions which are called when a
hook is being installed or removed.
PJN / 31-03-2003 1. Now includes the concept of auto deletion. This allows heap allocated
CHookWnds to be destroyed when the associated window is destroyed. Again thanks to
Martin Richter for this nice addition.
2. Reworked the way the code handles calling the WNDPROC hook. The code now
instead the ATL mechanism of installing an assembly language thunk. This results
in much less code required to implement the hook. From 435 lines to c. 300!
3. The code also now uses the variable and method naming convention as in the
ATL implementation
PJN / 02-04-2003 1. First hook is now stored in a Windows property. This avoids potential problems
identifying the first hook in cases where the window is already subclassed by a
thunk such as ATL. Thanks to Martin Richter for spotting this issue.
2. Now displays a warning if code is compiled on a non X86 compiler
3. Various code tidy up's following testing with trayicon class.
4. Fixed a bug where only 2 hooks in the chain of hooks being called.
5. Went back to the Hook / Unhook naming convention
6. Addition of various public helper methods
PJN / 17-04-2003 1. Fixed a level 4 unreferenced warning in CHookWnd::WindowProc. Thanks to
Frank Fesevur for reporting this.
Copyright (c) 1999 - 2003 by PJ Naughter. (Web: www.naughter.com, Email: pjna@naughter.com)
All rights reserved.
Copyright / Usage Details:
You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
when your product is released in binary form. You are allowed to modify the source code in any way you want
except you cannot modify the copyright details at the top of each module. If you want to distribute source
code with your application, then you are only allowed to distribute versions released by the author. This is
to maintain a single distribution point for the source code.
*/
///////////////////////////////// Includes //////////////////////////////////
#include "stdafx.h"
#include "HookWnd.h"
//////////////////////////////// Statics / Macros /////////////////////////////
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif
CCriticalSection CHookWnd::sm_cs;
LPCTSTR CHookWnd::sm_pszFirstHookProp = _T("FirstHookWnd");
////////////////////////////////// Implementation /////////////////////////////
IMPLEMENT_DYNAMIC(CHookWnd, CObject)
CHookWnd::CHookWnd(BOOL bAutoDelete) : m_pfnSuperWindowProc(NULL), m_bAutoDelete(bAutoDelete), m_hWnd(NULL), m_pNextHook(NULL)
{
}
CHookWnd::~CHookWnd()
{
}
BOOL CHookWnd::Hook(CWnd* pWnd)
{
ASSERT(pWnd);
return Hook(pWnd->m_hWnd);
}
BOOL CHookWnd::Hook(HWND hWnd)
{
//Validate our parameters
ASSERT(m_hWnd == NULL);
ASSERT(IsWindow(hWnd));
//Find the first hook if any which is already installed
CHookWnd* pFirstHook = FirstHook(hWnd);
//Use a Crit section to make code thread-safe
CSingleLock sl(&sm_cs, TRUE);
//Install the thunk
m_thunk.Init(WindowProc, this);
WNDPROC pProc = (WNDPROC)&(m_thunk.thunk);
WNDPROC pfnWndProc = (WNDPROC) SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);
if (pfnWndProc == NULL)
return FALSE;
if (pFirstHook)
{
m_pfnSuperWindowProc = pFirstHook->m_pfnSuperWindowProc;
m_pNextHook = pFirstHook;
}
else
m_pfnSuperWindowProc = pfnWndProc;
m_hWnd = hWnd;
//Store the first hook i.e. "this" in a windows property
if (pFirstHook)
RemoveProp(m_hWnd, sm_pszFirstHookProp);
SetProp(m_hWnd, sm_pszFirstHookProp, (HANDLE) this);
//Call the virtual function to indicate that the hook has been installed
OnSubclass(hWnd);
return TRUE;
}
HWND CHookWnd::Unhook()
{
//Validate our parameters
HWND hWnd = NULL;
if (m_hWnd)
{
//Find the first hook if any which is already installed
CHookWnd* pFirstHook = FirstHook(m_hWnd);
//Use a Crit section to make code thread-safe
CSingleLock sl(&sm_cs, TRUE);
//We are the first item in the chain
if (pFirstHook == this)
{
//Is there other hooks hanging off us
if (pFirstHook->m_pNextHook)
{
WNDPROC pProc = (WNDPROC)&(pFirstHook->m_pNextHook->m_thunk.thunk);
SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)pProc);
//Also setup the first hook as a windows property
RemoveProp(m_hWnd, sm_pszFirstHookProp);
SetProp(m_hWnd, sm_pszFirstHookProp, pFirstHook->m_pNextHook);
}
else
{
SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_pfnSuperWindowProc);
//Remove the first hook as a windows property
RemoveProp(m_hWnd, sm_pszFirstHookProp);
}
}
else if (pFirstHook)
{
//The item is in the middle of the chain, Just remove it from the linked list
CHookWnd* pHook = pFirstHook;
while (pHook->m_pNextHook != this)
pHook = pHook->m_pNextHook;
ASSERT(pHook);
ASSERT(pHook->m_pNextHook == this);
pHook->m_pNextHook = m_pNextHook;
//Also setup the first hook as a windows property (to balance the calls to SetProp)
RemoveProp(m_hWnd, sm_pszFirstHookProp);
SetProp(m_hWnd, sm_pszFirstHookProp, pFirstHook);
}
//Reset our member variables
m_pfnSuperWindowProc = NULL;
hWnd = m_hWnd;
m_hWnd = NULL;
m_pNextHook = NULL;
//Call the virtual function to indicate that the hook has been removed
OnUnsubclass(hWnd);
}
return hWnd;
}
BOOL CHookWnd::IsHooked() const
{
return (m_hWnd != NULL);
}
BOOL CHookWnd::IsHooked(HWND hWnd)
{
return (FirstHook(hWnd) != NULL);
}
BOOL CHookWnd::IsHooked(CWnd* pWnd)
{
return (FirstHook(pWnd) != NULL);
}
BOOL CHookWnd::FirstInChain() const
{
//Use a Crit section to make code thread-safe
CSingleLock sl(&sm_cs, TRUE);
//Lookup the HWND we're hooking and if the item found
//is this one, there we are the first in the chain
CHookWnd* pFirst = _FirstHook(m_hWnd);
return (pFirst == this);
}
BOOL CHookWnd::LastInChain() const
{
//Use a Crit section to make code thread-safe
CSingleLock sl(&sm_cs, TRUE);
//Lookup the HWND we're hooking, then traverse to the end
//of the chain and see is that the same one as us
CHookWnd* pTemp = _FirstHook(m_hWnd);
if (pTemp)
{
while (pTemp->m_pNextHook)
pTemp = pTemp->m_pNextHook;
}
return (pTemp == this);
}
BOOL CHookWnd::MiddleOfChain() const
{
return (!FirstInChain() && !LastInChain() && (SizeOfHookChain() > 1));
}
int CHookWnd::SizeOfHookChain() const
{
//Use a Crit section to make code thread-safe
CSingleLock sl(&sm_cs, TRUE);
//Run along the linked list to accumulate the size of the
int nSize = 0;
CHookWnd* pTemp = _FirstHook(m_hWnd);
if (pTemp)
{
nSize = 1;
while (pTemp->m_pNextHook)
{
++nSize;
pTemp = pTemp->m_pNextHook;
}
}
return nSize;
}
CHookWnd* CHookWnd::FirstHook(HWND hWnd)
{
//Use a Crit section to make code thread-safe
CSingleLock sl(&sm_cs, TRUE);
//Let the helper function do all the work
return _FirstHook(hWnd);
}
CHookWnd* CHookWnd::FirstHook(CWnd* pWnd)
{
//Validate our parameters
ASSERT(pWnd);
//Pass the buck to the helper function
return FirstHook(pWnd->GetSafeHwnd());
}
CHookWnd* CHookWnd::_FirstHook(HWND hWnd)
{
//What will be the return value
CHookWnd* pFirstHook = NULL;
//Look up the value using GetProp
if (IsWindow(hWnd))
pFirstHook = (CHookWnd*) GetProp(hWnd, sm_pszFirstHookProp);
return pFirstHook;
}
CHookWnd* CHookWnd::FindHook(HWND hWnd, const CRuntimeClass* pClass)
{
//Validate our parameters
ASSERT(IsWindow(hWnd));
ASSERT(pClass);
ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CHookWnd)));
//Use a Crit section to make code thread-safe
CSingleLock sl(&sm_cs, TRUE);
//Get the first one and look for a Hook with the specific type
CHookWnd* pHook = _FirstHook(hWnd);
while (pHook && !pHook->IsKindOf(pClass))
pHook = pHook->m_pNextHook;
return pHook;
}
CHookWnd* CHookWnd::FindHook(CWnd* pWnd, const CRuntimeClass* pClass)
{
//Validate our parameters
ASSERT(pWnd);
//Pass the buck to the helper function
return FindHook(pWnd->GetSafeHwnd(), pClass);
}
CHookWnd* CHookWnd::CreateHook(HWND hWnd, CRuntimeClass* pClass)
{
//Validate our parameters
ASSERT(IsWindow(hWnd));
ASSERT(pClass);
ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CHookWnd)));
//Have we already installed a hook of this type, there is
//not much sense in installing 2 hooks of the same runtime
//type
CHookWnd* pFound = FindHook(hWnd, pClass);
if (pFound)
return pFound;
//Create the new hook
CHookWnd* pNewHook = (CHookWnd*) pClass->CreateObject();
if (pNewHook)
{
pNewHook->m_bAutoDelete = TRUE;
pNewHook->Hook(hWnd);
}
return pNewHook;
}
CHookWnd* CHookWnd::CreateHook(CWnd* pWnd, CRuntimeClass* pClass)
{
//Validate our parameters
ASSERT(pWnd);
//Pass the buck to the helper function
return CreateHook(pWnd->GetSafeHwnd(), pClass);
}
void CHookWnd::RemoveAll(CHookWnd* pFirstHook)
{
CHookWnd* pHook = pFirstHook;
do
{
if (pHook)
{
CHookWnd* pNextHook = pHook->m_pNextHook;
pHook->Unhook();
if (pHook->m_bAutoDelete)
delete pHook;
pHook = pNextHook;
}
}
while (pHook);
}
void CHookWnd::RemoveAll(HWND hWnd)
{
//Validate our parameters
ASSERT(hWnd);
ASSERT(IsWindow(hWnd));
//Pass the buck to the helper function
RemoveAll(FirstHook(hWnd));
}
LRESULT CALLBACK CHookWnd::WindowProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
//Validate our parameters
CHookWnd* pThis = (CHookWnd*) hWnd;
ASSERT(!IsBadReadPtr(pThis, sizeof(pThis)));
ASSERT(pThis);
ASSERT(pThis->m_hWnd != NULL);
//Set local copies of variables
HWND temphWnd = pThis->m_hWnd;
WNDPROC tempSuperWindowProc = pThis->m_pfnSuperWindowProc;
//Call the virtual function for this hook instance
LRESULT lRes;
BOOL bRet = pThis->ProcessWindowMessage(temphWnd, nMsg, wParam, lParam, lRes);
//Handle the case where the first hook has changed in the previous call to ProcessWindowMessage
if (pThis->m_hWnd == NULL)
pThis = FirstHook(temphWnd);
//hand of to the chain if we have a chain and it was not handled by this hook (and we still have a chain)
CHookWnd* pTempHook = pThis;
if (pTempHook)
{
while (!bRet && pTempHook)
{
bRet = pTempHook->ProcessWindowMessage(temphWnd, nMsg, wParam, lParam, lRes);
pTempHook = pTempHook->m_pNextHook;
}
}
//do the default processing if message was not handled
if (!bRet)
{
if (nMsg != WM_NCDESTROY)
lRes = CallWindowProc(tempSuperWindowProc, temphWnd, nMsg, wParam, lParam);
else
{
//Call the super window proc
lRes = CallWindowProc(tempSuperWindowProc, temphWnd, nMsg, wParam, lParam);
//Remove all the hooks
if (pThis)
RemoveAll(pThis);
}
}
return lRes;
}
void CHookWnd::OnSubclass(HWND /*hWnd*/)
{
//Nothing to do, a derived class might have some use for it
}
void CHookWnd::OnUnsubclass(HWND /*hWnd*/)
{
//Nothing to do, a derived class might have some use for it
}
LRESULT CHookWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
ASSERT(m_pfnSuperWindowProc);
return CallWindowProc(m_pfnSuperWindowProc, m_hWnd, nMsg, wParam, lParam);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?