#include "stdafx.h" #include "handlers.h" //-------------------------------------------------------------------------// // Declare registered message vars **here** //-------------------------------------------------------------------------// #define __NO_APPHACKS__ //-------------------------------------------------------------------------// // Message Handlers //-------------------------------------------------------------------------// // Rules for message handlers [scotthan]: // // (1) Use DECL_ macros to declare message handler prototype and // table entries for message handlers below. // (2) A message handler implementation should NOT: // 1. call DefWindowProc or CallWindowProc directly, // but rather use DoMsgDefault(). // 2. delete the incoming CThemeWnd* object, // (3) A message handler SHOULD: // 1. Honor the codepage value in the message block when // handling messages that carry string args. // If the codepage member is CP_WINUNICODE, the widechar // string processing should be assumed; otherwise, multibyte // string processing should be assumed. // 2. If a message should not be forwarded for default processing, // mark the message as handled using MsgHandled(). // (4) Handlers should be listed in the BEGIN/ENDMSG() block // below in decreasing order of expected frequency. //---------------------// // WndProc overrides //---------------------// // msg handler decls: DECL_MSGHANDLER( OnOwpPostCreate ); DECL_MSGHANDLER( OnOwpPreStyleChange ); DECL_MSGHANDLER( OnOwpPreWindowPosChanging ); DECL_MSGHANDLER( OnOwpPreWindowPosChanged ); DECL_MSGHANDLER( OnOwpPostWindowPosChanged ); DECL_MSGHANDLER( OnOwpPostSettingChange ); DECL_MSGHANDLER( OnOwpPreMeasureItem ); DECL_MSGHANDLER( OnOwpPreDrawItem ); DECL_MSGHANDLER( OnOwpPreMenuChar ); DECL_MSGHANDLER( OnOwpPostThemeChanged ); DECL_MSGHANDLER( OnOwpPreNcPaint ); DECL_MSGHANDLER( OnOwpPostNcPaint ); // handler table: BEGIN_HANDLER_TABLE(_rgOwpHandlers) // frequent messages DECL_MSGENTRY( WM_NCPAINT, OnOwpPreNcPaint, OnOwpPostNcPaint ) DECL_MSGENTRY( WM_WINDOWPOSCHANGING, OnOwpPreWindowPosChanging, NULL ) DECL_MSGENTRY( WM_WINDOWPOSCHANGED, OnOwpPreWindowPosChanged, OnOwpPostWindowPosChanged ) DECL_MSGENTRY( WM_SETTINGCHANGE, NULL, OnOwpPostSettingChange ) DECL_MSGENTRY( WM_MEASUREITEM, OnOwpPreMeasureItem, NULL ) DECL_MSGENTRY( WM_DRAWITEM, OnOwpPreDrawItem, NULL ) DECL_MSGENTRY( WM_MDISETMENU, NULL, NULL ) // rare messages: DECL_MSGENTRY( WM_MENUCHAR, OnOwpPreMenuChar, NULL ) DECL_MSGENTRY( WM_STYLECHANGING, OnOwpPreStyleChange, NULL ) DECL_MSGENTRY( WM_STYLECHANGED, OnOwpPreStyleChange, NULL ) DECL_MSGENTRY( WM_NCCREATE, NULL, NULL ) DECL_MSGENTRY( WM_CREATE, NULL, OnOwpPostCreate ) DECL_MSGENTRY( WM_NCDESTROY, NULL, NULL ) DECL_MSGENTRY( WM_THEMECHANGED, NULL, OnOwpPostThemeChanged ) // we handle in line in ThemePreWndProc() DECL_MSGENTRY( WM_THEMECHANGED_TRIGGER, NULL, NULL ) // we handle in line in ThemePreWndProc() END_HANDLER_TABLE() // Note: values of high owp message must be in sync w/ table. #define WNDPROC_MSG_LAST WM_THEMECHANGED_TRIGGER // 0x031B (alias for WM_UAHINIT) //------------------------// // DefDlgProc overrides //------------------------// // msg handler decls: DECL_MSGHANDLER( OnDdpPostCtlColor ); DECL_MSGHANDLER( OnDdpCtlColor ); DECL_MSGHANDLER( OnDdpPrint ); DECL_MSGHANDLER( OnDdpPostInitDialog ); // handler table: BEGIN_HANDLER_TABLE(_rgDdpHandlers) // frequent messages: DECL_MSGENTRY( WM_CTLCOLORDLG, NULL, OnDdpPostCtlColor ) DECL_MSGENTRY( WM_CTLCOLORSTATIC, NULL, OnDdpCtlColor) DECL_MSGENTRY( WM_CTLCOLORBTN, NULL, OnDdpCtlColor) DECL_MSGENTRY( WM_CTLCOLORMSGBOX, NULL, OnDdpPostCtlColor ) DECL_MSGENTRY( WM_PRINTCLIENT, NULL, OnDdpPrint ) // rare messages: DECL_MSGENTRY( WM_INITDIALOG, NULL, OnDdpPostInitDialog ) END_HANDLER_TABLE() // Note: values of high ddp message must be in sync w/ table. #define DEFDLGPROC_MSG_LAST WM_PRINTCLIENT // 0x0318 //--------------------------// // DefWindowProc override //--------------------------// // msg handler decls: DECL_MSGHANDLER( OnDwpNcPaint ); DECL_MSGHANDLER( OnDwpNcHitTest ); DECL_MSGHANDLER( OnDwpNcActivate ); DECL_MSGHANDLER( OnDwpNcLButtonDown ); DECL_MSGHANDLER( OnDwpNcThemeDrawCaption ); DECL_MSGHANDLER( OnDwpNcThemeDrawFrame ); DECL_MSGHANDLER( OnDwpNcMouseMove ); DECL_MSGHANDLER( OnDwpNcMouseLeave ); DECL_MSGHANDLER( OnDwpWindowPosChanged ); DECL_MSGHANDLER( OnDwpSysCommand ); DECL_MSGHANDLER( OnDwpSetText ); DECL_MSGHANDLER( OnDwpSetIcon ); DECL_MSGHANDLER( OnDwpStyleChanged ); DECL_MSGHANDLER( OnDwpPrint ); DECL_MSGHANDLER( OnDwpPrintClient ); DECL_MSGHANDLER( OnDwpContextMenu ); // handler table: BEGIN_HANDLER_TABLE(_rgDwpHandlers) // frequent messages: DECL_MSGENTRY( WM_NCHITTEST, OnDwpNcHitTest, NULL ) DECL_MSGENTRY( WM_NCPAINT, OnDwpNcPaint, NULL ) DECL_MSGENTRY( WM_NCACTIVATE, OnDwpNcActivate, NULL ) DECL_MSGENTRY( WM_NCMOUSEMOVE, OnDwpNcMouseMove, NULL ) DECL_MSGENTRY( WM_NCMOUSELEAVE, OnDwpNcMouseLeave, NULL ) DECL_MSGENTRY( WM_WINDOWPOSCHANGED, OnDwpWindowPosChanged, NULL ) DECL_MSGENTRY( WM_SYSCOMMAND, OnDwpSysCommand, NULL ) DECL_MSGENTRY( WM_NCLBUTTONDOWN, OnDwpNcLButtonDown, NULL ) DECL_MSGENTRY( WM_NCUAHDRAWCAPTION, OnDwpNcThemeDrawCaption, NULL ) DECL_MSGENTRY( WM_NCUAHDRAWFRAME, OnDwpNcThemeDrawFrame, NULL ) DECL_MSGENTRY( WM_PRINT, OnDwpPrint, NULL ) DECL_MSGENTRY( WM_PRINTCLIENT, OnDwpPrintClient, NULL ) DECL_MSGENTRY( WM_CTLCOLORMSGBOX, OnDdpPostCtlColor, NULL) // Strange: Sent to DefWindowProc, but is a Dialog message DECL_MSGENTRY( WM_CTLCOLORSTATIC, OnDdpCtlColor, NULL) DECL_MSGENTRY( WM_CTLCOLORBTN, OnDdpCtlColor, NULL) // rare messages: DECL_MSGENTRY( WM_SETTEXT, OnDwpSetText, NULL ) DECL_MSGENTRY( WM_SETICON, OnDwpSetIcon, NULL ) DECL_MSGENTRY( WM_STYLECHANGED, OnDwpStyleChanged, NULL ) DECL_MSGENTRY( WM_CONTEXTMENU, OnDwpContextMenu, NULL ) DECL_MSGENTRY( WM_THEMECHANGED_TRIGGER, NULL, NULL ) DECL_MSGENTRY( WM_NCDESTROY, NULL, NULL ) END_HANDLER_TABLE() // Note: values of high dwp message must be in sync w/ handler table. #define DEFWNDPROC_MSG_LAST WM_THEMECHANGED_TRIGGER // 0x031B //--------------------------------------------------------------------------- BOOL _FindMsgHandler( UINT, MSGENTRY [], int, IN HOOKEDMSGHANDLER*, IN HOOKEDMSGHANDLER* ); BOOL _SetMsgHandler( UINT, MSGENTRY [], int, IN HOOKEDMSGHANDLER, BOOL ); //--------------------------------------------------------------------------- // Special case hook handling //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- BOOL _IsExcludedSystemProcess( LPCWSTR pszProcess ) { static const WCHAR* _rgszSystemProcessList[] = { L"lsass", // Local Security Authority sub-system L"services", // Service Control Manager L"svchost", // Service Host L"mstask", // Microsoft Task Scheduler L"dfssvc", // Distributed File System Service L"winmgmt", // Windows Management Instrumentation L"spoolsv", // Print Spool Service L"msdtc", // Microsoft Distributed Transaction Co-ordinator L"regsvc", // Remote Registry Service L"webclnt", // Web Client L"mspmspsv", // WMDM PMSP Service (what is this?) L"ntvdm" // NT virtual DOS machine }; return AsciiScanStringList( pszProcess, _rgszSystemProcessList, ARRAYSIZE(_rgszSystemProcessList), TRUE ); } //--------------------------------------------------------------------------- BOOL _IsProcessOnInteractiveWindowStation() // check if we're on winsta0. { BOOL fRet = FALSE; HWINSTA hWinSta = GetProcessWindowStation(); if( hWinSta != NULL ) { DWORD cbLength = 0; WCHAR wszName[0xFF]; WCHAR* pszName = wszName; GetUserObjectInformationW(hWinSta, UOI_NAME, NULL, 0, &cbLength); if( cbLength < sizeof(wszName) ) { pszName = (WCHAR*)LocalAlloc(LMEM_FIXED, cbLength); if( NULL == pszName ) return FALSE; } if (pszName != NULL) { if( GetUserObjectInformationW(hWinSta, UOI_NAME, pszName, cbLength, &cbLength) != FALSE ) { fRet = (0 == AsciiStrCmpI(pszName, L"winsta0")); } if( pszName != wszName ) LocalFree(pszName); } } return(fRet); } //--------------------------------------------------------------------------- BOOL _IsWin16App() // check if this is a 16-bit process { GUITHREADINFO gti; gti.cbSize = sizeof(gti); gti.flags = GUI_16BITTASK; return GetGUIThreadInfo( GetCurrentThreadId(), >i ) && TESTFLAG(gti.flags, GUI_16BITTASK ); } //--------------------------------------------------------------------------- BOOL ApiHandlerInit( const LPCTSTR pszProcess, USERAPIHOOK* puahTheme, const USERAPIHOOK* puahReal ) { // exclude known non-UI system processes if( _IsExcludedSystemProcess( pszProcess ) ) return FALSE; // exclude any process not hosted on winsta0. if( !_IsProcessOnInteractiveWindowStation() ) return FALSE; if( _IsWin16App() ) return FALSE; // SHIMSHIM [scotthan]: #ifdef _DEBUG //---- temp patch against msvcmon ---- if( 0 == AsciiStrCmpI(pszProcess, L"msvcmon") ) { return FALSE; } //---- temp patch against msdev ---- if( 0 == AsciiStrCmpI(pszProcess, L"msdev") ) { return FALSE; } //---- Aid in debugging classic/themed differences: --- if( 0 == AsciiStrCmpI( pszProcess, L"mditest1" ) ) { return FALSE; } #endif #ifndef __NO_APPHACKS__ static const WCHAR* _rgszExcludeAppList[] = { #ifdef THEME_CALCSIZE // Invoking SetWindowPos from CThemeWnd::SetFrameTheme on our Post-WM_CREATE handler // causes emacs to divide by zero after receiving meaningless rects from two // successive calls to AdjustWindowRectEx from his WM_WINDOWPOSCHANGING handler. // I don't believe it is related to the fact that AdjustWindowRectEx has yet // to be implemented for themed windows (raid# 140989), but rather that the wndproc // is not ready for a WM_WINDOWPOSCHANGING message on the abrubtly on the // heels of a WM_CREATE handler. L"emacs", L"neoplanet", // 247283: We rush in to theme neoplanet's dialogs, which we almost L"np", // immediately revoke, but not before sizing the dialog to theme-compatible // client rect. When we withdraw, we leave it clipped. No good way to deal // with this for beta2. // HTML Editor++ v.8: 286676: // This guy recomputes his nonclient area, and then AVs dereferencing a // WM_WINDOWPOSCHANGING message under themes. L"coffee", #endif THEME_CALCSIZE L"refcntr", // 205059: Corel Reference Center; lower 10% of window is clipped. L"KeyFramerPro", // 336456: Regardless of whether themes are enabled, Boris KeyFramer Pro v.5 // does two SetWindowRgn() calls for every WM_PAINT, the first with a region, // the next with NULL, Is the app trying to clip his painting? // If so, this is not what SetWindowRgn was intended for, and explains why this // app is so clunky at window resizing. Rather, SelectClipRgn is the // correct API. // When themes are enabled, we keep revoking and re-attatching with each // SetWindowRgn call, so we get substantial flicker. // The ISV should be notified of this bug. // Applications that do custom non-client painting and hence look broken when // themed. Our only recourse at the moment it to exclude them from non-client // themeing so that we don't stomp whatever they are trying to do. L"RealJBox", // 273370: Real JukeBox L"RealPlay", // 285368: Real AudioPlayer L"TeamMgr", // 286654: Microsoft Team Manager97 L"TrpMaker", // 307107: Rand McNally TripMaker 2000 L"StrFindr", // 307535: Rand McNally StreetFinder 2000 L"Exceed", // 276244: Hummingbird Exceed 6.2/7.0 L"VP30", // 328676: Intel Video Phone // 313407: Groove, build 760 // Calls DefWindowProc for NCPAINT, then paints his own caption over it. // Note: this just might work correctly if we had a DrawFrameControl hook. L"groove", // filever 1.1.0.760, 1/22/2001 tested. // 303756: Exclude all Lotus SmartSuite apps to provide consistency among their // apps. All of them draw into the caption bar. L"WordPro", // 285065: Lotus WordPro, a particularly poorly implemented app. L"SmartCtr", // It's WordPerfect compat menu is the elephant man of modern software. L"123w", L"Approach", L"FastSite", L"F32Main", L"Org5", // 358337: Best Technology - GCC Developer Lite. Custom caption bar fights with Luna. L"GCCDevL", // install point: http://www.besttechnology.co.jp/download/GDL1_0_3_6.EXE // 360422: J Zenrin The Real Digital Map Z3(T1):Max/Min/Close buttons are overlapped on classic buttons in title bar. L"emZmain", // 364337: Encarta World English Dictionary: Luna system buttons are overlaid on top of app's custom ones when mousing over L"ewed.exe", // 343171: Reaktor Realtime Instrument: pressing the close button while themed causes this app to // spin in a tight loop running at realtime priority, effectively hanging the machine. // The message loop for this app is extremely timing sensitive, the additional overhead // introduced by theming alters the timing enough to break this app. L"Reaktor", }; if( AsciiScanStringList( pszProcess, _rgszExcludeAppList, ARRAYSIZE(_rgszExcludeAppList), TRUE ) ) { return FALSE; } #ifdef THEME_CALCSIZE // Winstone 99 needs modified NC_CALCSIZE behavior for Netscape or it will hang. if ( 0 == AsciiStrCmpI( pszProcess, L"Netscape" )) { if (FindWindowEx(NULL, NULL, L"ZDBench32Frame", NULL) != NULL) { _SetMsgHandler( WM_NCCALCSIZE, _rgDwpHandlers, ARRAYSIZE(_rgDwpHandlers), OnDwpNcCalcSize2, FALSE ); return TRUE; } } #endif THEME_CALCSIZE //------------------------- // This AppHack was once fixed, but got broke again with // addition of logic for partial-screen maximized windows. // // Something in our answer to NCCALCSIZE causes quick time player // to continously flood its 'control' frame window's winproc with // WM_PAINTS by repeatedly calling InvalidateRgn + UpdateWindow. My // suspicion is that he looks at what DefWindowProc returns from // NCCALCSIZE to determine the area he needs to manage, and when // this doesn't hash with other SYSMET values and/or AdjustWindowRect, // he redundantly invalidates himself, // // This only repros if qtp is launched w/ .mov file, works fine if // launched without a file and then a file is loaded. #ifdef THEME_CALCSIZE if( 0 == AsciiStrCmpI( pszProcess, L"QuickTimePlayer" )) { _SetMsgHandler( WM_NCCALCSIZE, _rgDwpHandlers, ARRAYSIZE(_rgDwpHandlers), OnDwpNcCalcSize2, FALSE ); return TRUE; } // SEANHI DID NOT RECEIVE THE S/W FROM APPLIB AND SO WAS UNABLE TO VERIFY THIS // NO LONGER REPROS W/ ELIMINATION OF THEMED SYSMETS //------------------------- // Paradox 9 appHack for nonclient button sizes: // // Paradox table schema view uses DrawFrameControl to render both // a classic toolframe (small) caption and buttons, but uses the // themed values of SM_CYSIZE instead of SM_CYSMSIZE to size the buttons. // This apphack redirects requests in this process for SM_CX/YSIZE to SM_CX/YSMSIZE. if( 0 == AsciiStrCmpI( pszProcess, L"pdxwin32" ) ) { _SetGsmHandler( SM_CXSIZE, OnGsmCxSmBtnSize ); _SetGsmHandler( SM_CYSIZE, OnGsmCySmBtnSize ); return TRUE; } #endif THEME_CALCSIZE //------------------------- #else # pragma message("App hacks disabled") #endif __NO_APPHACKS__ return TRUE; } //--------------------------------------------------------------------------- // Handler table utility functions //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void HandlerTableInit() {} //--------------------------------------------------------------------------- BOOL _InitMsgMask( LPBYTE prgMsgMask, DWORD dwMaskBytes, MSGENTRY* prgEntries, int cEntries, IN OUT BOOL& fInit ) { if( !fInit ) { for( int i = 0; i < cEntries; i++ ) { if( -1 == prgEntries[i].nMsg ) { ASSERT(prgEntries[i].pnRegMsg); // Initialize registered message entry prgEntries[i].nMsg = *prgEntries[i].pnRegMsg; Log(LOG_TMHANDLE, L"InitMsgMsg corrected registered msg: 0x%x", prgEntries[i].nMsg); } //---- ensure we set up limit on table correctly ---- ASSERT((prgEntries[i].nMsg)/8 < dwMaskBytes); SET_MSGMASK( prgMsgMask, prgEntries[i].nMsg ); } fInit = TRUE; } return fInit; } //--------------------------------------------------------------------------- // Scan of MSG table as linear array: inline int _FindMsgHandler( UINT nMsg, MSGENTRY rgEntries[], int cEntries, OUT OPTIONAL HOOKEDMSGHANDLER* ppfnHandler, OUT OPTIONAL HOOKEDMSGHANDLER* ppfnHandler2 ) { ASSERT( nMsg ); ASSERT( nMsg != (UINT)-1 ); if( ppfnHandler ) *ppfnHandler = NULL; if( ppfnHandler2 ) *ppfnHandler2 = NULL; for( int i = 0; i < cEntries; i++ ) { if( rgEntries[i].nMsg == nMsg ) { // If no handler requested, return success if( NULL == ppfnHandler && NULL == ppfnHandler2 ) return i; // Assign outbound handler values if( ppfnHandler ) *ppfnHandler = rgEntries[i].pfnHandler; if( ppfnHandler2 ) *ppfnHandler2 = rgEntries[i].pfnHandler2; // return TRUE iif caller got what he asked for. return ((ppfnHandler && *ppfnHandler) || (ppfnHandler2 && *ppfnHandler2)) ? i : -1; } } return -1; } //--------------------------------------------------------------------------- // Modify existing handler inline BOOL _SetMsgHandler( UINT nMsg, MSGENTRY rgEntries[], int cEntries, IN HOOKEDMSGHANDLER pfnHandler, BOOL fHandler2 ) { int i = _FindMsgHandler( nMsg, rgEntries, cEntries, NULL, NULL ); if( i >= 0 ) { if( fHandler2 ) rgEntries[i].pfnHandler2 = pfnHandler; else rgEntries[i].pfnHandler = pfnHandler; return TRUE; } return FALSE; } #define CBMSGMASK(msgHigh) (((msgHigh)+1)/8 + ((((msgHigh)+1) % 8) ? 1: 0)) //--------------------------------------------------------------------------- DWORD GetOwpMsgMask( LPBYTE* prgMsgMask ) { static BOOL _fOwpMask = FALSE; // initialized? static BYTE _rgOwpMask[CBMSGMASK(WNDPROC_MSG_LAST)] = {0}; if( _InitMsgMask( _rgOwpMask, ARRAYSIZE(_rgOwpMask), _rgOwpHandlers, ARRAYSIZE(_rgOwpHandlers), _fOwpMask ) ) { *prgMsgMask = _rgOwpMask; return ARRAYSIZE(_rgOwpMask); } return 0; } //--------------------------------------------------------------------------- DWORD GetDdpMsgMask( LPBYTE* prgMsgMask ) { static BOOL _fDdpMask = FALSE; // initialized? static BYTE _rgDdpMask[CBMSGMASK(DEFDLGPROC_MSG_LAST)] = {0}; if( _InitMsgMask( _rgDdpMask, ARRAYSIZE(_rgDdpMask), _rgDdpHandlers, ARRAYSIZE(_rgDdpHandlers), _fDdpMask ) ) { *prgMsgMask = _rgDdpMask; return ARRAYSIZE(_rgDdpMask); } return 0; } //--------------------------------------------------------------------------- DWORD GetDwpMsgMask( LPBYTE* prgMsgMask ) { static BOOL _fDwpMask = FALSE; // initialized? static BYTE _rgDwpMask[CBMSGMASK(DEFWNDPROC_MSG_LAST)] = {0}; if( _InitMsgMask( _rgDwpMask, ARRAYSIZE(_rgDwpMask), _rgDwpHandlers, ARRAYSIZE(_rgDwpHandlers), _fDwpMask ) ) { *prgMsgMask = _rgDwpMask; return ARRAYSIZE(_rgDwpMask); } return 0; } //--------------------------------------------------------------------------- BOOL FindOwpHandler( UINT nMsg, HOOKEDMSGHANDLER* ppfnPre, HOOKEDMSGHANDLER* ppfnPost ) { return _FindMsgHandler( nMsg, _rgOwpHandlers, ARRAYSIZE(_rgOwpHandlers), ppfnPre, ppfnPost ) >= 0; } //--------------------------------------------------------------------------- BOOL FindDdpHandler( UINT nMsg, HOOKEDMSGHANDLER* ppfnPre, HOOKEDMSGHANDLER* ppfnPost ) { return _FindMsgHandler( nMsg, _rgDdpHandlers, ARRAYSIZE(_rgDdpHandlers), ppfnPre, ppfnPost ) >= 0; } //--------------------------------------------------------------------------- BOOL FindDwpHandler( UINT nMsg, HOOKEDMSGHANDLER* ppfnPre ) { HOOKEDMSGHANDLER pfnPost; return _FindMsgHandler( nMsg, _rgDwpHandlers, ARRAYSIZE(_rgDwpHandlers), ppfnPre, &pfnPost ) >= 0; } //--------------------------------------------------------------------------- // Performs default message processing. LRESULT WINAPI DoMsgDefault( const THEME_MSG *ptm ) { ASSERT( ptm ); if( ptm->pfnDefProc ) { MsgHandled( ptm ); if( MSGTYPE_DEFWNDPROC == ptm->type ) return ptm->pfnDefProc( ptm->hwnd, ptm->uMsg, ptm->wParam, ptm->lParam ); else { ASSERT( NULL == ptm->pfnDefProc ); // bad initialization (_InitThemeMsg) } } return 0L; }