I'm trying to put Pywinauto, Wine and Docker to work together for a desktop RPA project.
The general idea is to run a Windows application within Wine inside a Ubuntu Docker container and then run a Python script with Pywinauto to take control over that application. I'm also using Xvfb to emulate a display.
After a lot of research to get here now I'm facing the problem when running the Python script with pywinauto import:
AttributeError: module 'comtypes.gen' has no attribute 'UIAutomationClient'
This error happens when some script tries to import pywinauto.
VERSIONS
Ubuntu 20.04
Wine 8.0.1 (Windows 10, using winehq-stable)
Python 3.11.4
Pywinauto 0.6.8
Comtypes 1.2.0
Here are some of the project files to help reproduce the error.
Dockerfile:
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND noninteractive
ARG WINE_VERSION=winehq-stable
ARG PYTHON_VERSION=3.11.4
ENV PYTHONUNBUFFERED=0
RUN set -x \
&& dpkg --add-architecture i386 \
&& mkdir -pm755 /etc/apt/keyrings \
&& apt-get update -qy \
&& apt-get install --no-install-recommends -qfy gpg-agent rename apt-transport-https software-properties-common winbind cabextract wget curl zip unzip xvfb xdotool x11-utils xterm \
&& wget -nv https://dl.winehq.org/wine-builds/winehq.key \
&& apt-key add winehq.key \
&& add-apt-repository 'https://dl.winehq.org/wine-builds/ubuntu' \
&& apt-get update -qy \
&& apt-get install --install-recommends -qfy $WINE_VERSION winetricks \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENV WINEARCH win64
ENV WINEDEBUG fixme-all
ENV WINEPREFIX /wine
RUN set -x \
&& winetricks -q win10
ENV W_PYTHON_PATH="C:/Python"
RUN set -x \
&& for msifile in `echo core dev exe lib path pip tcltk tools`; do \
wget -nv "https://www.python.org/ftp/python/$PYTHON_VERSION/win32/${msifile}.msi"; \
wine msiexec /i "${msifile}.msi" /qb TARGETDIR="$W_PYTHON_PATH" \
rm ${msifile}.msi; \
done \
&& cd /wine/drive_c/Python
RUN echo 'wine '\''$W_PYTHON_PATH\python.exe'\'' "$@"' > /usr/bin/python \
&& echo 'wine '\''$W_PYTHON_PATH\Scripts\easy_install.exe'\'' "$@"' > /usr/bin/easy_install \
&& echo 'wine '\''$W_PYTHON_PATH\Scripts\pip.exe'\'' "$@"' > /usr/bin/pip \
&& echo 'wine '\''$W_PYTHON_PATH\Scripts\pyinstaller.exe'\'' "$@"' > /usr/bin/pyinstaller \
&& echo 'wine '\''$W_PYTHON_PATH\Scripts\pyupdater.exe'\'' "$@"' > /usr/bin/pyupdater \
&& echo 'assoc .py=PythonScript' | wine cmd \
&& echo 'ftype PythonScript=$W_PYTHON_PATH\python.exe "%1" %*' | wine cmd \
&& while pgrep wineserver >/dev/null; do echo "Waiting for wineserver"; sleep 1; done \
&& chmod +x /usr/bin/python /usr/bin/easy_install /usr/bin/pip /usr/bin/pyinstaller /usr/bin/pyupdater \
&& (pip install -U pip || true) \
&& rm -rf /tmp/.wine-*
ENV W_DRIVE_C="/wine/drive_c"
ENV W_WINDIR_UNIX="$W_DRIVE_C/windows"
ENV W_SYSTEM64_DLLS="$W_WINDIR_UNIX/system32"
ENV W_TMP="$W_DRIVE_C/windows/temp/_$0"
ENV PYTHON_EXE="$W_DRIVE_C/Python/python.exe"
ENV PYTHON="/wine/drive_c/Python/python.exe"
ENV PIP="/wine/drive_c/Python/Scripts/pip3.11.exe"
RUN wine $PYTHON -m venv venv
ENV PIPENV="/wine/drive_c/Python/Scripts/pipenv.exe"
RUN mkdir /src/ && ln -s /src /wine/drive_c/src
COPY . /src
WORKDIR /wine/drive_c/src/
ENV WINEPATH="C:\Python\python.exe;C:\Python\Scripts;C:\Python\Lib\site-packages;"
ENV VIRTUAL_ENV=venv
RUN wine $PYTHON -m venv $VIRTUAL_ENV
ENV WINEPATH="$VIRTUAL_ENV/bin:$WINEPATH"
RUN wine $PIP install -r requirements.txt
# comtypes workaround
CMD [ "$PYTHON /wine/drive_c/Python/Scripts/clear_comtypes_cache.py -Y" ]
CMD [ "wine $PIP uninstall comtypes" ]
CMD [ "wine $PIP install --no-cache-dir comtypes==1.2.0" ]
ENTRYPOINT [ "/bin/sh", "-c", "/usr/bin/xvfb-run -a $@", "" ]
CMD [ "wine", "/wine/drive_c/Python/python.exe", "bot.py" ]
requirements.txt (full file since I can't really remember the specific libs):
attrs==23.1.0
certifi==2023.5.7
charset-normalizer==3.1.0
comtypes==1.2.0
graypy==2.1.0
idna==3.4
isodate==0.6.1
lxml==4.9.2
Pillow==9.5.0
platformdirs==3.5.1
pycryptodome==3.18.0
pydantic==1.10.8
python-dotenv==1.0.0
pytz==2023.3
pywinauto==0.6.8
requests==2.31.0
requests-file==1.5.1
requests-toolbelt==1.0.0
setuptools==67.8.0
six==1.16.0
typing-extensions==4.6.2
urllib3==2.0.2
wenv==0.5.1
zeep==4.2.1
pytest==7.3.2
Imports excerpt from bot.py, where the error happens:
(...)
from pywinauto import Application # line 10, where the error is in traceback
from pywinauto.controls.uia_controls import TabControlWrapper
from pywinauto.findwindows import ElementNotFoundError
(...)
Traceback:
Traceback (most recent call last):
File "Z:\src\main.py", line 8, in <module>
from robot.bot import Bot
File "Z:\src\robot\bot.py", line 10, in <module>
from pywinauto import Application
File "C:\Python\Lib\site-packages\pywinauto\__init__.py", line 89, in <module>
from . import findwindows
File "C:\Python\Lib\site-packages\pywinauto\findwindows.py", line 42, in <modu
le>
from . import controls
File "C:\Python\Lib\site-packages\pywinauto\controls\__init__.py", line 36, in
<module>
from . import uiawrapper # register "uia" back-end (at the end of uiawrapper
module)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Python\Lib\site-packages\pywinauto\controls\uiawrapper.py", line 47,
in <module>
from ..uia_defines import IUIA
File "C:\Python\Lib\site-packages\pywinauto\uia_defines.py", line 181, in <mod
ule>
pattern_ids = _build_pattern_ids_dic()
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Python\Lib\site-packages\pywinauto\uia_defines.py", line 169, in _bui
ld_pattern_ids_dic
if hasattr(IUIA().ui_automation_client, cls_name):
^^^^^^
File "C:\Python\Lib\site-packages\pywinauto\uia_defines.py", line 50, in __cal
l__
cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Python\Lib\site-packages\pywinauto\uia_defines.py", line 61, in __ini
t__
self.ui_automation_client = comtypes.gen.UIAutomationClient
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'comtypes.gen' has no attribute 'UIAutomationClient'
X connection to :99 broken (explicit kill or server shutdown).
X connection to :99 broken (explicit kill or server shutdown).
From traceback there's a file main.py that imports bot.py but I adapted the Dockerfile to work with only a bot.py as it shows in files above.
Some considerations:
- Checked and Wine doesn't have the
UIAutomationClientDLL (onlyUIAutomationCore) - Tried using
clear_comtypes_cache.pyas a workaround in Dockerfile but didn't work (if I did it correctly) - Tried different versions of
comtypesand found that this kind of error was happening until fixed on1.1.9(latest is1.2.0, I'm using this one)
I'm fairly sure there's a way to fix it by installing some UI Automation or something like that through winetricks, since I don't remember it right now, I'll leave you with the quick and dirty version:
Commenting lines 34 to 37 on the library source file linked below did the trick for me, hope it helps.
Good to note that this will in fact disable the UIA wrapper entirely and rely solely on the Win32 one.
https://github.com/pywinauto/pywinauto/blob/aea0429b14f9e471974889246b3c4455b93508f3/pywinauto/controls/init.py#L34