From a8589b39bf0b7b51c7990376be929b47d72a4df1 Mon Sep 17 00:00:00 2001 From: Abs62 Date: Sat, 9 Jul 2011 23:26:30 +0400 Subject: [PATCH] Improvements in scan popup functionality. 1. Add search word under cursor through IAccessibleEx interface and UI Automation technology. 2. Reorganize GoldenDict main program and scan libraries interaction to reduce influence to other programs. 3. Fix crash in scan libraries in IE9 protected mode. --- config.cc | 18 ++ config.hh | 3 + goldendict.pro | 13 +- guids.c | 9 + mouseover.cc | 197 ++++++++++++---- mouseover.hh | 11 + mouseover_win32/GetWordByIAccEx.cc | 108 +++++++++ mouseover_win32/GetWordByIAccEx.h | 19 ++ mouseover_win32/HookImportFunction.c | 6 +- mouseover_win32/IAccEx.cc | 90 ++++++++ mouseover_win32/IAccExInt.h | 157 +++++++++++++ mouseover_win32/Makefile | 16 +- mouseover_win32/TextOutHook.c | 5 +- mouseover_win32/TextOutSpy.c | 71 ++++-- mouseover_win32/ThTypes.c | 6 +- mouseover_win32/ThTypes.h | 5 + mouseover_win32/guids.c | 8 + preferences.cc | 11 + preferences.ui | 95 +++++++- scanpopup.cc | 29 +++ scanpopup.hh | 5 + uiauto.hh | 330 +++++++++++++++++++++++++++ wordbyauto.cc | 114 +++++++++ wordbyauto.hh | 6 + 24 files changed, 1259 insertions(+), 73 deletions(-) create mode 100644 guids.c create mode 100644 mouseover_win32/GetWordByIAccEx.cc create mode 100644 mouseover_win32/GetWordByIAccEx.h create mode 100644 mouseover_win32/IAccEx.cc create mode 100644 mouseover_win32/IAccExInt.h create mode 100644 mouseover_win32/guids.c create mode 100644 uiauto.hh create mode 100644 wordbyauto.cc create mode 100644 wordbyauto.hh diff --git a/config.cc b/config.cc index c8537a19..addca5cd 100644 --- a/config.cc +++ b/config.cc @@ -102,6 +102,9 @@ Preferences::Preferences(): scanPopupModifiers( 0 ), scanPopupAltMode( false ), scanPopupAltModeSecs( 3 ), + scanPopupUseUIAutomation( false ), + scanPopupUseIAccessibleEx( false ), + scanPopupUseGDMessage( false ), pronounceOnLoadMain( false ), pronounceOnLoadPopup( false ), #ifdef Q_WS_WIN @@ -597,6 +600,9 @@ Class load() throw( exError ) c.preferences.scanPopupAltMode = ( preferences.namedItem( "scanPopupAltMode" ).toElement().text() == "1" ); if ( !preferences.namedItem( "scanPopupAltModeSecs" ).isNull() ) c.preferences.scanPopupAltModeSecs = preferences.namedItem( "scanPopupAltModeSecs" ).toElement().text().toUInt(); + c.preferences.scanPopupUseUIAutomation = ( preferences.namedItem( "scanPopupUseUIAutomation" ).toElement().text() == "1" ); + c.preferences.scanPopupUseIAccessibleEx = ( preferences.namedItem( "scanPopupUseIAccessibleEx" ).toElement().text() == "1" ); + c.preferences.scanPopupUseGDMessage = ( preferences.namedItem( "scanPopupUseGDMessage" ).toElement().text() == "1" ); c.preferences.pronounceOnLoadMain = ( preferences.namedItem( "pronounceOnLoadMain" ).toElement().text() == "1" ); c.preferences.pronounceOnLoadPopup = ( preferences.namedItem( "pronounceOnLoadPopup" ).toElement().text() == "1" ); @@ -1106,6 +1112,18 @@ void save( Class const & c ) throw( exError ) opt.appendChild( dd.createTextNode( QString::number( c.preferences.scanPopupAltModeSecs ) ) ); preferences.appendChild( opt ); + opt = dd.createElement( "scanPopupUseUIAutomation" ); + opt.appendChild( dd.createTextNode( c.preferences.scanPopupUseUIAutomation ? "1":"0" ) ); + preferences.appendChild( opt ); + + opt = dd.createElement( "scanPopupUseIAccessibleEx" ); + opt.appendChild( dd.createTextNode( c.preferences.scanPopupUseIAccessibleEx ? "1":"0" ) ); + preferences.appendChild( opt ); + + opt = dd.createElement( "scanPopupUseGDMessage" ); + opt.appendChild( dd.createTextNode( c.preferences.scanPopupUseGDMessage ? "1":"0" ) ); + preferences.appendChild( opt ); + opt = dd.createElement( "pronounceOnLoadMain" ); opt.appendChild( dd.createTextNode( c.preferences.pronounceOnLoadMain ? "1" : "0" ) ); preferences.appendChild( opt ); diff --git a/config.hh b/config.hh index 5089f33c..ff4dc0dc 100644 --- a/config.hh +++ b/config.hh @@ -159,6 +159,9 @@ struct Preferences unsigned long scanPopupModifiers; // Combination of KeyboardState::Modifier bool scanPopupAltMode; // When you press modifier shortly after the selection unsigned scanPopupAltModeSecs; + bool scanPopupUseUIAutomation; + bool scanPopupUseIAccessibleEx; + bool scanPopupUseGDMessage; // Whether the word should be pronounced on page load, in main window/popup bool pronounceOnLoadMain, pronounceOnLoadPopup; diff --git a/goldendict.pro b/goldendict.pro index ec58f41e..ec8ce8af 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -37,7 +37,10 @@ win32 { LIBS += -liconv \ -lwsock32 \ -lwinmm \ - -lpsapi + -lpsapi \ + -lole32 \ + -loleaut32 \ + -ladvapi32 LIBS += -lvorbisfile \ -lvorbis \ -logg \ @@ -255,8 +258,12 @@ SOURCES += folding.cc \ maintabwidget.cc \ mainstatusbar.cc win32 { - SOURCES += mouseover_win32/ThTypes.c - HEADERS += mouseover_win32/ThTypes.h + SOURCES += mouseover_win32/ThTypes.c \ + wordbyauto.cc \ + guids.c + HEADERS += mouseover_win32/ThTypes.h \ + wordbyauto.hh \ + uiauto.hh } RESOURCES += resources.qrc \ flags.qrc diff --git a/guids.c b/guids.c new file mode 100644 index 00000000..4cb2fdc5 --- /dev/null +++ b/guids.c @@ -0,0 +1,9 @@ +#define INITGUID +#include + +DEFINE_GUID(IID_IUIAutomation, 0x30cbe57d, 0xd9d0, 0x452a, 0xab, 0x13, 0x7a, 0xc5, 0xac, 0x48, 0x25, 0xee); +DEFINE_GUID(CLSID_CUIAutomation, 0xff48dba4, 0x60ef, 0x4201, 0xaa, 0x87, 0x54, 0x10, 0x3e, 0xef, 0x59, 0x4e); +DEFINE_GUID(IID_IUIAutomationElement, 0xd22108aa, 0x8ac5, 0x49a5, 0x83, 0x7b, 0x37, 0xbb, 0xb3, 0xd7, 0x59, 0x1e); +DEFINE_GUID(IID_IUIAutomationTextPattern, 0x32eba289, 0x3583, 0x42c9, 0x9c, 0x59, 0x3b, 0x6d, 0x9a, 0x1e, 0x9b, 0x6a); +DEFINE_GUID(IID_IUIAutomationTextRange, 0xa543cc6a, 0xf4ae, 0x494b, 0x82, 0x39, 0xc8, 0x14, 0x48, 0x11, 0x87, 0xa8); +DEFINE_GUID(IID_IUIAutomationTreeWalker, 0x4042c624, 0x389c, 0x4afc, 0xa6, 0x30, 0x9d, 0xf8, 0x54, 0xa5, 0x41, 0xfc); diff --git a/mouseover.cc b/mouseover.cc index 90ec3c6d..545a6f00 100644 --- a/mouseover.cc +++ b/mouseover.cc @@ -5,7 +5,13 @@ #include #ifdef Q_OS_WIN32 +#undef WINVER +#define WINVER 0x0500 +#include +#include +#include #include "mouseover_win32/ThTypes.h" +#include "wordbyauto.hh" #endif MouseOver & MouseOver::instance() @@ -18,12 +24,71 @@ MouseOver & MouseOver::instance() #ifdef Q_OS_WIN32 const UINT WM_MY_SHOW_TRANSLATION = WM_USER + 301; static wchar_t className[] = L"GoldenDictMouseover"; +typedef BOOL WINAPI ( *ChangeWindowMessageFilterFunc )( UINT, DWORD ); + +#ifndef CHANGEFILTERSTRUCT +typedef struct tagCHANGEFILTERSTRUCT { + DWORD cbSize; + DWORD ExtStatus; +} CHANGEFILTERSTRUCT, *PCHANGEFILTERSTRUCT; #endif +typedef BOOL WINAPI ( *ChangeWindowMessageFilterExFunc )( HWND, UINT, DWORD, PCHANGEFILTERSTRUCT ); + +#endif // Q_OS_WIN32 + +#ifdef Q_OS_WIN32 + +extern "C" BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW( + LPCWSTR StringSecurityDescriptor, + DWORD StringSDRevision, + PSECURITY_DESCRIPTOR *SecurityDescriptor, + PULONG SecurityDescriptorSize ); + + +static void SetLowLabelToGDSynchroObjects() +{ +// The LABEL_SECURITY_INFORMATION SDDL SACL to be set for low integrity +#define LOW_INTEGRITY_SDDL_SACL_W L"S:(ML;;NW;;;LW)" + DWORD dwErr = ERROR_SUCCESS; + PSECURITY_DESCRIPTOR pSD = NULL; + + PACL pSacl = NULL; // not allocated + BOOL fSaclPresent = FALSE; + BOOL fSaclDefaulted = FALSE; + LPCWSTR pwszMapFileName = L"GoldenDictTextOutHookSharedMem"; + LPCWSTR pwszSpyMutexName = L"GoldenDictTextOutSpyMutex"; + LPCWSTR pwszHookMutexName = L"GoldenDictTextOutHookMutex"; + + if( ConvertStringSecurityDescriptorToSecurityDescriptorW( LOW_INTEGRITY_SDDL_SACL_W, 1 /* SDDL_REVISION_1 */, &pSD, NULL ) ) + { + if( GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted)) + { +// Note that psidOwner, psidGroup, and pDacl are +// all NULL and set the new LABEL_SECURITY_INFORMATION + + dwErr = SetNamedSecurityInfoW( (LPWSTR)pwszMapFileName, + SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pSacl); + + dwErr = SetNamedSecurityInfoW( (LPWSTR)pwszSpyMutexName, + SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pSacl); + + dwErr = SetNamedSecurityInfoW( (LPWSTR)pwszHookMutexName, + SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pSacl); + + } + LocalFree(pSD); + } +} + +#endif // Q_OS_WIN32 + MouseOver::MouseOver() { #ifdef Q_OS_WIN32 - +HMODULE hm; +ChangeWindowMessageFilterFunc changeWindowMessageFilterFunc = NULL; +ChangeWindowMessageFilterExFunc changeWindowMessageFilterExFunc = NULL; mouseOverEnabled = false; ThTypes_Init(); @@ -37,17 +102,17 @@ MouseOver::MouseOver() wcex.cbSize = sizeof( WNDCLASSEX ); - wcex.style = 0; - wcex.lpfnWndProc = ( WNDPROC ) eventHandler; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = GetModuleHandle( 0 ); - wcex.hIcon = NULL; - wcex.hCursor = NULL, - wcex.hbrBackground = NULL; - wcex.lpszMenuName = NULL; - wcex.lpszClassName = className; - wcex.hIconSm = NULL; + wcex.style = 0; + wcex.lpfnWndProc = ( WNDPROC ) eventHandler; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetModuleHandle( 0 ); + wcex.hIcon = NULL; + wcex.hCursor = NULL, + wcex.hbrBackground = NULL; + wcex.lpszMenuName = NULL; + wcex.lpszClassName = className; + wcex.hIconSm = NULL; RegisterClassEx( &wcex ); @@ -58,6 +123,23 @@ MouseOver::MouseOver() if ( spyDll ) activateSpyFn = ( ActivateSpyFn ) GetProcAddress( spyDll, "ActivateTextOutSpying" ); +// Allow messages from low intehrity process - for Vista and Win7 + hm = GetModuleHandle( __TEXT("user32.dll")); + if ( hm != NULL ) { + changeWindowMessageFilterExFunc = (ChangeWindowMessageFilterExFunc)GetProcAddress( hm, "ChangeWindowMessageFilterEx" ); + if( changeWindowMessageFilterExFunc ) { + CHANGEFILTERSTRUCT cfs = { sizeof( CHANGEFILTERSTRUCT ), 0 }; + changeWindowMessageFilterExFunc( GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 1 /* MSGFLT_ALLOW */, &cfs ); + } else { + changeWindowMessageFilterFunc = (ChangeWindowMessageFilterFunc)GetProcAddress( hm, "ChangeWindowMessageFilter" ); + if( changeWindowMessageFilterFunc ) + changeWindowMessageFilterFunc( WM_MY_SHOW_TRANSLATION, 1 /* MSGFLT_ADD */ ); + } + } + +//Allow object access from low intehrity process - for Vista and Win7 + SetLowLabelToGDSynchroObjects(); + #endif } @@ -90,46 +172,72 @@ LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg, { if ( msg == WM_MY_SHOW_TRANSLATION ) { - // Don't handle word without necessity + /// Don't handle word without necessity + bool bNeed = false; + emit instance().askNeedWord( &bNeed ); + if( !bNeed ) return 0; - bool bNeedHandle = false; - emit instance().askNeedWord( &bNeedHandle ); - if( !bNeedHandle ) return 0; + if( wparam != 0) { //Ask for methods of word retrieving + LRESULT ret = GD_FLAG_METHOD_STANDARD; + emit instance().askUseGDMessage( &bNeed ); + if( bNeed ) + ret |= GD_FLAG_METHOD_GD_MESSAGE; + emit instance().askUseIAccessibleEx( &bNeed ); + if( bNeed ) + ret |= GD_FLAG_METHOD_IACCESSIBLEEX; + emit instance().askUseUIAutomation( &bNeed ); + if( bNeed ) + ret |= GD_FLAG_METHOD_UI_AUTOMATION; + return ret; + } - int wordSeqPos; + int wordSeqPos = 0; QString wordSeq; - // Is the string in utf8 or in locale encoding? - - gd::wchar testBuf[ 256 ]; - - long result = Utf8::decode( GlobalData->CurMod.MatchedWord, - strlen( GlobalData->CurMod.MatchedWord ), - testBuf ); - - if ( result >= 0 ) + if( GlobalData->CurMod.WordLen == 0) { - // It seems to be - QString begin = QString::fromUtf8( GlobalData->CurMod.MatchedWord, - GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); - - QString end = QString::fromUtf8( GlobalData->CurMod.MatchedWord + - GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); - - wordSeq = begin + end; - wordSeqPos = begin.size(); + emit instance().askUseUIAutomation( &bNeed ); + if( !bNeed ) return 0; + POINT pt = GlobalData->CurMod.Pt; + WCHAR *pwstr = gdGetWordAtPointByAutomation( pt ); + if( pwstr == NULL ) return 0; + wordSeq = QString::fromWCharArray( pwstr ); } else { - // It's not, so interpret it as in local encoding - QString begin = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord, - GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); - QString end = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord + - GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + // Is the string in utf8 or in locale encoding? - wordSeq = begin + end; - wordSeqPos = begin.size(); + gd::wchar testBuf[ 256 ]; + + long result = Utf8::decode( GlobalData->CurMod.MatchedWord, + strlen( GlobalData->CurMod.MatchedWord ), + testBuf ); + + if ( result >= 0 ) + { + // It seems to be + QString begin = QString::fromUtf8( GlobalData->CurMod.MatchedWord, + GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + + QString end = QString::fromUtf8( GlobalData->CurMod.MatchedWord + + GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + + wordSeq = begin + end; + wordSeqPos = begin.size(); + } + else + { + // It's not, so interpret it as in local encoding + QString begin = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord, + GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + + QString end = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord + + GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + + wordSeq = begin + end; + wordSeqPos = begin.size(); + } } // Now locate the word inside the sequence @@ -139,7 +247,7 @@ LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg, if ( wordSeq[ wordSeqPos ].isSpace() ) { // Currently we ignore such cases - return DefWindowProc( hwnd, msg, wparam, lparam ); + return 0; } else if ( !wordSeq[ wordSeqPos ].isLetterOrNumber() ) @@ -163,7 +271,7 @@ LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg, if ( end - begin == 1 ) { // Well, turns out it was just a single non-letter char, discard it - return DefWindowProc( hwnd, msg, wparam, lparam ); + return 0; } word = wordSeq.mid( begin, end - begin ); @@ -203,6 +311,7 @@ LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg, } emit instance().hovered( word ); + return 0; } return DefWindowProc( hwnd, msg, wparam, lparam ); diff --git a/mouseover.hh b/mouseover.hh index 9e38d3c9..48011fb0 100644 --- a/mouseover.hh +++ b/mouseover.hh @@ -33,8 +33,19 @@ signals: /// Emitted when there was some text under cursor which was hovered over. void hovered( QString const & ); + +#ifdef Q_OS_WIN32 + /// Ask for need to handle word under cursor void askNeedWord( bool * ); + /// Ask for use UI Automation interfaces to get word under cursor + void askUseUIAutomation( bool * ); + /// Ask for use IAccessibleEx interface to get word under cursor + void askUseIAccessibleEx( bool * ); + /// Ask for use GD message to get word under cursor + void askUseGDMessage( bool * ); + +#endif private: diff --git a/mouseover_win32/GetWordByIAccEx.cc b/mouseover_win32/GetWordByIAccEx.cc new file mode 100644 index 00000000..16c0a0e6 --- /dev/null +++ b/mouseover_win32/GetWordByIAccEx.cc @@ -0,0 +1,108 @@ +#include +#include +#include "ThTypes.h" +#include "IAccExInt.h" +#include "GetWordByIAccEx.h" + +GetPhysicalCursorPosFunc getPhysicalCursorPosFunc; + +BOOL FindGetPhysicalCursorPos() +{ +HMODULE hm; + getPhysicalCursorPosFunc = NULL; + hm = GetModuleHandle( "user32.dll" ); + if( hm != NULL ) { + getPhysicalCursorPosFunc = (GetPhysicalCursorPosFunc)GetProcAddress( hm, "GetPhysicalCursorPos" ); + } + return( getPhysicalCursorPosFunc != NULL ); +} + + +HRESULT GetParentAccessibleObject( IAccessible* pAcc, IAccessible** ppAccParent ) +{ +IDispatch* pDispatch = NULL; + + *ppAccParent = NULL; + if ( pAcc == NULL ) + return E_INVALIDARG; + HRESULT hr = pAcc->get_accParent( &pDispatch ); + if ( ( hr == S_OK ) && ( pDispatch != NULL ) ) { + hr = pDispatch->QueryInterface( IID_IAccessible, (void**)ppAccParent ); + pDispatch->Release(); + } + return hr; +} + + +BOOL getWordByAccEx( POINT pt ) +{ +HRESULT hr; +IAccessible *pAcc, *pAccParent; +ITextProvider *pText; +ITextRangeProvider *pTextRange; +VARIANT var; +long idChild; +BSTR bstr = NULL; +int n; +UiaPoint upt; +POINT ppt = { 0, 0 }; + + if( getPhysicalCursorPosFunc != NULL ) { + getPhysicalCursorPosFunc( &ppt ); + } else { + ppt = pt; + } + + upt.x = ppt.x; + upt.y = ppt.y; + + pAcc = NULL; + hr = AccessibleObjectFromPoint( ppt, &pAcc, &var ); + idChild = var.lVal; + + if( hr != S_OK || pAcc == NULL) { + VariantClear( &var ); + return FALSE; + } + + pText = NULL; + while( pAcc != NULL) { + hr = GetPatternFromIAccessible( pAcc, 0, UIA_TextPatternId, IID_ITextProvider, (void **)&pText ); + if( hr == S_OK && pText != NULL ) + break; + pAccParent = NULL; + hr = GetParentAccessibleObject( pAcc, &pAccParent ); + pAcc->Release(); + pAcc = pAccParent; + } + if( pAcc == NULL ) + return FALSE; + + pAcc->Release(); + + pTextRange = NULL; + hr = pText->RangeFromPoint( upt, &pTextRange ); + if( hr != S_OK || pTextRange == NULL ) { + pText->Release(); + return FALSE; + } + + hr = pTextRange->ExpandToEnclosingUnit( TextUnit_Word ); + if( hr == S_OK) { + bstr = NULL; + hr = pTextRange->GetText( 255, &bstr ); + if (hr == S_OK) { + n = SysStringLen( bstr ); + if( n != 0 ) { + n = WideCharToMultiByte( CP_UTF8, 0, (LPCWSTR)bstr, n, GlobalData->CurMod.MatchedWord, sizeof( GlobalData->CurMod.MatchedWord ) - 1, NULL, NULL ); + GlobalData->CurMod.WordLen = n; + GlobalData->CurMod.MatchedWord[n] = 0; + } + SysFreeString( bstr ); + } + } + pTextRange->Release(); + pText->Release(); + + return TRUE; +} diff --git a/mouseover_win32/GetWordByIAccEx.h b/mouseover_win32/GetWordByIAccEx.h new file mode 100644 index 00000000..ace0c8b7 --- /dev/null +++ b/mouseover_win32/GetWordByIAccEx.h @@ -0,0 +1,19 @@ +#ifndef __GetWordByIAccEx_H_DEFINED_ +#define __GetWordByIAccEx_H_DEFINED_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef BOOL (*GetPhysicalCursorPosFunc)(LPPOINT); +extern GetPhysicalCursorPosFunc getPhysicalCursorPosFunc; + +BOOL FindGetPhysicalCursorPos(); + +BOOL getWordByAccEx( POINT pt ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mouseover_win32/HookImportFunction.c b/mouseover_win32/HookImportFunction.c index 06fec359..d4b4287c 100644 --- a/mouseover_win32/HookImportFunction.c +++ b/mouseover_win32/HookImportFunction.c @@ -87,8 +87,10 @@ static BOOL HookImportFunction(HMODULE hModule, LPCSTR szImportModule, LPCSTR sz MEMORY_BASIC_INFORMATION mbi_thunk; DWORD dwOldProtect; - VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)); - VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect); + if( !VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)) ) + return FALSE; + if( !VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect) ) + return FALSE; if (paOrigFuncs) *paOrigFuncs = (PROC)pRealThunk->u1.Function; pRealThunk->u1.Function = (DWORD)paHookFuncs; diff --git a/mouseover_win32/IAccEx.cc b/mouseover_win32/IAccEx.cc new file mode 100644 index 00000000..70dd8415 --- /dev/null +++ b/mouseover_win32/IAccEx.cc @@ -0,0 +1,90 @@ +#include +#include +#include "IAccExInt.h" + +HRESULT GetIAccessibleExFromIAccessible( IAccessible *pAcc, long idChild, IAccessibleEx **ppaex ) +{ +*ppaex = NULL; +IAccessibleEx *paex; +IServiceProvider *pSp = NULL; +//char s[500]; + HRESULT hr = pAcc->QueryInterface( IID_IServiceProvider, (void **)&pSp ); +/* + wsprintf(s,"GD:QueryInterface (IServiceProvider) return hr=%08X, ptr=%p\n", hr, pSp); + OutputDebugString(s); +*/ + if( hr != S_OK ) return hr; + if( pSp == NULL ) return E_NOINTERFACE; + + paex = NULL; + hr = pSp->QueryService( IID_IAccessibleEx, IID_IAccessibleEx, (void **)&paex ); + pSp->Release(); +/* + wsprintf(s,"GD:QueryService (IAccessibleEx) return hr=%08X, ptr=%p\n", hr, paex); + OutputDebugString(s); +*/ + if( hr != S_OK ) return hr; + if( paex == NULL ) return E_NOINTERFACE; + + if(idChild == CHILDID_SELF) { + *ppaex = paex; + } else { + IAccessibleEx *paexChild = NULL; + hr = paex->GetObjectForChild( idChild, &paexChild ); +/* + wsprintf(s,"GD: GetObjectForChild return hr=%08X, ptr=%p (ChildID=%i)\n", hr, paexChild, idChild); + OutputDebugString(s); +*/ + paex->Release(); + if( hr != S_OK ) return hr; + if(paexChild == NULL) return E_NOINTERFACE; + *ppaex = paexChild; + } + return S_OK; +} + +HRESULT GetIRawElementProviderFromIAccessible( IAccessible * pAcc, long idChild, IRawElementProviderSimple **ppEl ) +{ +*ppEl = NULL; +IAccessibleEx *paex; +//char s[500]; + HRESULT hr = GetIAccessibleExFromIAccessible( pAcc, idChild, &paex ); + if( hr != S_OK ) return hr; + + hr = paex->QueryInterface( IID_IRawElementProviderSimple, (void **)ppEl ); +/* + wsprintf(s,"GD:QueryInterface (IRawElementProviderSimple) return hr=%08X, ptr=%p\n", hr, ppEl); + OutputDebugString(s); +*/ + paex->Release(); + return hr; +} + +HRESULT GetPatternFromIAccessible( IAccessible * pAcc, long idChild, PATTERNID patternId, REFIID iid, void **ppv ) +{ +IRawElementProviderSimple * pel; +//char s[500]; + HRESULT hr = GetIRawElementProviderFromIAccessible( pAcc, idChild, &pel ); + if( hr != S_OK ) return hr; + if( pel == NULL ) return E_NOINTERFACE; + + IUnknown * pPatternObject = NULL; + hr = pel->GetPatternProvider( patternId, &pPatternObject ); +/* + wsprintf(s,"GD:GetPatternProvider return hr=%08X, ptr=%p\n", hr, pPatternObject); + OutputDebugString(s); +*/ + pel->Release(); + if( hr != S_OK ) return hr; + if( pPatternObject == NULL ) return E_NOINTERFACE; + + *ppv = NULL; + hr = pPatternObject->QueryInterface( iid, ppv ); +/* + wsprintf(s,"GD:QueryInterface (TextPattern) return hr=%08X, ptr=%p\n", hr, ppv); + OutputDebugString(s); +*/ + pPatternObject->Release(); + if( *ppv == NULL ) return E_NOINTERFACE; + return hr; +} diff --git a/mouseover_win32/IAccExInt.h b/mouseover_win32/IAccExInt.h new file mode 100644 index 00000000..1477483d --- /dev/null +++ b/mouseover_win32/IAccExInt.h @@ -0,0 +1,157 @@ +#ifndef __UIAUTO_HH_INCLUDED__ +#define __UIAUTO_HH_INCLUDED__ + +//#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +EXTERN_C const IID IID_IAccessibleEx; +EXTERN_C const IID IID_IRawElementProviderSimple; +EXTERN_C const IID IID_ITextProvider; +EXTERN_C const IID IID_ITextRangeProvider; + +typedef interface IAccessibleEx IAccessibleEx; +typedef interface IRawElementProviderSimple IRawElementProviderSimple; +typedef interface ITextProvider ITextProvider; +typedef interface ITextRangeProvider ITextRangeProvider; + +typedef int PROPERTYID; +typedef int PATTERNID; +typedef int TEXTATTRIBUTEID; + +struct UiaPoint +{ + double x; + double y; +}; + +enum ProviderOptions +{ + ProviderOptions_ClientSideProvider = 0x1, + ProviderOptions_ServerSideProvider = 0x2, + ProviderOptions_NonClientAreaProvider = 0x4, + ProviderOptions_OverrideProvider = 0x8, + ProviderOptions_ProviderOwnsSetFocus = 0x10, + ProviderOptions_UseComThreading = 0x20 +}; + +enum SupportedTextSelection +{ + SupportedTextSelection_None = 0, + SupportedTextSelection_Single = 1, + SupportedTextSelection_Multiple = 2 +}; + +enum TextUnit +{ + TextUnit_Character = 0, + TextUnit_Format = 1, + TextUnit_Word = 2, + TextUnit_Line = 3, + TextUnit_Paragraph = 4, + TextUnit_Page = 5, + TextUnit_Document = 6 +}; + +enum TextPatternRangeEndpoint +{ + TextPatternRangeEndpoint_Start = 0, + TextPatternRangeEndpoint_End = 1 +}; + +/* UIA_PatternIds */ +const long UIA_InvokePatternId = 10000; +const long UIA_SelectionPatternId = 10001; +const long UIA_ValuePatternId = 10002; +const long UIA_RangeValuePatternId = 10003; +const long UIA_ScrollPatternId = 10004; +const long UIA_ExpandCollapsePatternId = 10005; +const long UIA_GridPatternId = 10006; +const long UIA_GridItemPatternId = 10007; +const long UIA_MultipleViewPatternId = 10008; +const long UIA_WindowPatternId = 10009; +const long UIA_SelectionItemPatternId = 10010; +const long UIA_DockPatternId = 10011; +const long UIA_TablePatternId = 10012; +const long UIA_TableItemPatternId = 10013; +const long UIA_TextPatternId = 10014; +const long UIA_TogglePatternId = 10015; +const long UIA_TransformPatternId = 10016; +const long UIA_ScrollItemPatternId = 10017; +const long UIA_LegacyIAccessiblePatternId = 10018; +const long UIA_ItemContainerPatternId = 10019; +const long UIA_VirtualizedItemPatternId = 10020; +const long UIA_SynchronizedInputPatternId = 10021; + +#define INTERFACE ITextProvider +DECLARE_INTERFACE_(ITextProvider, IUnknown) +{ + STDMETHOD(GetSelection)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(GetVisibleRanges)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(RangeFromChild)(THIS_ IRawElementProviderSimple *, ITextRangeProvider **) PURE; + STDMETHOD(RangeFromPoint)(THIS_ struct UiaPoint, ITextRangeProvider **pRetVal) PURE; + STDMETHOD(get_DocumentRange)(THIS_ ITextRangeProvider **) PURE; + STDMETHOD(get_SupportedTextSelection)(THIS_ enum SupportedTextSelection *) PURE; +}; +#undef INTERFACE + +#define INTERFACE ITextRangeProvider +DECLARE_INTERFACE_(ITextRangeProvider, IUnknown) +{ + STDMETHOD(Clone)(THIS_ ITextRangeProvider **) PURE; + STDMETHOD(Compare)(THIS_ ITextRangeProvider *, BOOL *) PURE; + STDMETHOD(CompareEndpoints)(THIS_ enum TextPatternRangeEndpoint, ITextRangeProvider *, enum TextPatternRangeEndpoint, int *) PURE; + STDMETHOD(ExpandToEnclosingUnit)(THIS_ enum TextUnit) PURE; + STDMETHOD(FindAttribute)(THIS_ TEXTATTRIBUTEID, VARIANT, BOOL, ITextRangeProvider **) PURE; + STDMETHOD(FindText)(THIS_ BSTR, BOOL, BOOL, ITextRangeProvider **) PURE; + STDMETHOD(GetAttributeValue)(THIS_ TEXTATTRIBUTEID, VARIANT *) PURE; + STDMETHOD(GetBoundingRectangles)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(GetEnclosingElement)(THIS_ IRawElementProviderSimple **) PURE; + STDMETHOD(GetText)(THIS_ int, BSTR *) PURE; + STDMETHOD(Move)(THIS_ enum TextUnit, int, int *) PURE; + STDMETHOD(MoveEndpointByUnit)(THIS_ enum TextPatternRangeEndpoint, enum TextUnit, int *) PURE; + STDMETHOD(MoveEndpointByRange)(THIS_ enum TextPatternRangeEndpoint, ITextRangeProvider *, enum TextPatternRangeEndpoint) PURE; + STDMETHOD(Select)(THIS) PURE; + STDMETHOD(AddToSelection)(THIS) PURE; + STDMETHOD(RemoveFromSelection)(THIS) PURE; + STDMETHOD(ScrollIntoView)(THIS_ BOOL) PURE; + STDMETHOD(GetChildren)(THIS_ SAFEARRAY **) PURE; +}; +#undef INTERFACE + +#define INTERFACE IRawElementProviderSimple +DECLARE_INTERFACE_(IRawElementProviderSimple, IUnknown) +{ + STDMETHOD(get_ProviderOptions)(THIS_ enum ProviderOptions *) PURE; + STDMETHOD(GetPatternProvider)(THIS_ PATTERNID, IUnknown **) PURE; + STDMETHOD(GetPropertyValue)(THIS_ PROPERTYID, VARIANT *) PURE; + STDMETHOD(get_HostRawElementProvider)(THIS_ IRawElementProviderSimple **) PURE; +}; +#undef INTERFACE + + +#define INTERFACE IAccessibleEx +DECLARE_INTERFACE_(IAccessibleEx, IUnknown) +{ + STDMETHOD(GetObjectForChild)(THIS_ long, IAccessibleEx **) PURE; + STDMETHOD(GetIAccessiblePair)(THIS_ IAccessible **, long *) PURE; + STDMETHOD(GetRuntimeId)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(ConvertReturnedElement)(THIS_ IRawElementProviderSimple *, IAccessibleEx **) PURE; +}; +#undef INTERFACE + +HRESULT GetIAccessibleExFromIAccessible( IAccessible *pAcc, long idChild, IAccessibleEx **ppaex ); +HRESULT GetIRawElementProviderFromIAccessible( IAccessible * pAcc, long idChild, IRawElementProviderSimple **ppEl ); +HRESULT GetPatternFromIAccessible( IAccessible * pAcc, long idChild, PATTERNID patternId, REFIID iid, void **ppv ); + +#ifdef __cplusplus +} +#endif + +#endif // UIAUTO_HH diff --git a/mouseover_win32/Makefile b/mouseover_win32/Makefile index e5ddfc9f..467e5f13 100644 --- a/mouseover_win32/Makefile +++ b/mouseover_win32/Makefile @@ -1,4 +1,5 @@ -GCC:=i686-pc-mingw32-gcc.exe -W -Wall -s -O2 +GCC:=gcc.exe -W -Wall -s -O2 +GPP:=g++.exe -W -Wall -s -O2 .PHONY: all clean @@ -16,6 +17,15 @@ HookImportFunction.o: HookImportFunction.c GetWord.o: GetWord.c $(GCC) -c $< +IAccEx.o: IAccEx.cc + $(GPP) -c $< + +GetWordByIAccEx.o: GetWordByIAccEx.cc + $(GPP) -c $< + +guids.o: guids.c + $(GCC) -c $< + GdTextOutHook.dll libGdTextOutHook.a: TextOutHook.o HookImportFunction.o GetWord.o $(GCC) -shared -o GdTextOutHook.dll $^ -lgdi32 -Wl,--out-implib,libGdTextOutHook.a @@ -25,5 +35,5 @@ TextOutSpy.o: TextOutSpy.c ThTypes.o: ThTypes.c $(GCC) -c $< -GdTextOutSpy.dll libGdTextOutSpy.a: TextOutSpy.o ThTypes.o - $(GCC) -shared -o GdTextOutSpy.dll $^ -lgdi32 -Wl,--out-implib,libGdTextOutSpy.a +GdTextOutSpy.dll libGdTextOutSpy.a: TextOutSpy.o ThTypes.o IAccEx.o guids.o GetWordByIAccEx.o + $(GPP) -shared -o GdTextOutSpy.dll $^ -lgdi32 -luuid -loleacc -loleaut32 -Wl,--out-implib,libGdTextOutSpy.a diff --git a/mouseover_win32/TextOutHook.c b/mouseover_win32/TextOutHook.c index a8e92fb8..cdeceae6 100644 --- a/mouseover_win32/TextOutHook.c +++ b/mouseover_win32/TextOutHook.c @@ -525,9 +525,9 @@ DWORD wso; case DLL_PROCESS_ATTACH: if(hHookMutex==0) { hHookMutex = CreateMutex(NULL, FALSE, "GoldenDictTextOutHookMutex"); - if(hHookMutex==0) return(FALSE); + if(hHookMutex==0) + return(FALSE); } - //ThTypes_Init(); InstallTextOutHooks(); break; @@ -541,7 +541,6 @@ DWORD wso; hHookMutex=0; } } - //Thtypes_End(); break; case DLL_THREAD_ATTACH: diff --git a/mouseover_win32/TextOutSpy.c b/mouseover_win32/TextOutSpy.c index 416b3f63..caa365e7 100644 --- a/mouseover_win32/TextOutSpy.c +++ b/mouseover_win32/TextOutSpy.c @@ -1,13 +1,14 @@ #include "TextOutSpy.h" #include "ThTypes.h" #include "GDDataTranfer.h" +#include "GetWordByIAccEx.h" const int MOUSEOVER_INTERVAL = 300; const int REQUEST_MESSAGE_INTERVAL = 500; const int WM_MY_SHOW_TRANSLATION = WM_USER + 301; HINSTANCE g_hInstance = NULL; -HANDLE hSynhroMutex = 0; +HANDLE hSynhroMutex = 0, hHookMutex = 0; HINSTANCE hGetWordLib = 0; UINT_PTR TimerID = 0; typedef void (*GetWordProc_t)(TCurrentMode *); @@ -29,9 +30,29 @@ HWND WndParent,WndChild; static void SendWordToServer() { -DWORD SendMsgAnswer; - if(uGdAskMessage) { - LRESULT lr; +DWORD SendMsgAnswer, flags; +LRESULT lr; + if (hGetWordLib == 0) { + hGetWordLib = LoadLibrary(GlobalData->LibName); + if (hGetWordLib) { + GetWordProc = (GetWordProc_t)GetProcAddress(hGetWordLib, "__gdGetWord"); + } + else { + hGetWordLib = (HINSTANCE)-1; + } + } + + // Ask for needing to retrieve word - WPARAM = 1 + lr = SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 1, 0, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer); + if( lr == 0 || SendMsgAnswer == 0) //No answer or no needing + return; + + GlobalData->CurMod.MatchedWord[0] = 0; + GlobalData->CurMod.WordLen = 0; + GlobalData->CurMod.BeginPos = 0; + flags = SendMsgAnswer; + + if( ( flags & GD_FLAG_METHOD_GD_MESSAGE ) != 0 && uGdAskMessage != 0 ) { int n; gds.dwSize = sizeof(gds); gds.cwData = Buffer; @@ -51,23 +72,28 @@ DWORD SendMsgAnswer; return; } } - if (hGetWordLib == 0) { - hGetWordLib = LoadLibrary(GlobalData->LibName); - if (hGetWordLib) { - GetWordProc = (GetWordProc_t)GetProcAddress(hGetWordLib, "__gdGetWord"); - } - else { - hGetWordLib = (HINSTANCE)-1; - } - } - if (GetWordProc) { + + if( ( flags & GD_FLAG_METHOD_STANDARD ) != 0 && GetWordProc != 0 ) { GlobalData->CurMod.WND = GlobalData->LastWND; GlobalData->CurMod.Pt = GlobalData->LastPt; GetWordProc(&(GlobalData->CurMod)); if (GlobalData->CurMod.WordLen > 0) { SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 0, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer); + return; } } + + if( ( flags & GD_FLAG_METHOD_IACCESSIBLEEX ) != 0 ) { + getWordByAccEx( GlobalData->LastPt ); + if (GlobalData->CurMod.WordLen > 0) { + SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 0, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer); + return; + } + } + + if( ( flags & GD_FLAG_METHOD_UI_AUTOMATION ) != 0 ) { + PostMessage( GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 0 ); + } } void CALLBACK TimerFunc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime) @@ -86,6 +112,7 @@ DWORD wso; LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) { DWORD wso; + if ((nCode == HC_ACTION) && ((wParam == WM_MOUSEMOVE) || (wParam == WM_NCMOUSEMOVE)) && (GlobalData != NULL)) { wso = WaitForSingleObject(hSynhroMutex, 0); if (wso == WAIT_OBJECT_0 || wso == WAIT_ABANDONED) { @@ -163,12 +190,18 @@ BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ , { case DLL_PROCESS_ATTACH: g_hInstance = hInst; + if(hHookMutex==0) { + hHookMutex = CreateMutex(NULL, FALSE, "GoldenDictTextOutHookMutex"); + } if(hSynhroMutex==0) { hSynhroMutex = CreateMutex(NULL, FALSE, "GoldenDictTextOutSpyMutex"); - if(hSynhroMutex==0) return(FALSE); + if(hSynhroMutex==0) { + return(FALSE); + } } ThTypes_Init(); uGdAskMessage = RegisterWindowMessage(GD_MESSAGE_NAME); + FindGetPhysicalCursorPos(); break; case DLL_PROCESS_DETACH: @@ -190,6 +223,14 @@ BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ , if ((hGetWordLib != 0)&&(hGetWordLib != (HINSTANCE)(-1))) { FreeLibrary(hGetWordLib); } + if(hHookMutex) { + DWORD wso = WaitForSingleObject(hHookMutex, 5000); + if (wso == WAIT_OBJECT_0 || wso == WAIT_ABANDONED) { + ReleaseMutex(hHookMutex); + CloseHandle(hHookMutex); + hHookMutex=0; + } + } Thtypes_End(); break; diff --git a/mouseover_win32/ThTypes.c b/mouseover_win32/ThTypes.c index 577fc780..9ba40230 100644 --- a/mouseover_win32/ThTypes.c +++ b/mouseover_win32/ThTypes.c @@ -5,8 +5,12 @@ TGlobalDLLData *GlobalData = NULL; void ThTypes_Init() { - if (!MMFHandle) + if (!MMFHandle) { MMFHandle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TGlobalDLLData), "GoldenDictTextOutHookSharedMem"); + } + if (!MMFHandle) { + MMFHandle = OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, 0, "GoldenDictTextOutHookSharedMem"); + } if (!GlobalData && MMFHandle != NULL) GlobalData = MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0); } diff --git a/mouseover_win32/ThTypes.h b/mouseover_win32/ThTypes.h index 5f9a3f84..efdaa57f 100644 --- a/mouseover_win32/ThTypes.h +++ b/mouseover_win32/ThTypes.h @@ -3,6 +3,11 @@ #include +#define GD_FLAG_METHOD_STANDARD 0x00000001 +#define GD_FLAG_METHOD_GD_MESSAGE 0x00000002 +#define GD_FLAG_METHOD_IACCESSIBLEEX 0x00000004 +#define GD_FLAG_METHOD_UI_AUTOMATION 0x00000008 + #ifdef __cplusplus extern "C" { diff --git a/mouseover_win32/guids.c b/mouseover_win32/guids.c new file mode 100644 index 00000000..0f613c2e --- /dev/null +++ b/mouseover_win32/guids.c @@ -0,0 +1,8 @@ +#define INITGUID +#include + +DEFINE_GUID(IID_IAccessibleEx, 0xf8b80ada, 0x2c44, 0x48d0, 0x89, 0xbe, 0x5f, 0xf2, 0x3c, 0x9c, 0xd8, 0x75); +DEFINE_GUID(IID_IRawElementProviderSimple, 0xd6dd68d1, 0x86fd, 0x4332, 0x86, 0x66, 0x9a, 0xbe, 0xde, 0xa2, 0xd2, 0x4c); +DEFINE_GUID(IID_ITextProvider, 0x3589c92c, 0x63f3, 0x4367, 0x99, 0xbb, 0xad, 0xa6, 0x53, 0xb7, 0x7c, 0xf2); +DEFINE_GUID(IID_ITextRangeProvider, 0x5347ad7b, 0xc355, 0x46f8, 0xaf, 0xf5, 0x90, 0x90, 0x33, 0x58, 0x2f, 0x63); + diff --git a/preferences.cc b/preferences.cc index 6c8e8f25..07f6901f 100644 --- a/preferences.cc +++ b/preferences.cc @@ -118,6 +118,9 @@ Preferences::Preferences( QWidget * parent, Config::Preferences const & p ): ui.scanPopupAltMode->setChecked( p.scanPopupAltMode ); ui.scanPopupAltModeSecs->setValue( p.scanPopupAltModeSecs ); + ui.scanPopupUseUIAutomation->setChecked( p.scanPopupUseUIAutomation ); + ui.scanPopupUseIAccessibleEx->setChecked( p.scanPopupUseIAccessibleEx ); + ui.scanPopupUseGDMessage->setChecked( p.scanPopupUseGDMessage ); // Different platforms have different keys available @@ -130,6 +133,11 @@ Preferences::Preferences( QWidget * parent, Config::Preferences const & p ): ui.rightCtrl->hide(); ui.leftShift->hide(); ui.rightShift->hide(); + + // This settings is Windows-specific + ui.scanPopupUseUIAutomation->hide(); + ui.scanPopupUseIAccessibleEx->hide(); + ui.scanPopupUseGDMessage->hide(); #endif // Sound @@ -224,6 +232,9 @@ Config::Preferences Preferences::getPreferences() p.scanPopupAltMode = ui.scanPopupAltMode->isChecked(); p.scanPopupAltModeSecs = ui.scanPopupAltModeSecs->value(); + p.scanPopupUseUIAutomation = ui.scanPopupUseUIAutomation->isChecked(); + p.scanPopupUseIAccessibleEx = ui.scanPopupUseIAccessibleEx->isChecked(); + p.scanPopupUseGDMessage = ui.scanPopupUseGDMessage->isChecked(); p.pronounceOnLoadMain = ui.pronounceOnLoadMain->isChecked(); p.pronounceOnLoadPopup = ui.pronounceOnLoadPopup->isChecked(); diff --git a/preferences.ui b/preferences.ui index dbb0a7c9..7d0e0cd0 100644 --- a/preferences.ui +++ b/preferences.ui @@ -7,7 +7,7 @@ 0 0 572 - 347 + 357 @@ -276,6 +276,19 @@ With this on however, it will hide the main window. + + Qt::Vertical + + + + 20 + 20 + + + + + + Qt::Vertical @@ -545,9 +558,88 @@ seconds, which is specified here. + + + + + + Try to use IAccessibleEx interface to retrieve word under cursor. +This technology works only with some programs that support it. +Don't select this option if you don't use such programs. + + + Use IAccessibleE&x + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Use special GoldenDict message to ask other programs about a word under cursor. +This technology works only with some programs that support it. +Don't select this option if you don't use such programs. + + + Use GD message + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Try to use UI Automation technology to retrieve word under cursor. +This technology works only with some programs that support it. +Don't select this option if you don't use such programs. + + + Use UI Automation + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -1069,7 +1161,6 @@ download page. proxyPort proxyUser proxyPassword - enableScanPopup diff --git a/scanpopup.cc b/scanpopup.cc index e7f6fecc..d9b022d1 100644 --- a/scanpopup.cc +++ b/scanpopup.cc @@ -153,8 +153,18 @@ ScanPopup::ScanPopup( QWidget * parent, connect( &mouseGrabPollTimer, SIGNAL( timeout() ), this, SLOT(mouseGrabPoll()) ); +#ifdef Q_OS_WIN32 + connect( &MouseOver::instance(), SIGNAL( askNeedWord( bool * ) ), this, SLOT( needHandleWord( bool * ) ), Qt::DirectConnection ); + connect( &MouseOver::instance(), SIGNAL( askUseUIAutomation( bool * ) ), + this, SLOT( useUIAutomation( bool * ) ), Qt::DirectConnection ); + connect( &MouseOver::instance(), SIGNAL( askUseIAccessibleEx( bool * ) ), + this, SLOT( useIAccessibleEx( bool * ) ), Qt::DirectConnection ); + connect( &MouseOver::instance(), SIGNAL( askUseGDMessage( bool * ) ), + this, SLOT( useGDMessage( bool * ) ), Qt::DirectConnection ); + +#endif } ScanPopup::~ScanPopup() @@ -768,7 +778,26 @@ void ScanPopup::mutedDictionariesChanged() definition->updateMutedContents(); } +#ifdef Q_OS_WIN32 + void ScanPopup::needHandleWord( bool *pNeed ) { *pNeed = !cfg.preferences.enableScanPopupModifiers || checkModifiersPressed( cfg.preferences.scanPopupModifiers ); } + +void ScanPopup::useUIAutomation( bool *pUse ) +{ + *pUse = cfg.preferences.scanPopupUseUIAutomation != 0; +} + +void ScanPopup::useIAccessibleEx( bool *pUse ) +{ + *pUse = cfg.preferences.scanPopupUseIAccessibleEx != 0; +} + +void ScanPopup::useGDMessage( bool *pUse ) +{ + *pUse = cfg.preferences.scanPopupUseGDMessage != 0; +} + +#endif // Q_OS_WIN32 diff --git a/scanpopup.hh b/scanpopup.hh index eca737e5..79ece28f 100644 --- a/scanpopup.hh +++ b/scanpopup.hh @@ -134,7 +134,12 @@ private slots: void altModeExpired(); void altModePoll(); +#ifdef Q_OS_WIN32 void needHandleWord(bool *pNeed); + void useUIAutomation(bool *pUse); + void useIAccessibleEx(bool *pUse); + void useGDMessage(bool *pUse); +#endif /// Called repeatedly once the popup is initially engaged and we monitor the /// mouse as it may move away from the window. This simulates mouse grab, in diff --git a/uiauto.hh b/uiauto.hh new file mode 100644 index 00000000..5d7205bb --- /dev/null +++ b/uiauto.hh @@ -0,0 +1,330 @@ +#ifndef __UIAUTO_HH_INCLUDED__ +#define __UIAUTO_HH_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +EXTERN_C const IID IID_IUIAutomation; +EXTERN_C const IID CLSID_CUIAutomation; +EXTERN_C const IID IID_IUIAutomationElement; +EXTERN_C const IID IID_IUIAutomationTextPattern; +EXTERN_C const IID IID_IUIAutomationTextRange; +EXTERN_C const IID IID_IUIAutomationTreeWalker; + +typedef interface IUIAutomationElement IUIAutomationElement; +typedef interface IUIAutomationElementArray IUIAutomationElementArray; +typedef interface IUIAutomationTextPattern IUIAutomationTextPattern; +typedef interface IUIAutomationTextRange IUIAutomationTextRange; +typedef interface IUIAutomationTextRangeArray IUIAutomationTextRangeArray; +typedef interface IUIAutomationCacheRequest IUIAutomationCacheRequest; +typedef interface IUIAutomationTreeWalker IUIAutomationTreeWalker; +typedef interface IUIAutomationCondition IUIAutomationCondition; +typedef interface IUIAutomationEventHandler IUIAutomationEventHandler; +typedef interface IUIAutomationPropertyChangedEventHandler IUIAutomationPropertyChangedEventHandler; +typedef interface IUIAutomationStructureChangedEventHandler IUIAutomationStructureChangedEventHandler; +typedef interface IUIAutomationFocusChangedEventHandler IUIAutomationFocusChangedEventHandler; +typedef interface IUIAutomationProxyFactory IUIAutomationProxyFactory; +typedef interface IUIAutomationProxyFactoryEntry IUIAutomationProxyFactoryEntry; +typedef interface IUIAutomationProxyFactoryMapping IUIAutomationProxyFactoryMapping; + +typedef void *UIA_HWND; +typedef int PROPERTYID; +typedef int EVENTID; +typedef int PATTERNID; +typedef int CONTROLTYPEID; +typedef int TEXTATTRIBUTEID; + +enum TreeScope +{ + TreeScope_Element = 0x1, + TreeScope_Children = 0x2, + TreeScope_Descendants = 0x4, + TreeScope_Parent = 0x8, + TreeScope_Ancestors = 0x10, + TreeScope_Subtree = ( ( TreeScope_Element | TreeScope_Children ) | TreeScope_Descendants ) +}; + +enum PropertyConditionFlags +{ + PropertyConditionFlags_None = 0, + PropertyConditionFlags_IgnoreCase = 0x1 +}; + +enum OrientationType +{ + OrientationType_None = 0, + OrientationType_Horizontal = 1, + OrientationType_Vertical = 2 +}; + +enum SupportedTextSelection +{ + SupportedTextSelection_None = 0, + SupportedTextSelection_Single = 1, + SupportedTextSelection_Multiple = 2 +}; + +enum TextPatternRangeEndpoint +{ + TextPatternRangeEndpoint_Start = 0, + TextPatternRangeEndpoint_End = 1 +}; + +enum TextUnit +{ + TextUnit_Character = 0, + TextUnit_Format = 1, + TextUnit_Word = 2, + TextUnit_Line = 3, + TextUnit_Paragraph = 4, + TextUnit_Page = 5, + TextUnit_Document = 6 +}; + +enum ProviderOptions +{ + ProviderOptions_ClientSideProvider = 0x1, + ProviderOptions_ServerSideProvider = 0x2, + ProviderOptions_NonClientAreaProvider = 0x4, + ProviderOptions_OverrideProvider = 0x8, + ProviderOptions_ProviderOwnsSetFocus = 0x10, + ProviderOptions_UseComThreading = 0x20 +} ; + +/* UIA_PatternIds */ +const long UIA_InvokePatternId = 10000; +const long UIA_SelectionPatternId = 10001; +const long UIA_ValuePatternId = 10002; +const long UIA_RangeValuePatternId = 10003; +const long UIA_ScrollPatternId = 10004; +const long UIA_ExpandCollapsePatternId = 10005; +const long UIA_GridPatternId = 10006; +const long UIA_GridItemPatternId = 10007; +const long UIA_MultipleViewPatternId = 10008; +const long UIA_WindowPatternId = 10009; +const long UIA_SelectionItemPatternId = 10010; +const long UIA_DockPatternId = 10011; +const long UIA_TablePatternId = 10012; +const long UIA_TableItemPatternId = 10013; +const long UIA_TextPatternId = 10014; +const long UIA_TogglePatternId = 10015; +const long UIA_TransformPatternId = 10016; +const long UIA_ScrollItemPatternId = 10017; +const long UIA_LegacyIAccessiblePatternId = 10018; +const long UIA_ItemContainerPatternId = 10019; +const long UIA_VirtualizedItemPatternId = 10020; +const long UIA_SynchronizedInputPatternId = 10021; + +#define INTERFACE IUIAutomation +DECLARE_INTERFACE_(IUIAutomation, IUnknown) +{ + STDMETHOD(CompareElements)(THIS_ IUIAutomationElement *, IUIAutomationElement *, BOOL *) PURE; + STDMETHOD(CompareRuntimeIds)(THIS_ SAFEARRAY *, SAFEARRAY *, BOOL *) PURE; + STDMETHOD(GetRootElement)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(ElementFromHandle)(THIS_ UIA_HWND, IUIAutomationElement **) PURE; + STDMETHOD(ElementFromPoint)(THIS_ POINT, IUIAutomationElement **) PURE; + STDMETHOD(GetFocusedElement)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(GetRootElementBuildCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(ElementFromHandleBuildCache)(THIS_ UIA_HWND, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(ElementFromPointBuildCache)(THIS_ POINT, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetFocusedElementBuildCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(CreateTreeWalker)(THIS_ IUIAutomationCondition *, IUIAutomationTreeWalker **) PURE; + STDMETHOD(get_ControlViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; + STDMETHOD(get_ContentViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; + STDMETHOD(get_RawViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; + STDMETHOD(get_RawViewCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(get_ControlViewCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(get_ContentViewCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(CreateCacheRequest)(THIS_ IUIAutomationCacheRequest **) PURE; + STDMETHOD(CreateTrueCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(CreateFalseCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(CreatePropertyCondition)(THIS_ PROPERTYID, VARIANT, IUIAutomationCondition **) PURE; + STDMETHOD(CreatePropertyConditionEx)(THIS_ PROPERTYID, VARIANT, enum PropertyConditionFlags, IUIAutomationCondition **) PURE; + STDMETHOD(CreateAndCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition *, IUIAutomationCondition **) PURE; + STDMETHOD(CreateAndConditionFromArray)(THIS_ SAFEARRAY *, IUIAutomationCondition **) PURE; + STDMETHOD(CreateAndConditionFromNativeArray)(THIS_ IUIAutomationCondition **, int , IUIAutomationCondition **) PURE; + STDMETHOD(CreateOrCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition *, IUIAutomationCondition **) PURE; + STDMETHOD(CreateOrConditionFromArray)(THIS_ SAFEARRAY *, IUIAutomationCondition **) PURE; + STDMETHOD(CreateOrConditionFromNativeArray)(THIS_ IUIAutomationCondition **, int , IUIAutomationCondition **) PURE; + STDMETHOD(CreateNotCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition **) PURE; + STDMETHOD(AddAutomationEventHandler)(THIS_ EVENTID, IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, IUIAutomationEventHandler *) PURE; + STDMETHOD(RemoveAutomationEventHandler)(THIS_ EVENTID, IUIAutomationElement *, IUIAutomationEventHandler *) PURE; + STDMETHOD(AddPropertyChangedEventHandlerNativeArray)(THIS_ IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, + IUIAutomationPropertyChangedEventHandler *, PROPERTYID *, int) PURE; + STDMETHOD(AddPropertyChangedEventHandler)(THIS_ IUIAutomationElement *, enum TreeScope, EVENTID, IUIAutomationCacheRequest *, + IUIAutomationPropertyChangedEventHandler *, SAFEARRAY *) PURE; + STDMETHOD(RemovePropertyChangedEventHandler)(THIS_ IUIAutomationElement *, IUIAutomationPropertyChangedEventHandler *) PURE; + STDMETHOD(AddStructureChangedEventHandler)(THIS_ IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, IUIAutomationStructureChangedEventHandler *) PURE; + STDMETHOD(RemoveStructureChangedEventHandler)(THIS_ IUIAutomationElement *, IUIAutomationStructureChangedEventHandler *) PURE; + STDMETHOD(AddFocusChangedEventHandler)(THIS_ IUIAutomationCacheRequest *, IUIAutomationFocusChangedEventHandler *) PURE; + STDMETHOD(RemoveFocusChangedEventHandler)(THIS_ IUIAutomationFocusChangedEventHandler *) PURE; + STDMETHOD(RemoveAllEventHandlers)(THIS) PURE; + STDMETHOD(IntNativeArrayToSafeArray)(THIS_ int *, int, SAFEARRAY **) PURE; + STDMETHOD(IntSafeArrayToNativeArray)(THIS_ SAFEARRAY *, int **, int *) PURE; + STDMETHOD(RectToVariant)(THIS_ RECT, VARIANT *) PURE; + STDMETHOD(VariantToRect)(THIS_ VARIANT, RECT *) PURE; + STDMETHOD(SafeArrayToRectNativeArray)(THIS_ SAFEARRAY *, RECT **, int *) PURE; + STDMETHOD(CreateProxyFactoryEntry)(THIS_ IUIAutomationProxyFactory *, IUIAutomationProxyFactoryEntry **) PURE; + STDMETHOD(get_ProxyFactoryMapping)(THIS_ IUIAutomationProxyFactoryMapping **) PURE; + STDMETHOD(GetPropertyProgrammaticName)(THIS_ PROPERTYID, BSTR *) PURE; + STDMETHOD(GetPatternProgrammaticName)(THIS_ PATTERNID, BSTR *) PURE; + STDMETHOD(PollForPotentialSupportedPatterns)(THIS_ IUIAutomationElement *, SAFEARRAY **, SAFEARRAY **) PURE; + STDMETHOD(PollForPotentialSupportedProperties)(THIS_ IUIAutomationElement *, SAFEARRAY **, SAFEARRAY **) PURE; + STDMETHOD(CheckNotSupported)(THIS_ VARIANT, BOOL *) PURE; + STDMETHOD(get_ReservedNotSupportedValue)(THIS_ IUnknown **) PURE; + STDMETHOD(get_ReservedMixedAttributeValue)(THIS_ IUnknown **) PURE; + STDMETHOD(ElementFromIAccessible)(THIS_ IAccessible *, int, IUIAutomationElement **) PURE; + STDMETHOD(ElementFromIAccessibleBuildCache)(THIS_ IAccessible *, int, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; +}; +#undef INTERFACE + +#define INTERFACE IUIAutomationElement +DECLARE_INTERFACE_(IUIAutomationElement, IUnknown) +{ + STDMETHOD(SetFocus)(THIS) PURE; + STDMETHOD(GetRuntimeId)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(FindFirst)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationElement **) PURE; + STDMETHOD(FindAll)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationElementArray **) PURE; + STDMETHOD(FindFirstBuildCache)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(FindAllBuildCache)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationCacheRequest *, IUIAutomationElementArray **) PURE; + STDMETHOD(BuildUpdatedCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetCurrentPropertyValue)(THIS_ PROPERTYID, VARIANT *) PURE; + STDMETHOD(GetCurrentPropertyValueEx)(THIS_ PROPERTYID, BOOL, VARIANT *) PURE; + STDMETHOD(GetCachedPropertyValue)(THIS_ PROPERTYID, VARIANT *) PURE; + STDMETHOD(GetCachedPropertyValueEx)(THIS_ PROPERTYID, BOOL, VARIANT *) PURE; + STDMETHOD(GetCurrentPatternAs)(THIS_ PATTERNID, REFIID, void **) PURE; + STDMETHOD(GetCachedPatternAs)(THIS_ PATTERNID, REFIID, void **) PURE; + STDMETHOD(GetCurrentPattern)(THIS_ PATTERNID, IUnknown **) PURE; + STDMETHOD(GetCachedPattern)(THIS_ PATTERNID, IUnknown **) PURE; + STDMETHOD(GetCachedParent)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(GetCachedChildren)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(get_CurrentProcessId)(THIS_ int *) PURE; + STDMETHOD(get_CurrentControlType)(THIS_ CONTROLTYPEID *) PURE; + STDMETHOD(get_CurrentLocalizedControlType)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentName)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentAcceleratorKey)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentAccessKey)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentHasKeyboardFocus)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentIsKeyboardFocusable)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentIsEnabled)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentAutomationId)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentClassName)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentHelpText)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentCulture)(THIS_ int *) PURE; + STDMETHOD(get_CurrentIsControlElement)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentIsContentElement)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentIsPassword)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentNativeWindowHandle)(THIS_ UIA_HWND *) PURE; + STDMETHOD(get_CurrentItemType)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentIsOffscreen)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentOrientation)(THIS_ enum OrientationType *) PURE; + STDMETHOD(get_CurrentFrameworkId)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentIsRequiredForForm)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentItemStatus)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentBoundingRectangle)(THIS_ RECT *) PURE; + STDMETHOD(get_CurrentLabeledBy)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(get_CurrentAriaRole)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentAriaProperties)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentIsDataValidForForm)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentControllerFor)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CurrentDescribedBy)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CurrentFlowsTo)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CurrentProviderDescription)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedProcessId)(THIS_ int *) PURE; + STDMETHOD(get_CachedControlType)(THIS_ CONTROLTYPEID *) PURE; + STDMETHOD(get_CachedLocalizedControlType)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedName)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedAcceleratorKey)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedAccessKey)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedHasKeyboardFocus)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedIsKeyboardFocusable)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedIsEnabled)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedAutomationId)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedClassName)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedHelpText)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedCulture)(THIS_ int *) PURE; + STDMETHOD(get_CachedIsControlElement)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedIsContentElement)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedIsPassword)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedNativeWindowHandle)(THIS_ UIA_HWND *) PURE; + STDMETHOD(get_CachedItemType)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedIsOffscreen)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedOrientation)(THIS_ enum OrientationType *) PURE; + STDMETHOD(get_CachedFrameworkId)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedIsRequiredForForm)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedItemStatus)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedBoundingRectangle)(THIS_ RECT *) PURE; + STDMETHOD(get_CachedLabeledBy)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(get_CachedAriaRole)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedAriaProperties)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedIsDataValidForForm)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedControllerFor)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CachedDescribedBy)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CachedFlowsTo)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CachedProviderDescription)(THIS_ BSTR *) PURE; +}; +#undef INTERFACE + +#define INTERFACE IUIAutomationTextPattern +DECLARE_INTERFACE_(IUIAutomationTextPattern, IUnknown) +{ + STDMETHOD(RangeFromPoint)(THIS_ POINT, IUIAutomationTextRange **) PURE; + STDMETHOD(RangeFromChild)(THIS_ IUIAutomationElement *, IUIAutomationTextRange **) PURE; + STDMETHOD(GetSelection)(THIS_ IUIAutomationTextRangeArray **) PURE; + STDMETHOD(GetVisibleRanges)(THIS_ IUIAutomationTextRangeArray **) PURE; + STDMETHOD(get_DocumentRange)(THIS_ IUIAutomationTextRange **) PURE; + STDMETHOD(get_SupportedTextSelection)(THIS_ enum SupportedTextSelection *) PURE; +}; +#undef INTERFACE + +#define INTERFACE IUIAutomationTreeWalker +DECLARE_INTERFACE_(IUIAutomationTreeWalker, IUnknown) +{ + STDMETHOD(GetParentElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetFirstChildElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetLastChildElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetNextSiblingElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetPreviousSiblingElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(NormalizeElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetParentElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetFirstChildElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetLastChildElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetNextSiblingElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetPreviousSiblingElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(NormalizeElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(get_Condition)(THIS_ IUIAutomationCondition **) PURE; +}; +#undef INTERFACE + +#define INTERFACE IUIAutomationTextRange +DECLARE_INTERFACE_(IUIAutomationTextRange, IUnknown) +{ + STDMETHOD(Clone)(THIS_ IUIAutomationTextRange **) PURE; + STDMETHOD(Compare)(THIS_ IUIAutomationTextRange *, BOOL *) PURE; + STDMETHOD(CompareEndpoints)(THIS_ enum TextPatternRangeEndpoint, IUIAutomationTextRange *, enum TextPatternRangeEndpoint, int *) PURE; + STDMETHOD(ExpandToEnclosingUnit)(THIS_ enum TextUnit) PURE; + STDMETHOD(FindAttribute)(THIS_ TEXTATTRIBUTEID, VARIANT, BOOL, IUIAutomationTextRange **) PURE; + STDMETHOD(FindText)(THIS_ BSTR, BOOL, BOOL, IUIAutomationTextRange **) PURE; + STDMETHOD(GetAttributeValue)(THIS_ TEXTATTRIBUTEID, VARIANT *) PURE; + STDMETHOD(GetBoundingRectangles)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(GetEnclosingElement)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(GetText)(THIS_ int, BSTR *) PURE; + STDMETHOD(Move)(THIS_ enum TextUnit, int, int *) PURE; + STDMETHOD(MoveEndpointByUnit)(THIS_ enum TextPatternRangeEndpoint, enum TextUnit, int *) PURE; + STDMETHOD(MoveEndpointByRange)(THIS_ enum TextPatternRangeEndpoint, IUIAutomationTextRange *, enum TextPatternRangeEndpoint) PURE; + STDMETHOD(Select)(THIS) PURE; + STDMETHOD(AddToSelection)(THIS) PURE; + STDMETHOD(RemoveFromSelection)(THIS) PURE; + STDMETHOD(ScrollIntoView)(THIS_ BOOL) PURE; + STDMETHOD(GetChildren)(THIS_ IUIAutomationElementArray **) PURE; +}; +#undef INTERFACE + +#ifdef __cplusplus +} +#endif + +#endif // UIAUTO_HH diff --git a/wordbyauto.cc b/wordbyauto.cc new file mode 100644 index 00000000..4d31d04e --- /dev/null +++ b/wordbyauto.cc @@ -0,0 +1,114 @@ +#include +#include +#include +#include "wordbyauto.hh" +#include "uiauto.hh" + +#include +#include "dprintf.hh" + +class GDAutomationClient { +public: + GDAutomationClient(); + ~GDAutomationClient(); + bool getWordAtPoint( POINT pt ); + WCHAR *getText() { return buffer; }; +private: + WCHAR buffer[256]; + IUIAutomation *pGDAutomation; + IUIAutomationTreeWalker *pTree; +}; + +GDAutomationClient gdAuto; + +GDAutomationClient::GDAutomationClient() +{ +HRESULT hr; + CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); + hr = CoCreateInstance( CLSID_CUIAutomation , NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, (void**)&pGDAutomation ); + if( hr != S_OK ) pGDAutomation = NULL; + pTree = NULL; + hr = pGDAutomation->get_RawViewWalker( &pTree ); + memset( buffer, 0, sizeof(buffer) ); +} + +GDAutomationClient::~GDAutomationClient() +{ + if( pTree != NULL ) pTree->Release(); + if( pGDAutomation != NULL ) pGDAutomation->Release(); + CoUninitialize(); +} + +bool GDAutomationClient::getWordAtPoint( POINT pt ) +{ +HRESULT hr; +IUIAutomationTextPattern *pTextPattern; +IUIAutomationTextRange *pTextRange; +IUIAutomationElement *pElement, *pParent; +BSTR bstr; +RECT r = { 0, 0, 0, 0 }; +bool bGoUp; + + DPRINTF("\nEntering getWordAtPoint\n"); + + if( pGDAutomation == NULL ) return false; + + buffer[0] = 0; + pElement = NULL; + hr = pGDAutomation->ElementFromPoint( pt, &pElement ); + DPRINTF("ElementFromPoint return hr=%08X, ptr=%p\n", hr, pElement); + if( hr != S_OK || pElement == NULL ) + return false; + + pTextPattern = NULL; + bGoUp = false; + while( pElement != NULL ) { + hr = pElement->GetCurrentPatternAs( UIA_TextPatternId, IID_IUIAutomationTextPattern, (void**)&pTextPattern ); + if( hr == S_OK && pTextPattern != NULL ) + break; + if( pTree == NULL ) { + pElement->Release(); + return false; + } + pParent = NULL; + hr = pTree->GetParentElement( pElement, &pParent ); + pElement->Release(); + pElement = pParent; + bGoUp = TRUE; + } + if( pElement == NULL ) + return false; + + if( !bGoUp ) { + hr = pElement->get_CurrentBoundingRectangle( &r ); + if( hr == S_OK) { + pt.x -= r.left; + pt.y -= r.top; + } + } + pElement->Release(); + + pTextRange = NULL; + hr = pTextPattern->RangeFromPoint( pt, &pTextRange ); + pTextPattern->Release(); + if( hr != S_OK || pTextRange == NULL ) + return false; + + hr = pTextRange->ExpandToEnclosingUnit( TextUnit_Word ); + if( hr == S_OK) { + hr = pTextRange->GetText( 255, &bstr ); + if (hr == S_OK) { + wsprintfW( buffer, L"%s", (LPCWSTR)bstr ); + SysFreeString( bstr ); + } + } + pTextRange->Release(); + + return ( buffer[0] != 0 ); +} + +WCHAR *gdGetWordAtPointByAutomation( POINT pt ) +{ + if( gdAuto.getWordAtPoint( pt ) ) return gdAuto.getText(); + else return NULL; +} diff --git a/wordbyauto.hh b/wordbyauto.hh new file mode 100644 index 00000000..6a4cebbf --- /dev/null +++ b/wordbyauto.hh @@ -0,0 +1,6 @@ +#ifndef __WORD_BY_AUTO_HH_INCLUDED +#define __WORD_BY_AUTO_HH_INCLUDED + +WCHAR *gdGetWordAtPointByAutomation( POINT pt ); + +#endif