编程语言
首页 > 编程语言> > 使用kivy和pywinauto时的python-ctypes.ArgumentError

使用kivy和pywinauto时的python-ctypes.ArgumentError

作者:互联网

我有一个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
> Kivy 1.10.1
> Pywinauto 0.6.6

作为旁注,之前我没有使用过任何2个软件包,我专门为这个任务安装了它们.

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

code.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()

输出:

06001

在进一步说明之前,我想指出:

> [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位上)原型,但它们以不同的方式执行
>从上面的3中,最后导入模块的结果决定了函数原型的样子
>当导入的模块1st尝试使用原型时,它与传递的参数不匹配,因此错误

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

看看2个ctypes原型和C one原型(来自MS URL),结果是:

> Pywinauto正在做正确的事情(我很好奇它如何在32位上运行)
> Kivy只使用SetWindowLongPtrW用例(带有函数指针的用例),为了简化操作,他们根据场景调整了原型.但是,它与C原型并不完全匹配,而且这个来自我的PoV,看起来像一个蹩脚的解决方法(获得)

我修改了我的Kivy装置和tadaa! (这是复活节兔子!:)):

Working

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

@ EDIT0:

我已经提交了合并的[GitHub]: kivy/kivy – SetWindowLongPtrW ctypes prototype bug.不知道什么时候它将在市场上出售(PyPI,所以你可以简单地将其安装).

作为替代方案,您可以下载修补程序,并在本地应用更改.检查[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版本.

标签:kivy,python,pywinauto,ctypes
来源: https://codeday.me/bug/20191008/1870904.html