// Copyright (c) 1996-2000 Microsoft Corporation // -------------------------------------------------------------------------- // // util // // Miscellaneous helper routines // // -------------------------------------------------------------------------- #include "oleacc_p.h" //#include "util.h" // already in oleacc_p.h #include "propmgr_util.h" #include "propmgr_client.h" #include #include "Win64Helper.h" const LPCTSTR g_szHotKeyEvent = TEXT("MSAASetHotKeyEvent"); const LPCTSTR g_szHotKeyAtom = TEXT("MSAASetFocusHotKey"); const LPCTSTR g_szMessageWindowClass = TEXT("MSAAMessageWindow"); #define HWND_MESSAGE ((HWND)-3) #define HOT_KEY 0xB9 LRESULT CALLBACK MessageWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); // This class is used to give aother window focus. // This used to be done using SetForgroundWindow but in Win2k and beyond you (as in your thread) // must have input focus in order for SetForgroundWindow to work. This class create a message // window, then registers a HotKey, sends that HotKey to the window and waits for that key to get // to its window proc. When it does I now has input focus and can call SetForgroundWindow with // the desired affect. class CSetForegroundWindowHelper { public: CSetForegroundWindowHelper() : m_hwndMessageWindow( NULL ), m_atomHotKeyId( 0 ), m_vkHotKey( 0 ), m_fReceivedHotKey( FALSE ), m_hwndTarget( NULL ), m_cUseCount( 0 ) { } ~CSetForegroundWindowHelper() { Reset(); } BOOL SetForegroundWindow( HWND hwnd ); LRESULT CALLBACK CSetForegroundWindowHelper::WinProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); private: BOOL RegHotKey(); void UnRegHotKey(); BOOL CreateHiddenWindow(); void Reset(); private: HWND m_hwndMessageWindow; ATOM m_atomHotKeyId; WORD m_vkHotKey; // this virtural key is undefined HWND m_hwndTarget; bool m_fReceivedHotKey; int m_cUseCount; }; BOOL CSetForegroundWindowHelper::SetForegroundWindow( HWND hwnd ) { // if a regular SetForegroundWindow works their is no reason to go through all this // work. This will be the case in win9x and win2k administrators. if ( ::SetForegroundWindow( hwnd ) ) return TRUE; if ( !m_hwndMessageWindow ) { m_vkHotKey = HOT_KEY; if ( !CreateHiddenWindow() ) { DBPRINTF( TEXT("CreateHiddenWindow failed") ); return FALSE; } // Wake up in 5 minutes and see if anyone is using this window SetTimer( m_hwndMessageWindow, 1, 300000, NULL ); } if ( !RegHotKey() ) { DBPRINTF( TEXT("RegHotKey failed") ); return FALSE; } m_hwndTarget = hwnd; m_cUseCount++; m_fReceivedHotKey = false; MyBlockInput (TRUE); // Get state of shift keys and if they are down, send an up // when we're done BOOL fCtrlPressed = GetKeyState(VK_CONTROL) & 0x8000; BOOL fAltPressed = GetKeyState(VK_MENU) & 0x8000; BOOL fShiftPressed = GetKeyState(VK_SHIFT) & 0x8000; if (fCtrlPressed) SendKey (KEYRELEASE,VK_VIRTUAL,VK_CONTROL,0); if (fAltPressed) SendKey (KEYRELEASE,VK_VIRTUAL,VK_MENU,0); if (fShiftPressed) SendKey (KEYRELEASE,VK_VIRTUAL,VK_SHIFT,0); // send the hot key SendKey( KEYPRESS, VK_VIRTUAL, m_vkHotKey, 0 ); SendKey( KEYRELEASE, VK_VIRTUAL, m_vkHotKey, 0 ); // send shift key down events if they were down before if (fCtrlPressed) SendKey (KEYPRESS,VK_VIRTUAL,VK_CONTROL,0); if (fAltPressed) SendKey (KEYPRESS,VK_VIRTUAL,VK_MENU,0); if (fShiftPressed) SendKey (KEYPRESS,VK_VIRTUAL,VK_SHIFT,0); MyBlockInput (FALSE); MSG msg; // Spin in this message loop until we get the hot key while ( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); if ( m_fReceivedHotKey ) break; } m_fReceivedHotKey = false; UnRegHotKey(); return TRUE; } LRESULT CALLBACK CSetForegroundWindowHelper::WinProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { LRESULT lReturn = 0; switch( Msg ) { case WM_HOTKEY: m_fReceivedHotKey = TRUE; ::SetForegroundWindow( m_hwndTarget ); break; case WM_TIMER: if ( m_cUseCount == 0 ) { KillTimer( m_hwndMessageWindow, 1 ); Reset(); } m_cUseCount = 0; break; default: lReturn = DefWindowProc( hWnd, Msg, wParam, lParam ); } return lReturn; } BOOL CSetForegroundWindowHelper::RegHotKey() { // If the ATOM is set the we already have a registered HotKey so get out if ( m_atomHotKeyId ) return TRUE; const UINT uiModifiers = 0; const int cMaxTries = 20; bool fFoundHotKey = false; m_atomHotKeyId = GlobalAddAtom( g_szHotKeyAtom ); // Try a buch if different hot keys incase its already registered for ( int i = 0; i < cMaxTries; i++, m_vkHotKey-- ) { if ( RegisterHotKey(m_hwndMessageWindow, m_atomHotKeyId, uiModifiers, m_vkHotKey ) ) { DBPRINTF( TEXT("HotKey found\r\n") ); fFoundHotKey = true; break; } } // only report an error if it the last try if ( !fFoundHotKey ) { DBPRINTF( TEXT("RegisterHotKey failed, error = %d\r\n"), GetLastError() ); GlobalDeleteAtom( m_atomHotKeyId ); m_atomHotKeyId = 0; return FALSE; } return TRUE; } void CSetForegroundWindowHelper::UnRegHotKey() { if ( m_atomHotKeyId ) { UnregisterHotKey( m_hwndMessageWindow, m_atomHotKeyId ); GlobalDeleteAtom( m_atomHotKeyId ); m_atomHotKeyId = 0; m_vkHotKey = HOT_KEY; } } // create a message only window this is just used to get a hotkey message BOOL CSetForegroundWindowHelper::CreateHiddenWindow() { WNDCLASSEX wc; ZeroMemory( &wc, sizeof(WNDCLASSEX) ); wc.cbSize = sizeof(WNDCLASSEX); wc.lpfnWndProc = MessageWindowProc; wc.lpszClassName = g_szMessageWindowClass; if( 0 == RegisterClassEx( &wc ) ) { DWORD dwError = GetLastError(); if ( ERROR_CLASS_ALREADY_EXISTS != dwError ) { DBPRINTF( TEXT("Register window class failed, error = %d\r\n"), dwError); return FALSE; } } m_hwndMessageWindow = CreateWindowEx(0, g_szMessageWindowClass, g_szMessageWindowClass, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, NULL, NULL); if( !m_hwndMessageWindow ) { DBPRINTF( TEXT("CreateWindowEx failed, error = %d\r\n"), GetLastError() ); return FALSE; } return TRUE; } void CSetForegroundWindowHelper::Reset() { UnRegHotKey(); if ( m_hwndMessageWindow ) { DestroyWindow( m_hwndMessageWindow ); m_hwndMessageWindow = NULL; } } CSetForegroundWindowHelper g_GetFocus; LRESULT CALLBACK MessageWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { return g_GetFocus.WinProc( hWnd, Msg, wParam, lParam ); } //////////////////////////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // // ClickOnTheRect // // This function takes a pointer to a rectangle that contains coordinates // in the form (top,left) (width,height). These are screen coordinates. It // then finds the center of that rectangle and checks that the window handle // given is in fact the window at that point. If so, it uses the SendInput // function to move the mouse to the center of the rectangle, do a single // click of the default button, and then move the cursor back where it // started. In order to be super-robust, it checks the Async state of the // shift keys (Shift, Ctrl, and Alt) and turns them off while doing the // click, then back on if they were on. if fDblClick is TRUE, it will do // a double click instead of a single click. // // We have to make sure we are not interrupted while doing this! // // Returns TRUE if it did it, FALSE if there was some bad error. // // -------------------------------------------------------------------------- // this is for ClickOnTheRect typedef struct tagMOUSEINFO { int MouseThresh1; int MouseThresh2; int MouseSpeed; } MOUSEINFO, FAR* LPMOUSEINFO; BOOL ClickOnTheRect(LPRECT lprcLoc,HWND hwndToCheck,BOOL fDblClick) { POINT ptCursor; POINT ptClick; HWND hwndAtPoint; MOUSEINFO miSave; MOUSEINFO miNew; int nButtons; INPUT rgInput[6]; int i; DWORD dwMouseDown; DWORD dwMouseUp; // Find Center of rect ptClick.x = lprcLoc->left + (lprcLoc->right/2); ptClick.y = lprcLoc->top + (lprcLoc->bottom/2); // check if hwnd at point is same as hwnd to check hwndAtPoint = WindowFromPoint (ptClick); if (hwndAtPoint != hwndToCheck) return FALSE; MyBlockInput (TRUE); // Get current cursor pos. GetCursorPos(&ptCursor); if (GetSystemMetrics(SM_SWAPBUTTON)) { dwMouseDown = MOUSEEVENTF_RIGHTDOWN; dwMouseUp = MOUSEEVENTF_RIGHTUP; } else { dwMouseDown = MOUSEEVENTF_LEFTDOWN; dwMouseUp = MOUSEEVENTF_LEFTUP; } // Get delta to move to center of rectangle from current // cursor location. ptCursor.x = ptClick.x - ptCursor.x; ptCursor.y = ptClick.y - ptCursor.y; // NOTE: For relative moves, USER actually multiplies the // coords by any acceleration. But accounting for it is too // hard and wrap around stuff is weird. So, temporarily turn // acceleration off; then turn it back on after playback. // Save mouse acceleration info if (!SystemParametersInfo(SPI_GETMOUSE, 0, &miSave, 0)) { MyBlockInput (FALSE); return (FALSE); } if (miSave.MouseSpeed) { miNew.MouseThresh1 = 0; miNew.MouseThresh2 = 0; miNew.MouseSpeed = 0; if (!SystemParametersInfo(SPI_SETMOUSE, 0, &miNew, 0)) { MyBlockInput (FALSE); return (FALSE); } } // Get # of buttons nButtons = GetSystemMetrics(SM_CMOUSEBUTTONS); // Get state of shift keys and if they are down, send an up // when we're done BOOL fCtrlPressed = GetKeyState(VK_CONTROL) & 0x8000; BOOL fAltPressed = GetKeyState(VK_MENU) & 0x8000; BOOL fShiftPressed = GetKeyState(VK_SHIFT) & 0x8000; if (fCtrlPressed) SendKey (KEYRELEASE,VK_VIRTUAL,VK_CONTROL,0); if (fAltPressed) SendKey (KEYRELEASE,VK_VIRTUAL,VK_MENU,0); if (fShiftPressed) SendKey (KEYRELEASE,VK_VIRTUAL,VK_SHIFT,0); DWORD time = GetTickCount(); // mouse move to center of start button rgInput[0].type = INPUT_MOUSE; rgInput[0].mi.dwFlags = MOUSEEVENTF_MOVE; rgInput[0].mi.dwExtraInfo = 0; rgInput[0].mi.dx = ptCursor.x; rgInput[0].mi.dy = ptCursor.y; rgInput[0].mi.mouseData = nButtons; rgInput[0].mi.time = time; i = 1; DBL_CLICK: // Mouse click down, left button rgInput[i].type = INPUT_MOUSE; rgInput[i].mi.dwFlags = dwMouseDown; rgInput[i].mi.dwExtraInfo = 0; rgInput[i].mi.dx = 0; rgInput[i].mi.dy = 0; rgInput[i].mi.mouseData = nButtons; rgInput[i].mi.time = time; i++; // Mouse click up, left button rgInput[i].type = INPUT_MOUSE; rgInput[i].mi.dwFlags = dwMouseUp; rgInput[i].mi.dwExtraInfo = 0; rgInput[i].mi.dx = 0; rgInput[i].mi.dy = 0; rgInput[i].mi.mouseData = nButtons; rgInput[i].mi.time = time; i++; if (fDblClick) { fDblClick = FALSE; goto DBL_CLICK; } // move mouse back to starting location rgInput[i].type = INPUT_MOUSE; rgInput[i].mi.dwFlags = MOUSEEVENTF_MOVE; rgInput[i].mi.dwExtraInfo = 0; rgInput[i].mi.dx = -ptCursor.x; rgInput[i].mi.dy = -ptCursor.y; rgInput[i].mi.mouseData = nButtons; rgInput[i].mi.time = time; i++; if (!MySendInput(i, rgInput,sizeof(INPUT))) MessageBeep(0); // send shift key down events if they were down before if (fCtrlPressed) SendKey (KEYPRESS,VK_VIRTUAL,VK_CONTROL,0); if (fAltPressed) SendKey (KEYPRESS,VK_VIRTUAL,VK_MENU,0); if (fShiftPressed) SendKey (KEYPRESS,VK_VIRTUAL,VK_SHIFT,0); // // Restore Mouse Acceleration // if (miSave.MouseSpeed) SystemParametersInfo(SPI_SETMOUSE, 0, &miSave, 0); MyBlockInput (FALSE); return TRUE; } //-------------------------------------------------------------------------- // // SendKey // // This is a private function. Sends the key event specified by // the parameters - down or up, plus a virtual key code or character. // // Parameters: // nEvent either KEYPRESS or KEYRELEASE // nKeyType either VK_VIRTUAL or VK_CHAR // wKeyCode a Virtual Key code if KeyType is VK_VIRTUAL, // ignored otherwise // cChar a Character if KeyType is VK_CHAR, ignored otherwise. // // Returns: // BOOL indicating success (TRUE) or failure (FALSE) //-------------------------------------------------------------------------- BOOL SendKey (int nEvent,int nKeyType,WORD wKeyCode,TCHAR cChar) { INPUT Input; Input.type = INPUT_KEYBOARD; if (nKeyType == VK_VIRTUAL) { Input.ki.wVk = wKeyCode; Input.ki.wScan = LOWORD(MapVirtualKey(wKeyCode,0)); } else // must be a character { Input.ki.wVk = VkKeyScan (cChar); Input.ki.wScan = LOWORD(OemKeyScan (cChar)); } Input.ki.dwFlags = nEvent; Input.ki.time = GetTickCount(); Input.ki.dwExtraInfo = 0; return MySendInput(1, &Input,sizeof(INPUT)); } // -------------------------------------------------------------------------- // // MyGetFocus() // // Gets the focus on this window's VWI. // // -------------------------------------------------------------------------- HWND MyGetFocus() { GUITHREADINFO gui; // // Use the foreground thread. If nobody is the foreground, nobody has // the focus either. // if (!MyGetGUIThreadInfo(0, &gui)) return(NULL); return(gui.hwndFocus); } // -------------------------------------------------------------------------- // // MySetFocus() // // Attempts to set the focused window. // Since SetFocus only works on HWNDs owned by the calling thread, // we use SetActiveWindow instead. // // -------------------------------------------------------------------------- void MySetFocus( HWND hwnd ) { HWND hwndParent = hwnd; BOOL fWindowEnabled = TRUE; HWND hwndDesktop = GetDesktopWindow(); while ( hwndParent != hwndDesktop ) { fWindowEnabled = IsWindowEnabled( hwndParent ); if ( !fWindowEnabled ) break; hwndParent = MyGetAncestor(hwndParent, GA_PARENT ); } if ( fWindowEnabled ) { // This is freaky, but seems to work. // There are some cases where it doesn't quite work, though: // * Won't focus the Address: combo in an IE/Explorer window // * Needs to check that the window is enabled first! Possible // to set focus to a hwnd that is disabled because it has a // modal dialog showing. // First, use SetForegroundWindow on the target window... // This can do weird things if its a child window - it looks // like the top-level window doesn't get activated properly... g_GetFocus.SetForegroundWindow( hwnd ); // Now call SetForegroundWindow on the top-level window. This fixes the // activation, but actually leaves the focus on the child window. HWND hTopLevel = MyGetAncestor( hwnd, GA_ROOT ); if( hTopLevel ) { SetForegroundWindow( hTopLevel ); } } } // -------------------------------------------------------------------------- // // MyGetRect // // This initializes the rectangle to empty, then makes a GetClientRect() // or GetWindowRect() call. These APIs will leave the rect alone if they // fail, hence the zero'ing out ahead of time. They don't return a useful // value in Win '95. // // -------------------------------------------------------------------------- void MyGetRect(HWND hwnd, LPRECT lprc, BOOL fWindowRect) { SetRectEmpty(lprc); if (fWindowRect) GetWindowRect(hwnd, lprc); else GetClientRect(hwnd, lprc); } // -------------------------------------------------------------------------- // // TCharSysAllocString // // Pillaged from SHELL source, does ANSI BSTR stuff. // // -------------------------------------------------------------------------- BSTR TCharSysAllocString(LPTSTR pszString) { #ifdef UNICODE return SysAllocString(pszString); #else LPOLESTR pwszOleString; BSTR bstrReturn; int cChars; // do the call first with 0 to get the size needed cChars = MultiByteToWideChar(CP_ACP, 0, pszString, -1, NULL, 0); pwszOleString = (LPOLESTR)LocalAlloc(LPTR,sizeof(OLECHAR)*cChars); if (pwszOleString == NULL) { return NULL; } cChars = MultiByteToWideChar(CP_ACP, 0, pszString, -1, pwszOleString, cChars); bstrReturn = SysAllocString(pwszOleString); LocalFree (pwszOleString); return bstrReturn; #endif } // -------------------------------------------------------------------------- // // HrCreateString // // Loads a string from the resource file and makes a BSTR from it. // // -------------------------------------------------------------------------- #define CCH_STRING_MAX 256 HRESULT HrCreateString(int istr, BSTR* pszResult) { TCHAR szString[CCH_STRING_MAX]; Assert(pszResult); *pszResult = NULL; if (!LoadString(hinstResDll, istr, szString, CCH_STRING_MAX)) return(E_OUTOFMEMORY); *pszResult = TCharSysAllocString(szString); if (!*pszResult) return(E_OUTOFMEMORY); return(S_OK); } // -------------------------------------------------------------------------- // // GetLocationRect // // Get a RECT location from an IAccessible. Converts accLocation's width and // height to right and bottom coords. // // -------------------------------------------------------------------------- HRESULT GetLocationRect( IAccessible * pAcc, VARIANT & varChild, RECT * prc ) { HRESULT hr = pAcc->accLocation( & prc->left, & prc->top, & prc->right, & prc->bottom, varChild ); if( hr == S_OK ) { // convert width/height to right/bottom... prc->right += prc->left; prc->bottom += prc->top; } return hr; } // -------------------------------------------------------------------------- // // IsClippedByWindow // // Returns TRUE if a given IAccesible/varChild is completely outside the // rectangle of a given HWND. // // (When varChildID is not CHILDID_SELF, and when the HWND is the HWND of // the IAccessible, then it means that the item is clipped by its parent.) // // -------------------------------------------------------------------------- BOOL IsClippedByWindow( IAccessible * pAcc, VARIANT & varChild, HWND hwnd ) { RECT rcItem; if( GetLocationRect( pAcc, varChild, & rcItem ) != S_OK ) return FALSE; RECT rcWindow; GetClientRect( hwnd, & rcWindow ); MapWindowPoints( hwnd, NULL, (POINT *) & rcWindow, 2 ); return Rect1IsOutsideRect2( rcItem, rcWindow ); } // // Why not use the stdlib? Well, we want to do this all in Unicode, and // work on 9x... (even when built as ANSI) // static void ParseInt( LPCWSTR pStart, LPCWSTR * ppEnd, int * pInt ) { // Allow single leading + or -... BOOL fIsNeg = FALSE; if( *pStart == '-' ) { fIsNeg = TRUE; pStart++; } else if( *pStart == '+' ) { pStart++; } // Skip possible leading 0... if( *pStart == '0' ) { pStart++; } // Possible 'x' indicating hex number... int base = 10; if( *pStart == 'x' || *pStart == 'X' ) { base = 16; pStart++; } // Numbers all the way from here... // Note - this doesn't handle overflow/wraparound, nor the // extremities of the range (eg. max and min possible #'s...) int x = 0; for( ; ; ) { int digit; if( *pStart >= '0' && *pStart <= '9' ) { digit = *pStart - '0'; } else if( *pStart >= 'a' && *pStart <= 'f' ) { digit = *pStart - 'a' + 10; } else if( *pStart >= 'A' && *pStart <= 'F' ) { digit = *pStart - 'A' + 10; } else { // invalid digit break; } if( digit >= base ) { // digit not appropriate for this base break; } pStart++; x = ( x * base ) + digit; } if( fIsNeg ) { x = -x; } *pInt = x; *ppEnd = pStart; } static void ParseString( LPCWSTR pStart, LPCWSTR * ppEnd, WCHAR wcSep ) { while( *pStart != '\0' && *pStart != wcSep ) { pStart++; } *ppEnd = pStart; } static BOOL StrEquW( LPCWSTR pStrA, LPCWSTR pStrB ) { while( *pStrA && *pStrA == *pStrB ) { pStrA++; pStrB++; } return *pStrA == *pStrB; } // Format of map: // // Type A - currently only supported string. // Separator can be any character (except NUL - must be a legal // string character. Doesn't make sense to use space... also can't // be a numeric digit...) // // "A:0:String0:1:String1:2:String2:3:String3:" // // or... // // "TypeA 0='String0' 1='String1' 2='String2' 3='String3'" // // How to deal with quotes? // FALSE -> Value not found in map. // TRUE -> Value found, ppStart, ppEnd point to end points of corresponding entry static BOOL ParseValueMap( LPCWSTR pWMapStr, int * aKeys, int cKeys, LPCWSTR * ppStrStart, LPCWSTR * ppStrEnd ) { // Check header for Type-A signature // Note - I've used plain ANSI literals below - eg. 'A' instead of // L'A' - this is ok, since the compiler will promote these to Unicode // before doing the comparison. // Check for leading 'A'... if( *pWMapStr != 'A' ) { return FALSE; } pWMapStr++; // Check for separator. WCHAR wcSeparator = *pWMapStr; if( wcSeparator == '\0' ) { return FALSE; } pWMapStr++; // The first item indicates which source key we are using... int iKey; LPCWSTR pWStartOfInt = pWMapStr; ParseInt( pWMapStr, & pWMapStr, & iKey ); if( pWMapStr == pWStartOfInt ) { // missing number return FALSE; } // Check for separator... if( *pWMapStr != wcSeparator ) { return FALSE; } pWMapStr++; // Is index within range? if( iKey >= cKeys ) { return FALSE; } // Now we know what the key is in the key-value map... int TargetValue = aKeys[ iKey ]; // We don't explicitly check for the terminating NUL in the map string here - // however, both ParseInt and ParseString will stop at it, and we'll then // check that it's the separator - which will fail, so we'll exit with FALSE. for( ; ; ) { int x; LPCWSTR pWStartOfInt = pWMapStr; ParseInt( pWMapStr, & pWMapStr, & x ); if( pWMapStr == pWStartOfInt ) { // missing number return FALSE; } // Check for separator... if( *pWMapStr != wcSeparator ) { return FALSE; } pWMapStr++; LPCWSTR pStrStart = pWMapStr; ParseString( pWMapStr, & pWMapStr, wcSeparator ); LPCWSTR pStrEnd = pWMapStr; // Check for separator... if( *pWMapStr != wcSeparator ) { return FALSE; } pWMapStr++; // Found it... if( TargetValue == x ) { *ppStrStart = pStrStart; *ppStrEnd = pStrEnd; return TRUE; } } } BOOL CheckStringMap( HWND hwnd, DWORD idObject, DWORD idChild, PROPINDEX idxProp, int * pKeys, int cKeys, BSTR * pbstr, BOOL fAllowUseRaw, BOOL * pfGotUseRaw ) { VARIANT varMap; BYTE HwndKey[ HWNDKEYSIZE ]; MakeHwndKey( HwndKey, hwnd, idObject, idChild ); if( ! PropMgrClient_LookupProp( HwndKey, HWNDKEYSIZE, idxProp, & varMap ) ) { return FALSE; } if( varMap.vt != VT_BSTR ) { VariantClear( & varMap ); return FALSE; } if( fAllowUseRaw ) { *pfGotUseRaw = StrEquW( varMap.bstrVal, L"use_raw" ); if( *pfGotUseRaw ) return TRUE; } LPCWSTR pStrStart; LPCWSTR pStrEnd; BOOL fGot = ParseValueMap( varMap.bstrVal, pKeys, cKeys, & pStrStart, & pStrEnd ); SysFreeString( varMap.bstrVal ); if( ! fGot ) { return FALSE; } // Cast for Win64 compile. Subtracting ptrs give 64-bit value; we only // want the 32-bit part... *pbstr = SysAllocStringLen( pStrStart, (UINT)( pStrEnd - pStrStart ) ); if( ! *pbstr ) { return FALSE; } return TRUE; } BOOL CheckDWORDMap( HWND hwnd, DWORD idObject, DWORD idChild, PROPINDEX idxProp, int * pKeys, int cKeys, DWORD * pdw ) { VARIANT varMap; BYTE HwndKey[ HWNDKEYSIZE ]; MakeHwndKey( HwndKey, hwnd, idObject, idChild ); if( ! PropMgrClient_LookupProp( HwndKey, HWNDKEYSIZE, idxProp, & varMap ) ) { return FALSE; } if( varMap.vt != VT_BSTR ) { VariantClear( & varMap ); return FALSE; } LPCWSTR pStrStart; LPCWSTR pStrEnd; BOOL fGot = ParseValueMap( varMap.bstrVal, pKeys, cKeys, & pStrStart, & pStrEnd ); SysFreeString( varMap.bstrVal ); if( ! fGot ) { return FALSE; } int i; LPCWSTR pIntEnd; ParseInt( pStrStart, & pIntEnd, & i ); if( pIntEnd == pStrStart || pIntEnd != pStrEnd ) { // invalid number... return FALSE; } *pdw = (DWORD) i; return TRUE; } #define MAX_NAME_SIZE 128 struct EnumThreadWindowInfo { HWND hwndCtl; DWORD dwIDCtl; TCHAR * pszName; }; static HRESULT TryTooltip( HWND hwndToolTip, LPTSTR pszName, HWND hwndCtl, DWORD dwIDCtl ) { TOOLINFO ti; ti.cbSize = SIZEOF_TOOLINFO; ti.lpszText = pszName; ti.hwnd = hwndCtl; ti.uId = dwIDCtl; *pszName = '\0'; HRESULT hr = XSend_ToolTip_GetItem( hwndToolTip, TTM_GETTEXT, 0, & ti, MAX_NAME_SIZE ); if( hr != S_OK ) return hr; return S_OK; } static BOOL CALLBACK EnumThreadWindowsProc( HWND hWnd, LPARAM lParam ) { EnumThreadWindowInfo * pInfo = (EnumThreadWindowInfo *) lParam; // Is this a tooltip window? TCHAR szClass[ 64 ]; if( ! GetClassName( hWnd, szClass, ARRAYSIZE( szClass ) ) ) return TRUE; if( lstrcmpi( szClass, TEXT("tooltips_class32") ) != 0 ) return TRUE; if( TryTooltip( hWnd, pInfo->pszName, pInfo->hwndCtl, pInfo->dwIDCtl ) != S_OK ) return TRUE; // Didn't get anything - continue looking... if( pInfo->pszName[ 0 ] == '\0' ) return TRUE; // Got it - can stop iterating now. return FALSE; } BOOL GetTooltipStringForControl( HWND hwndCtl, UINT uGetTooltipMsg, DWORD dwIDCtl, LPTSTR * ppszName ) { TCHAR szName[ MAX_NAME_SIZE ]; BOOL fTryScanningForTooltip = TRUE; if( uGetTooltipMsg ) { HWND hwndToolTip = (HWND) SendMessage( hwndCtl, uGetTooltipMsg, 0, 0 ); if( hwndToolTip ) { // We've found the tooltip window, so we won't need to scan for it. // Instead, when we exit this if, we'll fall through to the code that // post-processed the name we've got... fTryScanningForTooltip = FALSE; // Got a tooltip window - use it. // (Otherwise we fall through to scanning for a corresponding tooltip // window...) TOOLINFO ti; szName[ 0 ] = '\0'; ti.cbSize = SIZEOF_TOOLINFO; ti.lpszText = szName; ti.hwnd = hwndCtl; ti.uId = dwIDCtl; HRESULT hr = XSend_ToolTip_GetItem( hwndToolTip, TTM_GETTEXT, 0, & ti, MAX_NAME_SIZE ); if( hr != S_OK ) return FALSE; // Fall through and post-process the string... } } if( fTryScanningForTooltip ) { // Control doesn't know its tooltip window - instead scan for one... // Enum the top-level windows owned by this thread... DWORD pid; DWORD tid = GetWindowThreadProcessId( hwndCtl, & pid ); EnumThreadWindowInfo info; info.hwndCtl = hwndCtl; info.dwIDCtl = dwIDCtl; info.pszName = szName; info.pszName[ 0 ] = '\0'; EnumThreadWindows( tid, EnumThreadWindowsProc, (LPARAM) & info ); } // At this stage we might have gotten a name from some tooltip window - // check if there's anything there... if( szName[ 0 ] == '\0' ) return FALSE; int len = lstrlen( szName ) + 1; // +1 for terminating NUL *ppszName = (LPTSTR)LocalAlloc( LPTR, len * sizeof(TCHAR) ); if( ! * ppszName ) return FALSE; memcpy( *ppszName, szName, len * sizeof(TCHAR) ); return TRUE; } // This function also resets the stream pointer to the beginning static HRESULT RewindStreamAndGetSize( LPSTREAM pstm, PDWORD pcbSize ) { *pcbSize = 0; // If anything fails, 0 is returned LARGE_INTEGER li = { 0, 0 }; HRESULT hr = pstm->Seek( li, STREAM_SEEK_SET, NULL ); if( FAILED(hr) ) { TraceErrorHR( hr, TEXT("RewindStreamAndGetSize: pstm->Seek() failed") ); return hr; } // Get the number of bytes in the stream STATSTG statstg; hr = pstm->Stat( & statstg, STATFLAG_NONAME ); if( FAILED(hr) ) { TraceErrorHR( hr, TEXT("RewindStreamAndGetSize: pstm->Stat() failed") ); return hr; } *pcbSize = statstg.cbSize.LowPart; return S_OK; } // Marshals an interface, returning pointer to marshalled buffer. // When done, caller must call MarshalInterfaceDone(). HRESULT MarshalInterface( REFIID riid, IUnknown * punk, DWORD dwDestContext, DWORD mshlflags, const BYTE ** ppData, DWORD * pDataLen, MarshalState * pMarshalState ) { IStream * pStm = NULL; HRESULT hr = CreateStreamOnHGlobal( NULL, TRUE, & pStm ); if( FAILED( hr ) || ! pStm ) { TraceErrorHR( hr, TEXT("MarshalInterface: CreateStreamOnHGlobal failed") ); return FAILED( hr ) ? hr : E_FAIL; } // We use strong table marshalling to keep the object alive until we release it. hr = CoMarshalInterface( pStm, riid, punk, dwDestContext, NULL, mshlflags ); if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("MarshalInterface: CoMarshalInterface failed") ); pStm->Release(); return hr; } HGLOBAL hGlobal = NULL; hr = GetHGlobalFromStream( pStm, & hGlobal ); if( FAILED( hr ) || ! hGlobal ) { TraceErrorHR( hr, TEXT("MarshalInterface: GetHGlobalFromStream failed") ); LARGE_INTEGER li = { 0, 0 }; pStm->Seek(li, STREAM_SEEK_SET, NULL); CoReleaseMarshalData( pStm ); pStm->Release(); return FAILED( hr ) ? hr : E_FAIL; } DWORD dwDataLen = 0; hr = RewindStreamAndGetSize( pStm, & dwDataLen ); if( FAILED( hr ) || dwDataLen == 0 ) { CoReleaseMarshalData( pStm ); pStm->Release(); return FAILED( hr ) ? hr : E_FAIL; } BYTE * pData = (BYTE *) GlobalLock( hGlobal ); if( ! pData ) { TraceErrorW32( TEXT("MarshalInterface: GlobalLock failed") ); CoReleaseMarshalData( pStm ); pStm->Release(); return E_FAIL; } *ppData = pData; *pDataLen = dwDataLen; pMarshalState->pstm = pStm; pMarshalState->hGlobal = hGlobal; return S_OK; } void MarshalInterfaceDone( MarshalState * pMarshalState ) { // Unlock the HGLOBAL *before* we release the stream... GlobalUnlock( pMarshalState->hGlobal ); pMarshalState->pstm->Release(); } HRESULT ReleaseMarshallData( const BYTE * pMarshalData, DWORD dwMarshalDataLen ) { IStream * pStm = NULL; HRESULT hr = CreateStreamOnHGlobal( NULL, TRUE, & pStm ); if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("ReleaseMarshallData: CreateStreamOnHGlobal failed") ); return hr; } if( pStm == NULL ) { TraceErrorHR( hr, TEXT("ReleaseMarshallData: CreateStreamOnHGlobal returned NULL") ); return E_FAIL; } hr = pStm->Write( pMarshalData, dwMarshalDataLen, NULL ); if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("ReleaseMarshallData: pStm->Write() failed") ); pStm->Release(); return hr; } LARGE_INTEGER li = { 0, 0 }; hr = pStm->Seek( li, STREAM_SEEK_SET, NULL ); if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("ReleaseMarshallData: pStm->Seek() failed") ); pStm->Release(); return hr; } hr = CoReleaseMarshalData( pStm ); pStm->Release(); if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("ReleaseMarshallData: CoReleaseMarshalData failed") ); // Nothing we can do about this, so return S_OK anyway... } return S_OK; } HRESULT UnmarshalInterface( const BYTE * pData, DWORD cbData, REFIID riid, LPVOID * ppv ) { // Allocate memory for data HGLOBAL hGlobal = GlobalAlloc( GMEM_MOVEABLE, cbData ); if( hGlobal == NULL ) { TraceErrorW32( TEXT("UnmarshalInterface: GlobalAlloc failed") ); return E_OUTOFMEMORY; } VOID * pv = GlobalLock( hGlobal ); if( ! pv ) { TraceErrorW32( TEXT("UnmarshalInterface: GlobalLock failed") ); GlobalFree( hGlobal ); return E_FAIL; } memcpy( pv, pData, cbData ); GlobalUnlock( hGlobal ); // Create a stream out of the data buffer IStream * pstm; // TRUE => Delete HGLOBAL on release HRESULT hr = CreateStreamOnHGlobal( hGlobal, TRUE, & pstm ); if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("UnmarshalInterface: CreateStreamOnHGlobal failed") ); GlobalFree( hGlobal ); return hr; } hr = CoUnmarshalInterface( pstm, riid, ppv ); if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("UnmarshalInterface: CoUnmarshalInterface failed") ); } pstm->Release(); return hr; }