-2

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 UIAutomationClient DLL (only UIAutomationCore)
  • Tried using clear_comtypes_cache.py as a workaround in Dockerfile but didn't work (if I did it correctly)
  • Tried different versions of comtypes and found that this kind of error was happening until fixed on 1.1.9 (latest is 1.2.0, I'm using this one)
  • too much code, please reduce to this: [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) – D.L Jun 29 '23 at 20:11
  • Thanks! Tried to cut everything outside the focus of the question itself. Let me know if it's enough :) – João Pedro Guimarães Jun 29 '23 at 20:33
  • is the error coming from `main.py` or `bot.py` ? make it clear, cut out all the code apart from the bit that has the error, so that its clear and maybe somebody can help you (it will also be clearer for you too). – D.L Jun 29 '23 at 20:38
  • Thanks again. I wrote some explanations to make it clearer and cut down code. – João Pedro Guimarães Jun 29 '23 at 21:10

1 Answers1

0

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

Pedro Luz
  • 973
  • 5
  • 14