def getNearestWindowHandle(self, UIAElement): if hasattr(UIAElement, "_nearestWindowHandle"): # Called previously. Use cached result. return UIAElement._nearestWindowHandle try: processID = UIAElement.cachedProcessID except COMError: return None appModule = appModuleHandler.getAppModuleFromProcessID(processID) # WDAG (Windows Defender application Guard) UIA elements should be treated as being from a remote machine, and therefore their window handles are completely invalid on this machine. # Therefore, jump all the way up to the root of the WDAG process and use that window handle as it is local to this machine. if appModule.appName == WDAG_PROCESS_NAME: condition = UIAUtils.createUIAMultiPropertyCondition({ UIA_ClassNamePropertyId: [u'ApplicationFrameWindow', u'CabinetWClass'] }) walker = self.clientObject.createTreeWalker(condition) else: # Not WDAG, just walk up to the nearest valid windowHandle walker = self.windowTreeWalker try: new = walker.NormalizeElementBuildCache(UIAElement, self.windowCacheRequest) except COMError: return None try: window = new.cachedNativeWindowHandle except COMError: window = None # Cache for future use to improve performance. UIAElement._nearestWindowHandle = window return window
def _registerGlobalEventHandlers(self): self.clientObject.AddFocusChangedEventHandler(self.baseCacheRequest, self) if isinstance(self.clientObject, UIA.IUIAutomation6): self.globalEventHandlerGroup = self.clientObject.CreateEventHandlerGroup( ) else: self.globalEventHandlerGroup = UIAUtils.FakeEventHandlerGroup( self.clientObject) self.globalEventHandlerGroup.AddPropertyChangedEventHandler( UIA.TreeScope_Subtree, self.baseCacheRequest, self, *self.clientObject.IntSafeArrayToNativeArray( globalEventHandlerGroupUIAPropertyIds if config. conf['UIA']['selectiveEventRegistration'] else UIAPropertyIdsToNVDAEventNames)) for eventId in (globalEventHandlerGroupUIAEventIds if config.conf['UIA']['selectiveEventRegistration'] else UIAEventIdsToNVDAEventNames): self.globalEventHandlerGroup.AddAutomationEventHandler( eventId, UIA.TreeScope_Subtree, self.baseCacheRequest, self) # #7984: add support for notification event (IUIAutomation5, part of Windows 10 build 16299 and later). if isinstance(self.clientObject, UIA.IUIAutomation5): self.globalEventHandlerGroup.AddNotificationEventHandler( UIA.TreeScope_Subtree, self.baseCacheRequest, self) self.addEventHandlerGroup(self.rootElement, self.globalEventHandlerGroup)
def getNearestWindowHandle(self,UIAElement): if hasattr(UIAElement,"_nearestWindowHandle"): # Called previously. Use cached result. return UIAElement._nearestWindowHandle try: processID=UIAElement.cachedProcessID except COMError: return None appModule=appModuleHandler.getAppModuleFromProcessID(processID) # WDAG (Windows Defender application Guard) UIA elements should be treated as being from a remote machine, and therefore their window handles are completely invalid on this machine. # Therefore, jump all the way up to the root of the WDAG process and use that window handle as it is local to this machine. if appModule.appName==WDAG_PROCESS_NAME: condition=UIAUtils.createUIAMultiPropertyCondition({UIA_ClassNamePropertyId:[u'ApplicationFrameWindow',u'CabinetWClass']}) walker=self.clientObject.createTreeWalker(condition) else: # Not WDAG, just walk up to the nearest valid windowHandle walker=self.windowTreeWalker try: new=walker.NormalizeElementBuildCache(UIAElement,self.windowCacheRequest) except COMError: return None try: window=new.cachedNativeWindowHandle except COMError: window=None # Cache for future use to improve performance. UIAElement._nearestWindowHandle=window return window
def _isBadUIAWindowClassName(self, windowClass): "Given a windowClassName, returns True if this is a known problematic UIA implementation." if ( windowClass == "ConsoleWindowClass" and not UIAUtils.shouldUseUIAConsole() ): return True return windowClass in badUIAWindowClassNames
def _createLocalEventHandlerGroup(self): if isinstance(self.clientObject, UIA.IUIAutomation6): self.localEventHandlerGroup = self.clientObject.CreateEventHandlerGroup( ) else: self.localEventHandlerGroup = UIAUtils.FakeEventHandlerGroup( self.clientObject) self.localEventHandlerGroup.AddPropertyChangedEventHandler( UIA.TreeScope_Ancestors | UIA.TreeScope_Element, self.baseCacheRequest, self, *self.clientObject.IntSafeArrayToNativeArray( localEventHandlerGroupUIAPropertyIds)) for eventId in localEventHandlerGroupUIAEventIds: self.localEventHandlerGroup.AddAutomationEventHandler( eventId, UIA.TreeScope_Ancestors | UIA.TreeScope_Element, self.baseCacheRequest, self)
def _isUIAWindowHelper(self,hwnd): # UIA in NVDA's process freezes in Windows 7 and below processID=winUser.getWindowThreadProcessID(hwnd)[0] if windll.kernel32.GetCurrentProcessId()==processID: return False import NVDAObjects.window windowClass=NVDAObjects.window.Window.normalizeWindowClassName(winUser.getClassName(hwnd)) # For certain window classes, we always want to use UIA. if windowClass in goodUIAWindowClassNames: return True # allow the appModule for the window to also choose if this window is good # An appModule should be able to override bad UIA class names as prescribed by core appModule=appModuleHandler.getAppModuleFromProcessID(processID) if appModule and appModule.isGoodUIAWindow(hwnd): return True # There are certain window classes that just had bad UIA implementations if windowClass in badUIAWindowClassNames: return False # allow the appModule for the window to also choose if this window is bad if appModule and appModule.isBadUIAWindow(hwnd): return False if windowClass == "NetUIHWND" and appModule: # NetUIHWND is used for various controls in MS Office. # IAccessible should be used for NetUIHWND in versions older than 2016 # Fixes: lack of focus reporting (#4207), # Fixes: strange reporting of context menu items(#9252), # fixes: not being able to report ribbon sections when they starts with an edit field (#7067) # Note that #7067 is not fixed for Office 2016 and never. # Using IAccessible for NetUIHWND controls causes focus changes not to be reported # when the ribbon is collapsed. # Testing shows that these controls emits proper events but they are ignored by NVDA. isOfficeApp = appModule.productName.startswith(("Microsoft Office", "Microsoft Outlook")) isOffice2013OrOlder = int(appModule.productVersion.split(".")[0]) < 16 if isOfficeApp and isOffice2013OrOlder: parentHwnd = winUser.getAncestor(hwnd, winUser.GA_PARENT) while parentHwnd: if winUser.getClassName(parentHwnd) in ("Net UI Tool Window", "MsoCommandBar",): return False parentHwnd = winUser.getAncestor(parentHwnd, winUser.GA_PARENT) # Ask the window if it supports UIA natively res=windll.UIAutomationCore.UiaHasServerSideProvider(hwnd) if res: # The window does support UIA natively. # Detect if we can also inject in-process canUseOlderInProcessApproach = bool(appModule.helperLocalBindingHandle) # MS Word documents now have a fairly usable UI Automation implementation. # However, builds of MS Office 2016 before build 13901 or so had bugs which # we cannot work around. # Therefore for less recent versions of Office, # if we can inject in-process, refuse to use UIA and instead # fall back to the MS Word object model. if windowClass == "_WwG": isOfficeApp = appModule.productName.startswith(("Microsoft Office", "Microsoft Outlook")) if ( ( winVersion.getWinVer() < winVersion.WIN10 or ( # An MS Office app before build 13901 isOfficeApp and ( tuple(int(x) for x in appModule.productVersion.split('.')[:3]) < (16, 0, 13901) ) ) ) # Disabling is only useful if we can inject in-process (and use our older code) and canUseOlderInProcessApproach # Allow the user to still explicitly force UIA support # no matter the Office version and not config.conf['UIA']['useInMSWordWhenAvailable'] ): return False # MS Excel spreadsheets now have a fairly usable UI Automation implementation. # However, builds of MS Office 2016 before build 9000 or so had bugs which we # cannot work around. # And even current builds of Office 2016 are still missing enough info from UIA # that it is still impossible to switch to UIA completely. # Therefore, if we can inject in-process, refuse to use UIA and instead fall # back to the MS Excel object model. elif ( # An MS Excel spreadsheet window windowClass == "EXCEL7" # Disabling is only useful if we can inject in-process (and use our older code) and appModule.helperLocalBindingHandle # Allow the user to explicitly force UIA support for MS Excel spreadsheets # no matter the Office version and not config.conf['UIA']['useInMSExcelWhenAvailable'] ): return False # Unless explicitly allowed, all Chromium implementations (including Edge) should not be UIA, # As their IA2 implementation is still better at the moment. elif ( windowClass == "Chrome_RenderWidgetHostHWND" and ( AllowUiaInChromium.getConfig() == AllowUiaInChromium.NO # Disabling is only useful if we can inject in-process (and use our older code) or ( canUseOlderInProcessApproach and AllowUiaInChromium.getConfig() != AllowUiaInChromium.YES # Users can prefer to use UIA ) ) ): return False elif windowClass == "ConsoleWindowClass": return UIAUtils._shouldUseUIAConsole(hwnd) return bool(res)