#include "ctlspriv.h" /////////////////////////////////////////////////////////////////////////////// // SUBCLASS.C -- subclassing helper functions // // SetWindowSubclass // GetWindowSubclass // RemoveWindowSubclass // DefSubclassProc // // 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. // // History: // 26-April-96 francish Created. // /////////////////////////////////////////////////////////////////////////////// // // NOTE: Although a linked list would have made the code slightly simpler, this // module uses a packed callback array to avoid unneccessary fragmentation. fh // struct _SUBCLASS_HEADER; typedef struct { SUBCLASSPROC pfnSubclass; // subclass procedure UINT uIdSubclass; // unique subclass identifier DWORD dwRefData; // optional ref data } SUBCLASS_CALL; typedef struct _SUBCLASS_FRAME { UINT uCallIndex; // index of next callback to call UINT uDeepestCall; // deepest uCallIndex on stack struct _SUBCLASS_FRAME *pFramePrev; // previous subclass frame pointer struct _SUBCLASS_HEADER *pHeader; // header associated with this frame } SUBCLASS_FRAME; typedef struct _SUBCLASS_HEADER { UINT uRefs; // subclass count UINT uAlloc; // allocated subclass call nodes UINT uCleanup; // index of call node to clean up DWORD dwThreadId; // thread id of window we are hooking SUBCLASS_FRAME *pFrameCur; // current subclass frame pointer SUBCLASS_CALL CallArray[1]; // base of packed call node array } SUBCLASS_HEADER; #define CALLBACK_ALLOC_GRAIN (3) // 1 defproc, 1 subclass, 1 spare /////////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK MasterSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT CallNextSubclassProc(SUBCLASS_HEADER *pHeader, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); //----------------------------------------------------------------------------- // RETAIL_ZOMBIE_MESSAGE_WNDPROC // // this macro controls the generation of diagnostic code for an error condition // in the subclass code (see the SubclassDeath function below). // // commenting out this macro will zombie windows using DefWindowProc instead. // //----------------------------------------------------------------------------- //#define RETAIL_ZOMBIE_MESSAGE_WNDPROC #if defined(RETAIL_ZOMBIE_MESSAGE_WNDPROC) || defined(DEBUG) #ifndef DEBUG #pragma message("\r\nWARNING: disable retail ZombieWndProc before final release\r\n") #endif LRESULT ZombieWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); #else #define ZombieWndProc DefWindowProc #endif //----------------------------------------------------------------------------- // SubclassDeath // // this function is called if we ever enter one of our subclassing procedures // without our reference data (and hence without the previous wndproc). // // hitting this represents a catastrophic failure in the subclass code. // // the function resets the wndproc of the window to a 'zombie' window // procedure to avoid faulting. the RETAIL_ZOMBIE_MESSAGE_WNDPROC macro above // controls the generation of diagnostic code for this wndproc. // //----------------------------------------------------------------------------- LRESULT SubclassDeath(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // // WE SHOULD NEVER EVER GET HERE // if we do please find francish to debug it immediately // DebugMsg(TF_ALWAYS, TEXT("fatal: SubclassDeath in window %08X"), hWnd); // // if we are in a debugger, stop now regardless of break flags // __try { DebugBreak(); } __except(EXCEPTION_EXECUTE_HANDLER) {;} // // we call the outside world so prepare to deadlock if we have the critsec // ASSERTNONCRITICAL; // // in theory we could save the original wndproc in a separate property // but that just wastes memory for something that should never happen // // convert this window to a zombie in hopes that it will get debugged // InvalidateRect(hWnd, NULL, TRUE); SubclassWindow(hWnd, ZombieWndProc); return ZombieWndProc(hWnd, uMsg, wParam, lParam); } //----------------------------------------------------------------------------- // GetWindowProc // // this inline function returns the current wndproc for the specified window. // //----------------------------------------------------------------------------- __inline WNDPROC GetWindowProc(HWND hWnd) { return (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC); } //----------------------------------------------------------------------------- // g_aCC32Subclass // // This is the global ATOM we use to store our SUBCLASS_HEADER property on // random windows that come our way. // // HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK // // Win95's property code is BROKEN. If you SetProp using a text string, USER // adds and removes atoms for the property symmetrically, including when the // window is destroyed with properties lying around (good). Unfortunately, if // you SetProp using a global atom, USER doesn't do things quite right in the // window cleanup case. It uses the atom without adding references in SetProp // calls and without deleting them in RemoveProp calls (good so far). However, // when a window with one of these properties lying around is cleaned up, USER // will delete the atom on you. This tends to break apps that do the // following: // // - MyAtom = GlobalAddAtom("foo"); // at app startup // - SetProp(SomeWindow, MyAtom, MyData); // - // -