python - 将 kivy 与 pywinauto 一起使用时出现 ctypes.ArgumentError

标签 python kivy ctypes pywinauto

我有一个 kivy 应用程序,它可以使用 pywinauto 模块与其他窗口交互。该应用程序在 Linux 中运行良好(未使用 pywinauto)但在 Windows 中我收到以下错误,该应用程序甚至无法启动:

C:\Program Files (x86)\Python36_64\lib\site-packages\pywinauto\__init__.py:80: UserWarning: Revert to STA COM threading mode
    warnings.warn("Revert to STA COM threading mode", UserWarning)
[INFO   ] [GL          ] NPOT texture support is available
[INFO   ] [Base        ] Start application main loop
Traceback (most recent call last):
File ".\application.py", line 368, in <module>
    Application().run()
File "C:\Program Files (x86)\Python36_64\lib\site-packages\kivy\app.py", line 826, in run
    runTouchApp()
File "C:\Program Files (x86)\Python36_64\lib\site-packages\kivy\base.py", line 477, in runTouchApp
    EventLoop.start()
File "C:\Program Files (x86)\Python36_64\lib\site-packages\kivy\base.py", line 164, in start
    provider.start()
File "C:\Program Files (x86)\Python36_64\lib\site-packages\kivy\input\providers\wm_touch.py", line 68, in start
    self.hwnd, GWL_WNDPROC, self.new_windProc)
ctypes.ArgumentError: argument 3: <class 'TypeError'>: wrong type

我认为这是 pywinauto 的问题的原因是我有以下几行并且它在 Linux 中运行良好:
if SYSTEM == "Windows":
    import win32gui
    import win32process
    import wmi

    from pywinauto import application
    import pywinauto

我还注释掉了 pywinauto 导入行,然后它就开始了。它可以链接到this issue.我真的不知道要包含哪些代码,因为它可以在其他操作系统中使用……我假设 pywinauto 正在改变一些阻止 kivy 工作的东西。

我的问题是:如何在同一个应用程序中同时拥有 kivy 和 pywinauto 的功能?

最佳答案

我能够使用以下方法重现该行为:

  • Python 3.7.3 x64
  • 基维 1.10.1
  • Pywinauto 0.6.6

  • 作为旁注,我之前没有使用过这两个包中的任何一个,我 pip install专门为这项任务编辑了他们。

    由于我不知道如何重现该行为,所以我只是从 [GitHub]: pywinauto/pywinauto - ctypes.ArgumentError @ click_input 复制了 MCVE (你也在问题中分享了),并稍微修改了它(只是为了解决错误,没有风格,改进,......等等)。

    代码.py:

    import random
    from kivy.app           import App
    from kivy.lang          import Builder
    from kivy.core.window   import Window
    from kivy.uix.boxlayout import BoxLayout
    
    import pywinauto  # @TODO - cfati: moved after Kivy import(s), as it works otherwise (https://github.com/pywinauto/pywinauto/issues/419#issuecomment-488258224)
    
    
    class DemoLayout(BoxLayout): pass
    Builder.load_string("""
    #: import datetime  datetime.datetime
    <DemoLayout>:
      padding: 75
    
      Button:
        on_press: print(f"PRESSED @ {datetime.now()}")
    """)
    
    
    class Demo(App):
    
      def build(self):
        self.root = DemoLayout()
    
      def on_start(self):
        title = f"__KIVY_APP__{random.getrandbits(128)}"
        Window.set_title(title)
        hwnd = pywinauto.findwindows.find_window(title=title)
        app = pywinauto.Application()
        app.connect(handle=hwnd)
        window = app.window(handle=hwnd).wrapper_object()
        window.click_input(button="left", pressed="", coords=(100, 100), double=False, absolute=False)
    
    
    Demo().run()
    

    输出 :

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q055928463]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py
    [INFO   ] [Logger      ] Record log in C:\Users\cfati\.kivy\logs\kivy_19-05-01_83.txt
    [INFO   ] [Kivy        ] v1.10.1
    [INFO   ] [Python      ] v3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
    [INFO   ] [Factory     ] 194 symbols loaded
    [INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil, img_gif (img_ffpyplayer ignored)
    [INFO   ] [Window      ] Provider: sdl2
    [INFO   ] [GL          ] Using the "OpenGL" graphics system
    [INFO   ] [GL          ] GLEW initialization succeeded
    [INFO   ] [GL          ] Backend used <glew>
    [INFO   ] [GL          ] OpenGL version <b'4.5.0 - Build 23.20.16.4973'>
    [INFO   ] [GL          ] OpenGL vendor <b'Intel'>
    [INFO   ] [GL          ] OpenGL renderer <b'Intel(R) HD Graphics 530'>
    [INFO   ] [GL          ] OpenGL parsed version: 4, 5
    [INFO   ] [GL          ] Shading version <b'4.50 - Build 23.20.16.4973'>
    [INFO   ] [GL          ] Texture max size <16384>
    [INFO   ] [GL          ] Texture max units <32>
    [INFO   ] [Window      ] auto add sdl2 input provider
    [INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
     e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pywinauto\__init__.py:80: UserWarning: Revert to STA COM threading mode
       warnings.warn("Revert to STA COM threading mode", UserWarning)
    [INFO   ] [Text        ] Provider: sdl2
    [INFO   ] [Base        ] Start application main loop
     Traceback (most recent call last):
       File "code.py", line 36, in <module>
         Demo().run()
       File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\kivy\app.py", line 826, in run
         runTouchApp()
       File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\kivy\base.py", line 477, in runTouchApp
         EventLoop.start()
       File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\kivy\base.py", line 164, in start
         provider.start()
       File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\kivy\input\providers\wm_touch.py", line 68, in start
         self.hwnd, GWL_WNDPROC, self.new_windProc)
     ctypes.ArgumentError: argument 3: <class 'TypeError'>: wrong type
    


    在进一步讨论之前,我想指出:
  • [Python 3.Docs]: ctypes - A foreign function library for Python
  • [MS.Docs]: SetWindowLongPtrW function

  • 从后者可以看出,SetWindowLongPtrW 的第三个参数可以是一个 DWORD、一个 HANDLE、一个函数指针,这取决于第二个参数值:基本上它是一个可以映射到任何东西的 void*。两个模块都通过 ctypes 调用这个函数:

  • Pywinauto ( [GitHub]: pywinauto/pywinauto - (0.6.6) pywinauto/pywinauto/win32functions.py ):

    try:
        SetWindowLongPtr    =   ctypes.windll.user32.SetWindowLongPtrW
        SetWindowLongPtr.argtypes = [win32structures.HWND, ctypes.c_int, win32structures.LONG_PTR]
        SetWindowLongPtr.restype = win32structures.LONG_PTR
    except AttributeError:
        SetWindowLongPtr = SetWindowLong
    
  • Kivy ( [GitHub]: kivy/kivy - (1.10.1) kivy/kivy/input/providers/wm_common.py ):

    try:
        windll.user32.SetWindowLongPtrW.restype = WNDPROC
        windll.user32.SetWindowLongPtrW.argtypes = [HANDLE, c_int, WNDPROC]
        SetWindowLong_wrapper = windll.user32.SetWindowLongPtrW
    except AttributeError:
        windll.user32.SetWindowLongW.restype = WNDPROC
        windll.user32.SetWindowLongW.argtypes = [HANDLE, c_int, WNDPROC]
        SetWindowLong_wrapper = windll.user32.SetWindowLongW
    

  • 说明 :
  • user32.dll 在当前 Python 进程中只加载一次
  • 上面的代码初始化函数,通常只在模块导入时执行一次(可以根据需要执行多次,但会降低性能)
  • 两个模块都指定了函数(windll.user32.SetWindowLongPtrW,因为我们在 64 位上)原型(prototype),但他们这样做 不同
  • 从上面的 3,结果 最后导入的模块决定了函数原型(prototype)的外观
  • 当第一次导入的模块尝试使用原型(prototype)时,它与传递的参数不匹配 ,因此出现错误

  • 这就是为什么我必须在 Kivy 之后移动 Pywinauto 导入,以便 Kivy 尝试使用 Pywinauto 的原型(prototype)调用该函数,否则它会起作用。它是
    可能反过来,但我没有费心找到 Pywinauto 会调用该函数的场景,因为它不相关。

    查看 2 个 ctypes 原型(prototype)和 C 原型(prototype)(来自 MS URL),结果是:
  • Pywinauto 做事正确(不过我很好奇它在 32 位上是如何工作的)
  • Kivy 仅使用 SetWindowLongPtrW 用例(带有函数指针的用例),为了使事情更容易,他们针对自己的场景调整了原型(prototype)。然而,它与 C 原型(prototype)不太匹配 ,这来自我的 PoV,看起来像是一个蹩脚的解决方法(gainarie)

  • 我修改了我的 Kivy 安装,tadaa! (这是复活节兔子!:)):

    Working

    备注 :这里遇到的(更简单的)变体:[SO]: How to keep pynput and ctypes from clashing?



    更新 #0

    我已提交 [GitHub]: kivy/kivy - SetWindowLongPtrW ctypes prototype bug , 其中 已合并 .不过,不确定它什么时候会在市场上销售( PyPI ,所以你可以简单地 pip install )。

    作为替代方案,您可以下载补丁,并在本地应用更改。查询 [SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer) ( 修补 utrunner 部分)关于如何在 Win 上应用补丁(基本上,以 开头的每一行 | 一个“+” 符号进入,以 开头的每一行一个“-” 标志熄灭)。我正在使用 Cygwin,顺便说一句。或者您可以下载 3 个修改过的文件并覆盖现有的文件。无论如何,备份它们 第一的!另外,我不知道这些更改如何适应旧的 Kivy 版本。

    关于python - 将 kivy 与 pywinauto 一起使用时出现 ctypes.ArgumentError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55928463/

    相关文章:

    python - 根据不同的列值分配唯一值

    Python Ctypes 异常 : access violation reading

    python - OpenCV Python 接口(interface)和 ctypes 库之间的互操作性

    python - 对多个字段执行 OR 或将这些字段值组合成单个字段名是否更高效

    python - 没有这样的过滤器 '"拆分' : ffplay with ffmpeg in Python

    python - 大数据矩阵分解推荐系统给出MemoryError

    python - 奇异果和游戏中的声音:游戏更新循环等待声音完成,然后继续[奇异果中使用SoundLoader的FPS问题]

    android - 排行榜中的分数未更新

    android - Windows 上的 Kivy 独立 android apk

    python - Pyglet OpenGL 设置雾颜色