Files
admin
base
com
developer
drivers
ds
enduser
inetcore
inetsrv
loc
mergedcomponents
multimedia
danim
ddk
directx
applicationmanager
ddraw
ddrawex
deliveryimages
dinput
dicfgddk
diconfig
dimapcfg
diquick
dx7
diquick
dll
daytona
dilib
win9x
winnt
assert.c
common.c
debug.h
default.mk
diaddhw.c
diaphack.c
dical.c
dicf.c
didenum.c
didev.c
didev.h
didevdf.c
didevef.c
dieff.c
dieffj.c
dieffv.c
diem.c
diem.h
diemh.c
diemk.c
diemm.c
dieshep.c
diexcl.c
diextdll.c
digendef.c
digenj.c
digenk.c
digenm.c
digenx.c
diguid.c
dihel.c
dihel.h
dihid.c
dihid.h
dihiddat.c
dihidenm.c
dihidini.c
dihidusg.c
dijoycfg.c
dijoyhid.c
dijoyhid.h
dijoyreg.c
dijoyreg.h
dijoytyp.c
dilib1.c
dilib2.c
dilib3.c
dilib4.c
dilib5.c
dilist.c
dimem.c
dinput.c
dinput.def
dinput.fmt
dinput.inc
dinput.inf
dinput.txt
dinputi.h
dinputpr.h
dinputrc.h
dinputrc.w
dinputv.h
diobj.c
dioledup.c
diport.c
diport.h
diraw.c
direg.c
diregutl.c
diriff.c
diriff.h
dirs
disubcls.c
dithunk.c
dithunk.h
diutil.c
diwinnt.c
diwinnt.h
ffdummy.reg
guids.c
makefil0
makefile
makefile.inc
mkhdr.pl
valid.c
dirs
dx8
dxff
ihvmap
pid
sys
dirs
readme.txt
dmusic
dplay
dsound
dxcrt
dxdiag
dxg
dxvb
gamectrl
inc
loc
misc
dirs
project.mk
dshow
embedded
inc
lib
media
netshow
opengl
private
published
reality
resmgr
dirs
multimedia.mk
project.mk
net
printscan
public
published
sdktools
shell
termsrv
tools
windows
dirs
makefil0
2025-04-27 07:49:33 -04:00

2039 lines
58 KiB
C

/*****************************************************************************
*
* DISubCls.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* "Safe subclassing" code, stolen from comctl32.
*
* Originally written by francish. Stolen by raymondc.
*
* Contents:
*
* SetWindowSubclass
* GetWindowSubclass
* RemoveWindowSubclass
* DefSubclassProc
*
*****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflSubclass
/*****************************************************************************
*
* @doc INTERNAL
*
* @topic DirectInput Subclassing |
*
*
* This module defines helper functions that make subclassing windows safe(er)
* and easy(er). The code maintains a single property on the subclassed window
* and dispatches various "subclass callbacks" to its clients a required. The
* client is provided reference data and a simple "default processing" API.
*
* Semantics:
* A "subclass callback" is identified by a unique pairing of a callback
* function pointer and an unsigned ID value. Each callback can also store a
* single DWORD of reference data, which is passed to the callback function
* when it is called to filter messages. No reference counting is performed
* for the callback, it may repeatedly call the SetWindowSubclass API to alter
* the value of its reference data element as desired.
*
*****************************************************************************/
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct SUBCLASS_CALL |
*
* Structure which tracks a single subclassing client.
*
* Although a linked list would have made the code slightly
* simpler, this module uses a packed callback array to avoid
* unneccessary fragmentation.
*
* @field SUBCLASSPROC | pfnSubclass |
*
* The subclass procedure. If this is zero, it means that
* the node is dying and should be ignored.
*
* @field UINT | uIdSubclass |
*
* Unique subclass identifier.
*
* @field DWORD | dwRefData |
*
* Optional reference data for subclass procedure.
*
*****************************************************************************/
typedef struct SUBCLASS_CALL {
SUBCLASSPROC pfnSubclass;
UINT_PTR uIdSubclass;
ULONG_PTR dwRefData;
} SUBCLASS_CALL, *PSUBCLASS_CALL;
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct SUBCLASS_FRAME |
*
* Structure which tracks the state of an active call to the
* window's window procedure.
*
* Each time the window procedure is entered, we create a new
* <t SUBCLASS_FRAME>, which remains active until the last
* subclass procedure returns, at which point the frame is
* torn down.
*
* The subclass frames are stored on the stack. So walking
* the frame chain causes you to wander through the stack.
*
* @field UINT | uCallIndex |
*
* Index of next callback to call.
*
* @field UINT | uDeepestCall |
*
* Deepest <e SUBCLASS_FRAME.uCallIndex> on the stack.
*
* @field SUBCLASS_FRAME * | pFramePrev |
*
* The previous subclass frame.
*
* @field PSUBCLASS_HEADER | pHeader |
*
* The header associated with this frame.
*
*****************************************************************************/
typedef struct SUBCLASS_FRAME {
UINT uCallIndex;
UINT uDeepestCall;
struct SUBCLASS_FRAME *pFramePrev;
struct SUBCLASS_HEADER *pHeader;
} SUBCLASS_FRAME, *PSUBCLASS_FRAME;
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct SUBCLASS_HEADER |
*
* Structure which tracks the subclass goo associated with
* a window. A pointer to this structure is kept in a private
* window property.
*
* @field UINT | uRefs |
*
* Subclass count. This is the number of valid entries
* in the <p CallArray>.
*
* @field UINT | uAlloc |
*
* Number of allocated <t SUBCLASS_CALL> nodes in the array.
*
* @field UINT | uCleanup |
*
* Index of the call node to clean up.
*
* @field WORD | dwThreadId |
*
* Thread id of the window with which the structure is associated.
*
* @field PSUBCLASS_FRAME | pFrameCur |
*
* Pointer to the current subclass frame.
*
* @field SUBCLASS_CALL | CallArray[1] |
*
* Base of the packed call node array.
*
*****************************************************************************/
typedef struct SUBCLASS_HEADER {
UINT uRefs;
UINT uAlloc;
UINT uCleanup;
DWORD dwThreadId;
PSUBCLASS_FRAME pFrameCur;
SUBCLASS_CALL CallArray[1];
} SUBCLASS_HEADER, *PSUBCLASS_HEADER;
#define CALLBACK_ALLOC_GRAIN (3) /* 1 defproc, 1 subclass, 1 spare */
LRESULT CALLBACK
MasterSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
LRESULT INTERNAL
CallNextSubclassProc(PSUBCLASS_HEADER pHeader, HWND hwnd, UINT wm,
WPARAM wp, LPARAM lp);
/*****************************************************************************
*
* @doc INTERNAL
*
* @func LRESULT | SubclassDeath |
*
* This function is called if we ever enter one of our subclassing
* procedures without our reference data (and hence without the
* previous <t WNDPROC>).
*
* Hitting this represents a catastrophic failure in the
* subclass code.
*
* The function resets the <t WNDPROC> of the window to
* <f DefWindowProc> to avoid faulting.
*
* @parm HWND | hwnd |
*
* Window that just got hosed.
*
* @parm UINT | wm |
*
* Window message that caused us to realize that we are hosed.
*
* @parm WPARAM | wp |
*
* Meaning depends on window message.
*
* @parm LPARAM | lp |
*
* Meaning depends on window message.
*
*****************************************************************************/
LRESULT INTERNAL
SubclassDeath(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
/*
* WE SHOULD NEVER EVER GET HERE
*/
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflError,
TEXT("Fatal! SubclassDeath in window %p"),
hwnd);
AssertF(0);
/*
* We call the outside world, so we'd better not have the critsec.
*/
AssertF(!InCrit());
/*
* In theory, we could save the original WNDPROC in a separate property,
* but that just wastes memory for something that should never happen.
*/
#ifdef WINNT
SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR)(DefWindowProc));
#else
SubclassWindow(hwnd, DefWindowProc);
#endif
return DefWindowProc(hwnd, wm, wp, lp);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func WNDPROC | GetWindowProc |
*
* Returns the <t WNDPROC> of the specified window.
*
* @parm HWND | hwnd |
*
* Window to be inspected.
*
* @returns
*
* The <t WNDPROC> of the specified window.
*
*****************************************************************************/
WNDPROC INLINE
GetWindowProc(HWND hwnd)
{
#ifdef WINNT
return (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
#else
return (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC);
#endif
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @global ATOM | g_atmDISubclass |
*
* This is the global <t ATOM> we use to store our
* <t SUBCLASS_HEADER> property on whatever windows come our way.
*
* If the <p WIN95_HACK> symbol is defined, then we will work
* around a bug in Windows 95 where Windows "helpfully"
* <f GlobalDeleteAtom>'s all properties that are on a window
* when the window dies. See Francis's original explanation
* in subclass.c.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
TCHAR c_tszDISubclass[] = TEXT("DirectInputSubclassInfo");
#pragma END_CONST_DATA
#ifdef WIN95_HACK
ATOM g_atmDISubclass;
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func PSUBCLASS_HEADER | FastGetSubclassHeader |
*
* Obtains the <t SUBCLASS_HEADER> for the specified window.
*
* This function succeeds on any thread, although the value
* is meaningless from the wrong process.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @returns
*
* Pointer to the <t SUBCLASS_HEADER> associated with the window,
* or <c NULL> if the window is not subclassed by us.
*
*****************************************************************************/
PSUBCLASS_HEADER INLINE
FastGetSubclassHeader(HWND hwnd)
{
#ifdef WIN95_HACK
/*
* The right thing happens if g_atmDISubclass is 0, namely,
* the property is not found. Unfortunately, NT RIPs when
* you do this, so we'll be polite and not RIP.
*/
if (g_atmDISubclass) {
return (PSUBCLASS_HEADER)GetProp(hWnd, (PV)g_atmDISubclass);
} else {
return 0;
}
#else
return (PSUBCLASS_HEADER)GetProp(hwnd, c_tszDISubclass);
#endif
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func PSUBCLASS_HEADER | GetSubclassHeader |
*
* Obtains the <t SUBCLASS_HEADER> for the specified window.
* It fails if the caller is in the wrong process, but will
* allow the caller to get the header from a different thread.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @returns
*
* Pointer to the <t SUBCLASS_HEADER> associated with the window,
* or <c NULL> if the window is not subclass by us yet, or 1
* if it belongs to another process.
*
*****************************************************************************/
PSUBCLASS_HEADER INTERNAL
GetSubclassHeader(HWND hwnd)
{
DWORD idProcess;
/*
* Make sure we're in the right process.
*
* Must use our helper function to catch bad scenarios like
* the goofy Windows 95 console window which lies about its
* owner.
*/
idProcess = GetWindowPid(hwnd);
if (idProcess == GetCurrentProcessId()) { /* In the right process */
return FastGetSubclassHeader(hwnd);
} else {
if (idProcess) {
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflError,
TEXT("XxxWindowSubclass: ")
TEXT("wrong process for window %p"), hwnd);
}
return (PSUBCLASS_HEADER)1;
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | SetSubclassHeader |
*
* Sets the <t SUBCLASS_HEADER> for the specified window.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* The value to set.
*
* @parm PSUBCLASS_FRAME | pFrameFixup |
*
* The active frames, which need to be walked and fixed up
* to refer to the new <t SUBCLASS_HEADER>.
*
*****************************************************************************/
BOOL INTERNAL
SetSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader,
PSUBCLASS_FRAME pFrameFixup)
{
BOOL fRc;
AssertF(InCrit()); /* We are partying on the header and frame list */
#ifdef WIN95_HACK
if (g_atmDISubclass == 0) {
ATOM atm;
/*
* HACK: we are intentionally incrementing the refcount on this atom
* WE DO NOT WANT IT TO GO BACK DOWN so we will not delete it in
* process detach (see comments for g_atmDISubclass in subclass.c
* for more info).
*/
atm = GlobalAddAtom(c_tszDISubclass);
if (atm) {
g_atmDISubclass = atm; /* In case the old atom got nuked */
}
}
#endif
/*
* Update the frame list if required.
*/
while (pFrameFixup) {
pFrameFixup->pHeader = pHeader;
pFrameFixup = pFrameFixup->pFramePrev;
}
/*
* If we have a window to update, then update/remove the property
* as required.
*/
if (hwnd) {
if (!pHeader) {
#ifdef WIN95_HACK
/*
* HACK: we remove with an ATOM so the refcount won't drop
* (see comments for g_atmDISubclass above)
*/
RemoveProp(hwnd, (PV)g_atmDISubclass);
#else
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl, TEXT("SetSubclassHeader: Removing %p"),
pHeader);
RemoveProp(hwnd, c_tszDISubclass);
#endif
fRc = 1;
} else {
#ifdef WIN95_HACK
/*
* HACK: we add using a STRING so the refcount will go up
* (see comments for g_atmDISubclass above)
*/
#endif
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl, TEXT("SetSubclassHeader: Adding %p"),
pHeader);
fRc = SetProp(hwnd, c_tszDISubclass, pHeader);
if (!fRc) {
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflError, TEXT("SetWindowSubclass: ")
TEXT("couldn't subclass window %p"), hwnd);
}
}
} else {
fRc = 1; /* Weird vacuous success */
}
return fRc;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | FreeSubclassHeader |
*
* Toss the subclass header for the specified window.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* The value being tossed.
*
*****************************************************************************/
void INTERNAL
FreeSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader)
{
AssertF(InCrit()); /* we will be removing the subclass header */
/*
* Sanity checking...
*/
if (pHeader) {
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl, TEXT("FreeSubclassHeader: Freeing %p"),
pHeader);
SetSubclassHeader(hwnd, 0, pHeader->pFrameCur); /* Clean up the header */
LocalFree(pHeader);
} else {
AssertF(0);
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | ReallocSubclassHeader |
*
* Change the size of the subclass header as indicated.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* The current header.
*
* @parm UINT | uCallbacks |
*
* Desired size.
*
*****************************************************************************/
PSUBCLASS_HEADER INTERNAL
ReAllocSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader, UINT uCallbacks)
{
UINT uAlloc;
AssertF(InCrit()); /* we will be replacing the subclass header */
/*
* Granularize the allocation.
*/
uAlloc = CALLBACK_ALLOC_GRAIN *
((uCallbacks + CALLBACK_ALLOC_GRAIN - 1) / CALLBACK_ALLOC_GRAIN);
/*
* Do we need to change the allocation?
*/
if (!pHeader || (uAlloc != pHeader->uAlloc)) {
/*
* compute bytes required
*/
uCallbacks = uAlloc * sizeof(SUBCLASS_CALL) + sizeof(SUBCLASS_HEADER);
/*
* And try to alloc / realloc.
*/
if (SUCCEEDED(ReallocCbPpv(uCallbacks, &pHeader))) {
/*
* Update info.
*/
pHeader->uAlloc = uAlloc;
if (SetSubclassHeader(hwnd, pHeader, pHeader->pFrameCur)) {
} else {
FreeSubclassHeader(hwnd, pHeader);
pHeader = 0;
}
} else {
pHeader = 0;
}
}
AssertF(pHeader);
return pHeader;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func LRESULT | CallOriginalWndProc |
*
* This procedure is the default <t SUBCLASSPROC> which is always
* installed when we subclass a window. The original window
* procedure is installed as the reference data for this
* callback. It simply calls the original <t WNDPROC> and
* returns its result.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm UINT | wm |
*
* Window message that needs to go to the original <t WNDPROC>.
*
* @parm WPARAM | wp |
*
* Meaning depends on window message.
*
* @parm LPARAM | lp |
*
* Meaning depends on window message.
*
* @parm UINT | uIdSubclass |
*
* ID number (not used).
*
* @parm DWORD | dwRefData |
*
* Reference data for subclass procedure (original <t WNDPROC>).
*
*****************************************************************************/
LRESULT CALLBACK
CallOriginalWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp,
UINT_PTR uIdSubclass, ULONG_PTR dwRefData)
{
/*
* dwRefData should be the original window procedure
*/
AssertF(dwRefData);
/*
* and call it.
*/
return CallWindowProc((WNDPROC)dwRefData, hwnd, wm, wp, lp);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func PSUBCLASS_HEADER | AttachSubclassHeader |
*
* This procedure makes sure that a given window is subclassed by us.
* It maintains a reference count on the data structures associated
* with our subclass. if the window is not yet subclassed by us
* then this procedure installs our subclass procedure and
* associated data structures.
*
* @parm HWND | hwnd |
*
* Window in question.
*
*****************************************************************************/
PSUBCLASS_HEADER INTERNAL
AttachSubclassHeader(HWND hwnd)
{
PSUBCLASS_HEADER pHeader;
/*
* We party on the subclass call chain here
*/
AssertF(InCrit());
/*
* Yes, we subclass the window out of context, but we are careful
* to avoid race conditions. There is still a problem if some
* other DLL tries to un-subclass a window just as we are subclassing
* it. But there's nothing you can do about it, and besides,
* what are the odds...?
*/
/*
* If haven't already subclassed the window then do it now
*/
pHeader = GetSubclassHeader(hwnd);
if( pHeader == (PSUBCLASS_HEADER)1 )
{
/*
* It's all gone horribly wrong
* This can happen when the application uses joyXXX functions in Winmm.dll.
*/
pHeader = 0;
}
else if (pHeader == 0) {
/*
* attach our header data to the window
* we need space for two callbacks:
* the subclass and the original proc
*/
pHeader = ReAllocSubclassHeader(hwnd, 0, 2);
if (pHeader) {
SUBCLASS_CALL *pCall;
/*
* Set up the first node in the array to call
* the original WNDPROC. Do this before subclassing
* to avoid a race if the window receives a message
* after we have installed our subclass but before
* we can save the original WNDPROC.
*/
AssertF(pHeader->uAlloc);
pCall = pHeader->CallArray;
pCall->pfnSubclass = CallOriginalWndProc;
pCall->uIdSubclass = 0;
pCall->dwRefData = (ULONG_PTR)GetWindowProc(hwnd);
/*
* init our subclass refcount...
*/
pHeader->uRefs = 1;
pHeader->dwThreadId = GetWindowThreadProcessId(hwnd, NULL);
/*
* Super-paranoid. We must must not race with another
* instance of ourselves trying to un-subclass.
*/
AssertF(InCrit());
/*
* Save the new "old" wndproc in case we raced with
* somebody else trying to subclass.
*/
#ifdef WINNT
pCall->dwRefData = (ULONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MasterSubclassProc);
#else
pCall->dwRefData = (DWORD)SubclassWindow(hwnd, MasterSubclassProc);
#endif
if (pCall->dwRefData) {
DllLoadLibrary(); /* Make sure we don't get unloaded */
} else { /* clean up and get out */
FreeSubclassHeader(hwnd, pHeader);
pHeader = 0;
}
}
}
return pHeader;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | DetachSubclassHeader |
*
* This procedure attempts to detach the subclass header from
* the specified window.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* Header to detach.
*
* @parm BOOL | fForce |
*
* Nonzero if we should detach even if we are not the top-level
* subclass.
*
*****************************************************************************/
void INTERNAL
DetachSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader, BOOL fForce)
{
WNDPROC wndprocOld;
AssertF(InCrit()); /* we party on the subclass call chain here */
AssertF(pHeader); /* fear */
/*
* If we are not being forced to remove and the window is still
* valid then sniff around a little and decide if it's a good
* idea to detach now.
*/
if (!fForce && hwnd) {
AssertF(pHeader == FastGetSubclassHeader(hwnd)); /* paranoia */
/* should always have the "call original" node */
AssertF(pHeader->uRefs);
/*
* We can't have active clients.
* We can't have people still on our stack.
*/
if (pHeader->uRefs <= 1 && !pHeader->pFrameCur) {
/*
* We must be in the correct context.
*/
if (pHeader->dwThreadId == GetCurrentThreadId()) {
/*
* We kept the original window procedure as refdata for our
* CallOriginalWndProc subclass callback.
*/
wndprocOld = (WNDPROC)pHeader->CallArray[0].dwRefData;
AssertF(wndprocOld);
/*
* Make sure we are the top of the subclass chain.
*/
if (GetWindowProc(hwnd) == MasterSubclassProc) {
/*
* go ahead and try to detach
*/
#ifdef WINNT
if (SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)wndprocOld)) {
#else
if (SubclassWindow(hwnd, wndprocOld)) {
#endif
SquirtSqflPtszV(sqfl, TEXT("DetachSubclassHeader: ")
TEXT("Unhooked"));
} else {
AssertF(0); /* just plain shouldn't happen */
goto failed;
}
} else { /* Not at top of chain; can't do it */
SquirtSqflPtszV(sqfl, TEXT("DetachSubclassHeader: ")
TEXT("Somebody else subclassed"));
goto failed;
}
} else { /* Out of context. Try again later. */
SendNotifyMessage(hwnd, WM_NULL, 0, 0L);
goto failed;
}
} else {
// 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl, TEXT("DetachSubclassHeader: ")
TEXT("Still %d users, %p frame"),
pHeader->uRefs, pHeader->pFrameCur);
goto failed;
}
}
#if 0
#ifdef DEBUG
{
/*
* warn about anybody who hasn't unhooked yet
*/
UINT uCur;
SUBCLASS_CALL *pCall;
uCur = pHeader->uRefs;
pCall = pHeader->CallArray + uCur;
/* don't complain about our 'call original' node */
while (--uCur) {
pCall--;
if (pCall->pfnSubclass) {
/*
* always warn about these they could be leaks
*/
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflError, TEXT("warning: orphan subclass: ")
TEXT("fn %p, id %08x, dw %08x"),
pCall->pfnSubclass, pCall->uIdSubclass,
pCall->dwRefData);
}
}
}
#endif
#endif
/*
* free the header now
*/
FreeSubclassHeader(hwnd, pHeader);
DllFreeLibrary(); /* Undo LoadLibrary when we hooked */
failed:;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | PurgeSingleCallNode |
*
* Purges a single dead node in the call array.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* The header associated with the window.
* The <p uCleanup> field is the index of the node being
* cleaned up.
*
*****************************************************************************/
void INTERNAL
PurgeSingleCallNode(HWND hwnd, PSUBCLASS_HEADER pHeader)
{
AssertF(InCrit()); /* we will try to re-arrange the call array */
if (pHeader->uCleanup) {/* Sanity check */
UINT uRemain;
SquirtSqflPtszV(sqfl,
TEXT("PurgeSingleCallNode: Purging number %d"),
pHeader->uCleanup);
/*
* and a little paranoia
*/
AssertF(pHeader->CallArray[pHeader->uCleanup].pfnSubclass == 0);
AssertF(fLimpFF(pHeader->pFrameCur,
pHeader->uCleanup < pHeader->pFrameCur->uDeepestCall));
/*
* are there any call nodes above the one we're about to remove?
*/
uRemain = pHeader->uRefs - pHeader->uCleanup;
if (uRemain > 0) {
/*
* yup, need to fix up the array the hard way
*/
SUBCLASS_CALL *pCall;
SUBCLASS_FRAME *pFrame;
UINT uCur, uMax;
/*
* move the remaining nodes down into the empty space
*/
pCall = pHeader->CallArray + pHeader->uCleanup;
/*
* Since the souce and destination overlap (unless there's only
* one node remaining) the behavior of memcpy is undefined.
* memmove (aka MoveMemory) would guarantee the correct
* behavior but requires the runtime library.
* Since this is the only function we require in retail from the
* RTL, it is not worth the 22% bloat we gain from using the
* static version and using the dynamic version is a load time
* and redist test hit. So copy the array one element at a time.
*/
for( uCur = 0; uCur < uRemain; uCur++ )
{
memcpy( &pCall[uCur], &pCall[uCur+1], sizeof(*pCall) );
}
/*
* update the call indices of any active frames
*/
uCur = pHeader->uCleanup;
pFrame = pHeader->pFrameCur;
while (pFrame) {
if (pFrame->uCallIndex >= uCur) {
pFrame->uCallIndex--;
if (pFrame->uDeepestCall >= uCur) {
pFrame->uDeepestCall--;
}
}
pFrame = pFrame->pFramePrev;
}
/*
* now search for any other dead call nodes in the remaining area
*/
uMax = pHeader->uRefs - 1; /* we haven't decremented uRefs yet */
while (uCur < uMax && pCall->pfnSubclass) {
pCall++;
uCur++;
}
pHeader->uCleanup = (uCur < uMax) ? uCur : 0;
} else {
/*
* No call nodes above. This case is easy.
*/
pHeader->uCleanup = 0;
}
/*
* finally, decrement the client count
*/
pHeader->uRefs--;
SquirtSqflPtszV(sqfl, TEXT("warning: PurgeSingleCallNode: ")
TEXT("Still %d refs"), pHeader->uRefs);
} else {
AssertF(0); /* Nothing to do! */
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CompactSubclassHeader |
*
* Attempts to compact the subclass array, freeing the
* subclass header if the array is empty.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* The header associated with the window.
*
*****************************************************************************/
void INTERNAL
CompactSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader)
{
AssertF(InCrit()); /* we will try to re-arrange the call array */
/*
* we must handle the "window destroyed unexpectedly during callback" case
*/
if (hwnd) {
/*
* Clean out as many dead callbacks as possible.
*
* The "DeepestCall" test is an optimization so we don't go
* purging call nodes when no active frame cares.
*
* (I'm not entirely conviced of this. I mean, we have to
* purge it eventually anyway, right?)
*/
while (pHeader->uCleanup &&
fLimpFF(pHeader->pFrameCur,
pHeader->uCleanup < pHeader->pFrameCur->uDeepestCall)) {
PurgeSingleCallNode(hwnd, pHeader);
}
/*
* do we still have clients?
*/
if (pHeader->uRefs > 1) {
SquirtSqflPtszV(sqfl, TEXT("CompactSubclassHeader: ")
TEXT("Still %d users"), pHeader->uRefs);
/*
* yes, shrink our allocation, leaving room for at least one client
*/
ReAllocSubclassHeader(hwnd, pHeader, pHeader->uRefs + 1);
goto done;
}
}
/*
* There are no clients left, or the window is gone.
* Try to detach and free
*/
DetachSubclassHeader(hwnd, pHeader, FALSE);
done:;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func PSUBCLASS_CALL | FindCallRecord |
*
* Searches for a call record with the specified subclass proc
* and id, and returns its address. If no such call record is
* found then NULL is returned.
*
* This is a helper function used when we need to track down
* a callback because the client is changing its refdata or
* removing it.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* The header in which to search.
*
* @parm SUBCLASSPROC | pfnSubclass |
*
* Subclass callback procedure to locate.
*
* @parm UINT | uIdSubclass |
*
* Instance identifier associated with the callback.
*
*****************************************************************************/
SUBCLASS_CALL * INTERNAL
FindCallRecord(PSUBCLASS_HEADER pHeader, SUBCLASSPROC pfnSubclass,
UINT_PTR uIdSubclass)
{
SUBCLASS_CALL *pCall;
UINT uCallIndex;
AssertF(InCrit()); /* we'll be scanning the call array */
/*
* scan the call array. note that we assume there is always at least
* one member in the table (our CallOriginalWndProc record)
*/
uCallIndex = pHeader->uRefs;
pCall = &pHeader->CallArray[uCallIndex];
do {
uCallIndex--;
pCall--;
if ((pCall->pfnSubclass == pfnSubclass) &&
(pCall->uIdSubclass == uIdSubclass))
{
return pCall;
}
} while (uCallIndex != (UINT)-1);
return NULL;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | GetWindowSubclass |
*
* Retrieves the reference data for the specified window
* subclass callback.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm SUBCLASSPROC | pfnSubclass |
*
* Subclass callback procedure to locate.
*
* @parm UINT | uIdSubclass |
*
* Instance identifier associated with the callback.
*
* @parm LPDWORD | pdwRefData |
*
* Output pointer.
*
*****************************************************************************/
BOOL EXTERNAL
GetWindowSubclass(HWND hwnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass,
PULONG_PTR pdwRefData)
{
BOOL fRc;
ULONG_PTR dwRefData;
DllEnterCrit();
/*
* sanity
*/
if (IsWindow(hwnd) && pfnSubclass) {
PSUBCLASS_HEADER pHeader;
SUBCLASS_CALL *pCall;
/*
* if we've subclassed it and they are a client then get the refdata
*/
pHeader = GetSubclassHeader(hwnd);
if (pHeader &&
(pHeader != (PSUBCLASS_HEADER)1) &&
(pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass)) != 0) {
/*
* fetch the refdata and note success
*/
fRc = 1;
dwRefData = pCall->dwRefData;
} else {
fRc = 0;
dwRefData = 0;
}
} else { /* Invalid window handle */
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflError, TEXT("GetWindowSubclass: ")
TEXT("Bad window %p or callback %p"),
hwnd, pfnSubclass);
fRc = 0;
dwRefData = 0;
}
/*
* we always fill in/zero pdwRefData regradless of result
*/
if (pdwRefData) {
*pdwRefData = dwRefData;
}
DllLeaveCrit();
return fRc;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | SetWindowSubclass |
*
* Installs/updates a window subclass callback. Subclass
* callbacks are identified by their callback address and id pair.
* If the specified callback/id pair is not yet installed then
* the procedure installs the pair. If the callback/id pair is
* already installed, then this procedure changes the reference
* data for the pair.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm SUBCLASSPROC | pfnSubclass |
*
* Subclass callback procedure to install or modify.
*
* @parm UINT | uIdSubclass |
*
* Instance identifier associated with the callback.
*
* @parm DWORD | dwRefData |
*
* Reference data to associate with the callback/id.
*
*****************************************************************************/
BOOL EXTERNAL
SetWindowSubclass(HWND hwnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass,
ULONG_PTR dwRefData)
{
BOOL fRc;
/*
* sanity
*/
if (IsWindow(hwnd) && pfnSubclass) {
SUBCLASS_HEADER *pHeader;
/*
* we party on the subclass call chain here
*/
DllEnterCrit();
/*
* actually subclass the window
*/
/*
* Prefix gets confused (mb:34501) by this. I assume this is because
* AttachSubclassHeader returns a pointer to allocated memory but we
* allow the pointer to go out of context without saving it. This is
* OK because AttachSubclassHeader already saved it for us.
*/
pHeader = AttachSubclassHeader(hwnd);
if (pHeader) {
SUBCLASS_CALL *pCall;
/*
* find a call node for this caller
*/
pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass);
if (pCall == NULL) {
/*
* not found, alloc a new one
*/
SUBCLASS_HEADER *pHeaderT =
ReAllocSubclassHeader(hwnd, pHeader, pHeader->uRefs + 1);
if (pHeaderT) {
pHeader = pHeaderT;
pCall = &pHeader->CallArray[pHeader->uRefs++];
} else {
/*
* re-query in case it is already gone
*/
pHeader = FastGetSubclassHeader(hwnd);
if (pHeader) {
CompactSubclassHeader(hwnd, pHeader);
}
goto bail;
}
}
/*
* fill in the subclass call data
*/
pCall->pfnSubclass = pfnSubclass;
pCall->uIdSubclass = uIdSubclass;
pCall->dwRefData = dwRefData;
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl,
TEXT("SetWindowSubclass: Added %p/%d as %d"),
pfnSubclass, uIdSubclass, pHeader->uRefs - 1);
fRc = 1;
} else { /* Unable to subclass */
bail:;
fRc = 0;
}
DllLeaveCrit();
} else {
fRc = 0; /* Invalid parameter */
}
return fRc;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | RemoveWindowSubclass |
*
* Removes a subclass callback from a window.
* Subclass callbacks are identified by their
* callback address and id pair.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm SUBCLASSPROC | pfnSubclass |
*
* Subclass callback procedure to remove.
*
* @parm UINT | uIdSubclass |
*
* Instance identifier associated with the callback.
*
*****************************************************************************/
BOOL EXTERNAL
RemoveWindowSubclass(HWND hwnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass)
{
BOOL fRc;
/*
* sanity
*/
if (IsWindow(hwnd) && pfnSubclass) {
SUBCLASS_HEADER *pHeader;
/*
* we party on the subclass call chain here
*/
DllEnterCrit();
/*
* obtain our subclass data and find the callback to remove.
*/
pHeader = GetSubclassHeader(hwnd);
if (pHeader && (pHeader != (PSUBCLASS_HEADER)1) ) {
SUBCLASS_CALL *pCall;
/*
* find the callback to remove
*/
pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass);
if (pCall) {
UINT uCall;
/*
* disable this node.
*/
pCall->pfnSubclass = 0;
/*
* Remember that we have something to clean up.
*
* Set uCleanup to the index of the shallowest node that
* needs to be cleaned up. CompactSubclassHeader will
* clean up everything from uCleanup onward.
*/
uCall = (UINT)(pCall - pHeader->CallArray);
if (fLimpFF(pHeader->uCleanup, uCall < pHeader->uCleanup)) {
pHeader->uCleanup = uCall;
}
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl,
TEXT("RemoveWindowSubclass: Removing %p/%d as %d"),
pfnSubclass, uIdSubclass, uCall);
/*
* now try to clean up any unused nodes
*/
CompactSubclassHeader(hwnd, pHeader);
/*
* the call above can realloc or free the subclass
* header for this window, so make sure we don't use it.
*/
D(pHeader = 0);
fRc = 1;
} else { /* Not found */
fRc = 0;
}
} else { /* Never subclassed (ergo not found) */
fRc = 0;
}
/*
* release the critical section and return the result
*/
DllLeaveCrit();
} else {
fRc = 0; /* Validation failed */
}
return fRc;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func LRESULT | DefSubclassProc |
*
* Calls the next handler in the window's subclass chain.
* The last handler in the subclass chain is installed by us,
* and calls the original window procedure for the window.
*
* Every subclass procedure should call <f DefSubclassProc>
* in order to allow the message to be processed by other handlers.
*
* @parm HWND | hwnd |
*
* Window in question.
*
* @parm UINT | wm |
*
* Window message that needs to go to the original <t WNDPROC>.
*
* @parm WPARAM | wp |
*
* Meaning depends on window message.
*
* @parm LPARAM | lp |
*
* Meaning depends on window message.
*
*****************************************************************************/
LRESULT EXTERNAL
DefSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
LRESULT lResult;
/*
* make sure the window is still valid
*/
if (IsWindow(hwnd)) {
PSUBCLASS_HEADER pHeader;
/*
* take the critical section while we figure out who to call next
*/
AssertF(!InCrit());
DllEnterCrit();
/*
* complain if we are being called improperly
*/
pHeader = FastGetSubclassHeader(hwnd);
if (pHeader &&
pHeader->pFrameCur &&
GetCurrentThreadId() == pHeader->dwThreadId) {
/*
* call the next proc in the subclass chain
*
* WARNING: this call temporarily releases the critical section
* WARNING: pHeader is invalid when this call returns
*/
lResult = CallNextSubclassProc(pHeader, hwnd, wm, wp, lp);
D(pHeader = 0);
} else {
SquirtSqflPtszV(sqfl | sqflError,
TEXT("DefSubclassProc: Called improperly"));
lResult = 0;
}
DllLeaveCrit();
} else {
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflError,
TEXT("DefSubclassProc: %P not a window"),
hwnd);
lResult = 0;
}
return lResult;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | UpdateDeepestCall |
*
* Updates the deepest call index for the specified frame.
*
* @parm PSUBCLASS_FRAME | pFrame |
*
* Frame in question.
*
*****************************************************************************/
void INTERNAL
UpdateDeepestCall(SUBCLASS_FRAME *pFrame)
{
AssertF(InCrit()); /* we are partying on the frame list */
/*
* My deepest call equals my current call or my parent's
* deepest call, whichever is deeper.
*/
if (pFrame->pFramePrev &&
(pFrame->pFramePrev->uDeepestCall < pFrame->uCallIndex)) {
pFrame->uDeepestCall = pFrame->pFramePrev->uDeepestCall;
} else {
pFrame->uDeepestCall = pFrame->uCallIndex;
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | EnterSubclassFrame |
*
* Sets up a new subclass frame for the specified header,
* saving away the previous one.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* Header in question.
*
* @parm PSUBCLASS_FRAME | pFrame |
*
* Brand new frame to link in.
*
*****************************************************************************/
void INLINE
EnterSubclassFrame(PSUBCLASS_HEADER pHeader, SUBCLASS_FRAME *pFrame)
{
AssertF(InCrit()); /* we are partying on the header and frame list */
/*
* fill in the frame and link it into the header
*/
pFrame->uCallIndex = pHeader->uRefs + 1;
pFrame->pFramePrev = pHeader->pFrameCur;
pFrame->pHeader = pHeader;
pHeader->pFrameCur = pFrame;
/*
* initialize the deepest call index for this frame
*/
UpdateDeepestCall(pFrame);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | LeaveSubclassFrame |
*
* Tear down the current subclass frame, restoring the previous one.
*
* @parm PSUBCLASS_FRAME | pFrame |
*
* Frame going away.
*
*****************************************************************************/
PSUBCLASS_HEADER INLINE
LeaveSubclassFrame(SUBCLASS_FRAME *pFrame)
{
PSUBCLASS_HEADER pHeader;
AssertF(InCrit()); /* we are partying on the header */
/*
* unlink the frame from its header (if it still exists)
*/
pHeader = pFrame->pHeader;
if (pHeader) {
pHeader->pFrameCur = pFrame->pFramePrev;
}
return pHeader;
}
#ifdef SUBCLASS_HANDLEEXCEPTIONS
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | SubclassFrameException |
*
* Clean up when a exception is thrown from a subclass frame.
*
* @parm PSUBCLASS_FRAME | pFrame |
*
* Frame to clean up.
*
*****************************************************************************/
void INTERNAL
SubclassFrameException(SUBCLASS_FRAME *pFrame)
{
/*
* clean up the current subclass frame
*/
AssertF(!InCrit());
DllEnterCrit();
SquirtSqflPtszV(sqfl | sqflError, TEXT("SubclassFrameException: ")
TEXT("cleaning up subclass frame after exception"));
LeaveSubclassFrame(pFrame);
DllLeaveCrit();
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func LRESULT | MasterSubclassProc |
*
* The window procedure we install to dispatch subclass
* callbacks.
*
* It maintains a linked list of "frames" through the stack
* which allow <f DefSubclassProc> to call the right subclass
* procedure in multiple-message scenarios.
*
* @parm HWND | hwnd |
*
* Window under attack.
*
* @parm UINT | wm |
*
* Window message.
*
* @parm WPARAM | wp |
*
* Meaning depends on window message.
*
* @parm LPARAM | lp |
*
* Meaning depends on window message.
*
*****************************************************************************/
LRESULT CALLBACK
MasterSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
SUBCLASS_HEADER *pHeader;
LRESULT lResult;
/*
* prevent people from partying on the callback chain while we look at it
*/
AssertF(!InCrit());
DllEnterCrit();
/*
* We'd better have our data.
*/
pHeader = FastGetSubclassHeader(hwnd);
if (pHeader) {
SUBCLASS_FRAME Frame;
/*
* set up a new subclass frame and save away the previous one
*/
EnterSubclassFrame(pHeader, &Frame);
#ifdef SUBCLASS_HANDLEEXCEPTIONS
__try /* protect our state information from exceptions */
#endif
{
/*
* go ahead and call the subclass chain on this frame
*
* WARNING: this call temporarily releases the critical section
* WARNING: pHeader is invalid when this call returns
*/
lResult =
CallNextSubclassProc(pHeader, hwnd, wm, wp, lp);
D(pHeader = 0);
}
#ifdef SUBCLASS_HANDLEEXCEPTIONS
__except (SubclassFrameException(&Frame), EXCEPTION_CONTINUE_SEARCH)
{
AssertF(0);
}
#endif
AssertF(InCrit());
/*
* restore the previous subclass frame
*/
pHeader = LeaveSubclassFrame(&Frame);
/*
* Do postprocessing if the header is still here.
*/
if (pHeader) {
/*
* was the window nuked (somehow)
* without us seeing the WM_NCDESTROY?
*/
if (!IsWindow(hwnd)) {
/*
* EVIL! somebody subclassed after us and didn't pass on WM_NCDESTROY
*/
AssertF(!TEXT("unknown subclass proc swallowed a WM_NCDESTROY"));
/* go ahead and clean up now */
hwnd = 0;
wm = WM_NCDESTROY;
}
/*
* if we are returning from a WM_NCDESTROY then we need to clean up
*/
if (wm == WM_NCDESTROY) {
DetachSubclassHeader(hwnd, pHeader, TRUE);
} else {
/*
* is there any pending cleanup, or are all our clients gone?
*/
if (pHeader->uCleanup ||
(!pHeader->pFrameCur && (pHeader->uRefs <= 1))) {
CompactSubclassHeader(hwnd, pHeader);
D(pHeader = 0);
}
}
/*
* all done
*/
} else {
/*
* Header is gone. We already cleaned up in a nested frame.
*/
}
DllLeaveCrit();
AssertF(!InCrit());
} else { /* Our property vanished! */
DllLeaveCrit();
lResult = SubclassDeath(hwnd, wm, wp, lp);
}
return lResult;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func UINT | EnterSubclassCallback |
*
* Finds the next callback in the subclass chain and updates
* <p pFrame> to indicate that we are calling it.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* Controlling header.
*
* @parm PSUBCLASS_FRAME | pFrame |
*
* Frame to update.
*
* @parm SUBCLASS_CALL * | pCallChosen |
*
* The call selected for calling.
*
*****************************************************************************/
UINT INTERNAL
EnterSubclassCallback(PSUBCLASS_HEADER pHeader, SUBCLASS_FRAME *pFrame,
SUBCLASS_CALL *pCallChosen)
{
SUBCLASS_CALL *pCall;
UINT uDepth;
/*
* we will be scanning the subclass chain and updating frame data
*/
AssertF(InCrit());
/*
* scan the subclass chain for the next callable subclass callback
* Assert that the loop will terminate.
*/
AssertF(pHeader->CallArray[0].pfnSubclass);
pCall = pHeader->CallArray + pFrame->uCallIndex;
uDepth = 0;
do {
uDepth++;
pCall--;
} while (!pCall->pfnSubclass);
/*
* copy the callback information for the caller
*/
pCallChosen->pfnSubclass = pCall->pfnSubclass;
pCallChosen->uIdSubclass = pCall->uIdSubclass;
pCallChosen->dwRefData = pCall->dwRefData;
/*
* adjust the frame's call index by the depth we entered
*/
pFrame->uCallIndex -= uDepth;
/*
* keep the deepest call index up to date
*/
UpdateDeepestCall(pFrame);
return uDepth;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | LeaveSubclassCallback |
*
* Get out one level.
*
* @parm PSUBCLASS_FRAME | pFrame |
*
* Frame to update.
*
* @parm UINT | uDepth |
*
* Who just finished.
*
*****************************************************************************/
void INLINE
LeaveSubclassCallback(SUBCLASS_FRAME *pFrame, UINT uDepth)
{
/*
* we will be updating subclass frame data
*/
AssertF(InCrit());
/*
* adjust the frame's call index by the depth we entered and return
*/
pFrame->uCallIndex += uDepth;
/*
* keep the deepest call index up to date
*/
UpdateDeepestCall(pFrame);
}
#ifdef SUBCLASS_HANDLEEXCEPTIONS
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | SubclassCallbackException |
*
* Clean up when a subclass callback throws an exception.
*
* @parm PSUBCLASS_FRAME | pFrame |
*
* Frame to clean up.
*
* @parm UINT | uDepth |
*
* Where we were.
*
*****************************************************************************/
void INTERNAL
SubclassCallbackException(SUBCLASS_FRAME *pFrame, UINT uDepth)
{
/*
* clean up the current subclass callback
*/
AssertF(!InCrit());
DllEnterCrit();
SquirtSqflPtszV(sqfl | sqflError, TEXT("SubclassCallbackException: ")
TEXT("cleaning up subclass callback after exception"));
LeaveSubclassCallback(pFrame, uDepth);
DllLeaveCrit();
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func LRESULT | CallNextSubclassProc |
*
* Calls the next subclass callback in the subclass chain.
*
* WARNING: this call temporarily releases the critical section.
*
* WARNING: <p pHeader> is invalid when this call returns.
*
* @parm PSUBCLASS_HEADER | pHeader |
*
* The header that is tracking the state.
*
* @parm HWND | hwnd |
*
* Window under attack.
*
* @parm UINT | wm |
*
* Window message.
*
* @parm WPARAM | wp |
*
* Meaning depends on window message.
*
* @parm LPARAM | lp |
*
* Meaning depends on window message.
*
*****************************************************************************/
LRESULT INTERNAL
CallNextSubclassProc(PSUBCLASS_HEADER pHeader, HWND hwnd, UINT wm,
WPARAM wp, LPARAM lp)
{
SUBCLASS_CALL Call;
SUBCLASS_FRAME *pFrame;
LRESULT lResult;
UINT uDepth;
AssertF(InCrit()); /* sanity */
AssertF(pHeader); /* paranoia */
/*
* get the current subclass frame
*/
pFrame = pHeader->pFrameCur;
AssertF(pFrame);
/*
* get the next subclass call we need to make
*/
uDepth = EnterSubclassCallback(pHeader, pFrame, &Call);
/*
* leave the critical section so we don't deadlock in our callback
*
* WARNING: pHeader is invalid when this call returns
*/
DllLeaveCrit();
D(pHeader = 0);
/*
* we call the outside world so prepare to deadlock if we have the critsec
*/
AssertF(!InCrit());
#ifdef SUBCLASS_HANDLEEXCEPTIONS
__try /* protect our state information from exceptions */
#endif
{
/*
* call the chosen subclass proc
*/
AssertF(Call.pfnSubclass);
lResult = Call.pfnSubclass(hwnd, wm, wp, lp,
Call.uIdSubclass, Call.dwRefData);
}
#ifdef SUBCLASS_HANDLEEXCEPTIONS
__except (SubclassCallbackException(pFrame, uDepth),
EXCEPTION_CONTINUE_SEARCH)
{
AssertF(0);
}
#endif
/*
* we left the critical section before calling out so re-enter it
*/
AssertF(!InCrit());
DllEnterCrit();
/*
* finally, clean up and return
*/
LeaveSubclassCallback(pFrame, uDepth);
return lResult;
}