当 AutomationFocusChangedEventHandler 在前台运行应用程序窗口时,如何阻止我的 Windows python 3 应用程序崩溃?

问题描述

我有一个想要在 Windows (10+) 上运行的 python 3 应用程序。但是,当我在 MacBook Pro 上的 Windows-10 Pro Parallels VM 上打开应用程序窗口的情况下运行 onfocus 事件自动化时,应用程序崩溃并显示以下错误:

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at Accessibility.IAccessible.get_accFocus()
   at MS.Internal.AutomationProxies.Accessible.GetFocus()
   at MS.Internal.AutomationProxies.MsaaNativeProvider.System.Windows.Automation.Provider.IRawElementProviderFragmentRoot.GetFocus()
   at MS.Internal.Automation.FocusTracker.GetFocusedElementFromWinEvent(IntPtr hwnd,Int32 idObject,Int32 idChild)
   at MS.Internal.Automation.FocusTracker.HandleFocusChange(IntPtr hwnd,Accessible acc,Int32 idChild,UInt32 eventTime)
   at MS.Internal.Automation.FocusTracker.WinEventProc(Int32 eventId,IntPtr hwnd,UInt32 eventTime)
   at MS.Internal.Automation.WinEventWrap.WinEventReentrancyFilter(Int32 winEventHook,Int32 eventId,Int32 eventThread,UInt32 eventTime)

它不会立即崩溃,但会很快崩溃。下面是一个最小的可重现示例(您需要先安装 dot-net 和 python)。要复制错误,请通过命令提示符运行代码,当我的应用程序窗口打开时,单击无用按钮,然后单击窗口内的背景以更改焦点,然后单击该按钮,然后继续单击窗口中的内容。也许单击其他应用程序,然后在窗口中单击返回。在所有点击后不久,它就会使程序崩溃。

这在我的戴尔上似乎根本没有发生。只是 MacBook Pro。它似乎在戴尔上运行正常。

将 UIAutomation onfocus 绑定线程作为守护程序运行(或不运行)没有区别。

我非常乐意让它只杀死一个有问题的 onfocus 事件,然后处理下一个 onfocus 事件,而不会导致应用程序本身崩溃。甚至杀死所有 onfocus 功能,以便我需要重新启动代码中的 onfocus 监控,只要应用程序本身不会崩溃。但我不知道如何捕捉 onfocus 错误:它们不是来自我的代码。

当然,理想情况下,根本不会抛出任何错误,但如果我至少能在抛出错误时处理它们,那对我来说将是朝着正确方向迈出的一大步。

这是 MRE。我很想知道它是否发生在你身上!欢迎提出任何修复建议!

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import threading

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

import clr

clr.AddReference('../UIAutomationClient')
clr.AddReference('../UIAutomationClientsideProviders')
clr.AddReference('../UIAutomationProvider')
clr.AddReference('../UIAutomationTypes')

from System.Windows.Automation import *


class MyApp:
    qtApp = None
    myWindow = None
    frameMainMenu = None
    frameMainMenuLayout = None
    contentRunContainer = None
    
    def __init__(self):
        self.runApp()
    
    def onFocusChange(self,element,args):
        print('onFocusChange() RUNNING')
        pass

    def launchWindowsEventMonitoring(self):
        #TODO: This randomly crashes the app on Windows.
        try:
            print("----------------------------------")
            print("launchWindowsEventMonitoring() 00")
            focusHandler = AutomationFocusChangedEventHandler(self.onFocusChange)
            Automation.AddAutomationFocusChangedEventHandler(focusHandler)
            print("launchWindowsEventMonitoring() 01")
        except Exception as e:
            print("useful.launchWindowsEventMonitoring() ERROR: " + str(e))
    
    def runApp(self):
        try:
            print('runApp() 00')
            t = threading.Thread(target=self.launchWindowsEventMonitoring)
            #t.setDaemon(True)
            t.start()
            
            self.setupGUI()
            print('runApp() 01')
        except Exception as e:
            print("self.runApp() ERROR: " + str(e))
    
    def setupMainMenuScreen(self):
        try:
            print("setupMainMenuScreen() 00")
            self.frameMainMenu = QFrame()
            self.frameMainMenuLayout = QVBoxLayout()
            self.frameMainMenu.setLayout(self.frameMainMenuLayout)
            
            menuWidget = QWidget()
            menuWidget.setObjectName('menuWidget')
            menuScroller = QScrollArea()
            menuScroller.horizontalScrollbarPolicy = Qt.ScrollBarAlwaysOff
            menuScroller.verticalScrollbarPolicy = Qt.ScrollBarAlwaysOn
            menuScroller.setObjectName('menuScroller')
            self.menuLayout = QVBoxLayout(menuWidget)
            
            btnMyButton = QPushButton('A great button')
            self.menuLayout.addWidget(btnMyButton)
            
            menuScroller.setWidget(menuWidget)
            self.frameMainMenuLayout.addWidget(menuScroller)
            
            self.contentRunContainer.addWidget(self.frameMainMenu)
            print("setupMainMenuScreen() 01")
        except Exception as e:
            print("self.setupMainMenuScreen() ERROR: " + str(e))
    
    def setupGUI(self):
        try:
            print("setupGUI() 00")
            self.qtApp = QApplication([])
            self.qtApp.setQuitOnLastWindowClosed(False)
            self.myWindow = theWindow()
            
            self.theWindowWidget = QWidget()
            self.theWindowWidget.setObjectName('body')
            self.myWindow.setCentralWidget(self.theWindowWidget)
            mainLayout = QVBoxLayout()
            self.theWindowWidget.setLayout(mainLayout)
            
            self.runWrapper = QFrame()
            self.runWrapperLayout = QVBoxLayout()
            self.runWrapper.setLayout(self.runWrapperLayout)
            
            sharedRunWrapper = QVBoxLayout()
            self.contentRunContainer = QVBoxLayout()
            sharedRunWrapper.addLayout(self.contentRunContainer)
            
            self.runWrapperLayout.addLayout(sharedRunWrapper)
            mainLayout.addWidget(self.runWrapper)
            
            self.setupMainMenuScreen()
            self.myWindow.show()
            print("setupGUI() 01")
            
            self.qtApp.exec()
        except Exception as e:
            print("self.setupGUI() ERROR: " + str(e))

class theWindow(QMainWindow):
    def __init__(self):
        try:
            super().__init__()
            self.setWindowTitle("My Application")
        except Exception as e:
            print("theWindow.init() ERROR: " + str(e))
        
if __name__ == "__main__":
    MyApp = MyApp()
    

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)