windows-nt/Source/XPSP1/NT/multimedia/directx/dinput/dx7/dll/disubcls.c
2020-09-26 16:20:57 +08:00

2039 lines
56 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;
}