fix: pre-load tesseract51.dll by absolute path to fix transitive DLL deps

os.add_dll_directory alone is not enough on Windows. When Python loads
the tesserocr .pyd via LOAD_LIBRARY_SEARCH_USER_DIRS, Windows finds
tesseract51.dll in the added directory but then resolves tesseract's own
transitive deps (leptonica, zlib, libpng etc.) using only the standard
system search path -- not the user DLL dirs. Those libs live in
Library\bin, not System32, so they're invisible and the load fails even
though every DLL is present.

Fix: call ctypes.WinDLL(absolute_path_to_tesseract51.dll) before the
tesserocr import. LoadLibraryW with a full path anchors tesseract51.dll
to Library\bin, so Windows searches that directory for its transitive
deps. The already-loaded DLL is then returned from cache when the .pyd
requests it, making the import succeed.

Applied to ocr.py (test entry point), main.py, and shopper.py.
Also updated install.bat smoke test and diagnostic to use the same fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
alex
2026-05-24 13:59:21 +02:00
parent ee940b7e3d
commit f8380af3bd
4 changed files with 27 additions and 6 deletions

View File

@@ -99,15 +99,15 @@ echo Installing tesserocr wheel...
:: --- Smoke test: import key dependencies ---
echo.
echo Verifying dependencies...
"%PYTHON%" -c "import os,sys;[os.add_dll_directory(p) for p in [os.path.join(sys.prefix,'Library',d) for d in ['bin','mingw-w64\\bin','usr\\bin']] if os.path.isdir(p)];import cv2,mss,numpy,tesserocr,discord,transitions,rapidfuzz" >nul 2>&1
"%PYTHON%" -c "import os,sys,ctypes;lb=os.path.join(sys.prefix,'Library','bin');[os.add_dll_directory(os.path.join(sys.prefix,'Library',d)) for d in ['bin','mingw-w64\\bin','usr\\bin'] if os.path.isdir(os.path.join(sys.prefix,'Library',d))];ctypes.WinDLL(os.path.join(lb,'tesseract51.dll')) if os.path.isfile(os.path.join(lb,'tesseract51.dll')) else None;import cv2,mss,numpy,tesserocr,discord,transitions,rapidfuzz" >nul 2>&1
if %errorlevel% neq 0 (
echo.
echo WARNING: Dependency check failed. Running diagnostics...
echo.
"%PYTHON%" -c "import os,sys; lb=os.path.join(sys.prefix,'Library','bin'); print(' Prefix:',sys.prefix); print(' Library/bin exists:',os.path.isdir(lb)); dlls=[f for f in (os.listdir(lb) if os.path.isdir(lb) else []) if any(k in f.lower() for k in ['tess','lepton'])]; print(' Tesseract DLLs found:',dlls if dlls else 'NONE - re-run install.bat'); [os.add_dll_directory(os.path.join(sys.prefix,'Library',d)) for d in ['bin','mingw-w64\\bin','usr\\bin'] if os.path.isdir(os.path.join(sys.prefix,'Library',d))]; __import__('tesserocr')" 2>&1
"%PYTHON%" -c "import os,sys,ctypes; lb=os.path.join(sys.prefix,'Library','bin'); print(' Prefix:',sys.prefix); dlls=[f for f in (os.listdir(lb) if os.path.isdir(lb) else []) if any(k in f.lower() for k in ['tess','lepton'])]; print(' Tesseract DLLs in Library/bin:',dlls or 'NONE'); [os.add_dll_directory(os.path.join(sys.prefix,'Library',d)) for d in ['bin','mingw-w64\\bin','usr\\bin'] if os.path.isdir(os.path.join(sys.prefix,'Library',d))]; t=os.path.join(lb,'tesseract51.dll'); r=ctypes.WinDLL(t) if os.path.isfile(t) else None; print(' ctypes load tesseract51.dll:','OK' if r else 'skipped'); __import__('tesserocr'); print(' tesserocr import: OK')" 2>&1
echo.
echo If you see "NONE" above, re-run install.bat once more to let conda finish.
echo If the error persists, see development.md for manual steps.
echo *** Do NOT run "pip install tesserocr" manually - it builds from source and
echo *** will always fail on Windows. Re-run install.bat to fix dependencies.
) else (
echo All key dependencies verified.
)

View File

@@ -1,7 +1,8 @@
import os
import sys
if os.name == 'nt':
# Python 3.8+ requires explicit DLL directories; conda puts tesseract/leptonica in Library\bin
import ctypes
# Add all conda DLL dirs so Python's loader can find them
for _dll_dir in [
os.path.join(sys.prefix, 'Library', 'bin'),
os.path.join(sys.prefix, 'Library', 'mingw-w64', 'bin'),
@@ -9,6 +10,16 @@ if os.name == 'nt':
]:
if os.path.isdir(_dll_dir):
os.add_dll_directory(_dll_dir)
# Pre-load tesseract51.dll by absolute path so Windows anchors it to
# Library\bin when resolving its own transitive deps (leptonica, zlib, etc.).
# os.add_dll_directory alone is not enough: when Python loads the .pyd via
# LOAD_LIBRARY_SEARCH_USER_DIRS, Windows finds tesseract51.dll there, but
# then resolves tesseract's transitive deps using only System32 — not the
# user DLL dirs. Loading by absolute path first puts tesseract in the cache
# with Library\bin as its home, fixing the transitive search.
_tess = os.path.join(sys.prefix, 'Library', 'bin', 'tesseract51.dll')
if os.path.isfile(_tess):
ctypes.WinDLL(_tess)
from tesserocr import PyTessBaseAPI, OEM
import numpy as np
import cv2

View File

@@ -3,8 +3,8 @@
# This must run BEFORE any import that might trigger tesserocr loading.
import os, sys
if sys.platform == "win32":
import ctypes
_conda_dll_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "conda_env", "Library", "bin")
# Also check the conda env where this python lives (sys.prefix is reliable on Windows)
for _dll_candidate in [
_conda_dll_dir,
os.path.join(sys.prefix, "Library", "bin"),
@@ -13,6 +13,12 @@ if sys.platform == "win32":
]:
if os.path.isdir(_dll_candidate):
os.add_dll_directory(_dll_candidate)
# Pre-load tesseract51.dll by absolute path so Windows anchors its transitive
# deps (leptonica, zlib, etc.) to Library\bin. os.add_dll_directory alone is
# insufficient: transitive deps of a user-dir DLL are not searched there.
_tess = os.path.join(sys.prefix, "Library", "bin", "tesseract51.dll")
if os.path.isfile(_tess):
ctypes.WinDLL(_tess)
from dataclasses import dataclass
from input_layer import keyboard

View File

@@ -1,6 +1,7 @@
# Fix: On Windows, Python 3.8+ requires os.add_dll_directory for conda-forge DLLs
import os, sys
if sys.platform == "win32":
import ctypes
for _dll_dir in [
os.path.join(sys.prefix, "Library", "bin"),
os.path.join(sys.prefix, "Library", "mingw-w64", "bin"),
@@ -8,6 +9,9 @@ if sys.platform == "win32":
]:
if os.path.isdir(_dll_dir):
os.add_dll_directory(_dll_dir)
_tess = os.path.join(sys.prefix, "Library", "bin", "tesseract51.dll")
if os.path.isfile(_tess):
ctypes.WinDLL(_tess)
from beautifultable import BeautifulTable
import logging