I was able to reproduce the behavior using:
- Python 3.7.3 x64
- Kivy 1.10.1
- Pywinauto 0.6.6
As a side note, I didn't work with any of the 2 packages before, I pip install
ed them specifically for this task.
Since I had no idea how to reproduce the behavior, I just copied the MCVE from [GitHub]: pywinauto/pywinauto - ctypes.ArgumentError @ click_input (that you also shared in the question), and slightly modified it (only to hit the error, no style, improvements, ... and so on).
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()
Output:
[cfati@CFATI-5510-0:e:WorkDevStackOverflowq055928463]> "e:WorkDevVEnvspy_064_03.07.03_test0Scriptspython.exe" code.py
[INFO ] [Logger ] Record log in C:Userscfati.kivylogskivy_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:WorkDevVEnvspy_064_03.07.03_test0libsite-packagespywinauto\__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:WorkDevVEnvspy_064_03.07.03_test0libsite-packageskivyapp.py", line 826, in run
runTouchApp()
File "e:WorkDevVEnvspy_064_03.07.03_test0libsite-packageskivyase.py", line 477, in runTouchApp
EventLoop.start()
File "e:WorkDevVEnvspy_064_03.07.03_test0libsite-packageskivyase.py", line 164, in start
provider.start()
File "e:WorkDevVEnvspy_064_03.07.03_test0libsite-packageskivyinputproviderswm_touch.py", line 68, in start
self.hwnd, GWL_WNDPROC, self.new_windProc)
ctypes.ArgumentError: argument 3: <class 'TypeError'>: wrong type
Before going further, I want to point out:
- [Python 3.Docs]: ctypes - A foreign function library for Python
- [MS.Docs]: SetWindowLongPtrW function
As seen from the latter, SetWindowLongPtrW's 3rd argument can be a DWORD, a HANDLE, a function pointer, depending on the 2nd argument value: basically it's a void* that can be mapped to anything.
Both modules call this function via 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
Explanation:
- user32.dll is only loaded once in the current Python process
- Code as above initializes the functions and typically is only executed once, at module import time (it can be executed as many times as desired, but it would decrease performance)
- Both modules specify the function (windll.user32.SetWindowLongPtrW as we are on 64bit) prototype, but they do it differently
- From the 3 above, results that the module that gets imported last, decides how the function prototype will look like
- When the module that was imported 1st tries to use the prototype, it doesn't match the arguments passed, hence the error
That's why I had to move Pywinauto import after Kivy, so that Kivy tried to call the function using Pywinauto's prototype, otherwise it would have worked.
It's
possible to go the other way around, but I didn't bother to find a scenario where Pywinauto would call the function, as it's not relevant.
Looking at the 2 ctypes prototypes and the C one (from MS URL), it turns out that:
- Pywinauto is doing things correctly (I'm curious how it would work on 32bit, though)
- Kivy only uses a SetWindowLongPtrW use-case (the one with the function pointer), and to make things easier, they adapted the prototype for their scenario. However, it doesn't quite match the C prototype, and this from my PoV, looks like a lame workaround (gainarie)
I modified my Kivy installation, and tadaa! (it's the Easter Bunny! :) ):
Note: A (simpler) variant encountered here: [SO]: How to keep pynput and ctypes from clashing?
Update #0
I've submitted [GitHub]: kivy/kivy - SetWindowLongPtrW ctypes prototype bug, which was merged. Not sure when it will be available on the market (PyPI, so you can simply pip install
it), though.
As an alternative, you could download the patch, and apply the changes locally. Check [SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer) (Patching utrunner section) for how to apply patches on Win (basically, every line that starts with one "+" sign goes in, and every line that starts with one "-" sign goes out). I am using Cygwin, btw.
Or you could download the 3 modified files and overwrite your existing ones.
In any case, back them up first! Also, I don't know how the changes fit into older Kivy versions.