fix: remove ctypes.WinDLL preload — use only os.add_dll_directory

ctypes.WinDLL uses LoadLibraryW which does NOT search user DLL dirs
registered via os.add_dll_directory/AddDllDirectory. It was throwing
FileNotFoundError and blocking the import before tesserocr was ever tried.

Python 3.8+ loads .pyd files with LOAD_LIBRARY_SEARCH_USER_DIRS which
DOES search user-registered dirs for the pyd and all its transitive DLL
dependencies. os.add_dll_directory(Library\bin) alone is sufficient.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
alex
2026-05-24 14:19:18 +02:00
co-authored by Claude Sonnet 4.6
parent d9383cc164
commit 77ad97022e
4 changed files with 24 additions and 41 deletions
+5 -2
View File
@@ -104,14 +104,17 @@ echo Installing tesserocr wheel...
"%PYTHON%" -m pip install --force-reinstall "%~dp0dependencies\tesserocr-2.5.2-cp310-cp310-win_amd64.whl" >nul 2>&1
:: --- Smoke test: import key dependencies ---
:: Note: os.add_dll_directory (not ctypes.WinDLL) is the correct mechanism.
:: Python 3.8+ loads .pyd files with LOAD_LIBRARY_SEARCH_USER_DIRS which
:: searches user-registered dirs for the pyd AND all its transitive deps.
echo.
echo Verifying dependencies...
"%PYTHON%" -c "import os,sys,ctypes;dirs=[d for d in [os.path.join(sys.prefix,'Library',x) for x in ['bin','mingw-w64\\bin','usr\\bin']] if os.path.isdir(d)];[os.add_dll_directory(d) for d in dirs];os.environ.__setitem__('PATH',os.pathsep.join(dirs)+os.pathsep+os.environ.get('PATH',''));t=os.path.join(sys.prefix,'Library','bin','tesseract51.dll');ctypes.WinDLL(t) if os.path.isfile(t) else None;import cv2,mss,numpy,tesserocr,discord,transitions,rapidfuzz" >nul 2>&1
"%PYTHON%" -c "import os,sys;[os.add_dll_directory(d) for d in [os.path.join(sys.prefix,'Library',x) for x in ['bin','mingw-w64\\bin','usr\\bin']] if os.path.isdir(d)];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,ctypes; lb=os.path.join(sys.prefix,'Library','bin'); 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'); dirs=[d for d in [os.path.join(sys.prefix,'Library',x) for x in ['bin','mingw-w64\\bin','usr\\bin']] if os.path.isdir(d)]; [os.add_dll_directory(d) for d in dirs]; os.environ.__setitem__('PATH',os.pathsep.join(dirs)+os.pathsep+os.environ.get('PATH','')); t=os.path.join(lb,'tesseract51.dll'); r=ctypes.WinDLL(t) if os.path.isfile(t) else 'missing'; print(' ctypes tesseract51.dll:',type(r).__name__); __import__('tesserocr'); print(' tesserocr import: OK')" 2>&1
"%PYTHON%" -c "import os,sys; lb=os.path.join(sys.prefix,'Library','bin'); 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(d) for d in [os.path.join(sys.prefix,'Library',x) for x in ['bin','mingw-w64\\bin','usr\\bin']] if os.path.isdir(d)]; import tesserocr; print(' tesserocr import: OK')" 2>&1
echo.
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.
+8 -19
View File
@@ -1,28 +1,17 @@
import os
import sys
if os.name == 'nt':
import ctypes
# Collect all conda DLL dirs that exist
_conda_dll_dirs = [d for d in [
# Python 3.8+ loads .pyd files with LOAD_LIBRARY_SEARCH_USER_DIRS, which
# searches directories registered via os.add_dll_directory() — including
# all transitive DLL dependencies. This is the correct mechanism for
# making tesserocr find tesseract51.dll and leptonica without conda activate.
for _d in [
os.path.join(sys.prefix, 'Library', 'bin'),
os.path.join(sys.prefix, 'Library', 'mingw-w64', 'bin'),
os.path.join(sys.prefix, 'Library', 'usr', 'bin'),
] if os.path.isdir(d)]
# 1) os.add_dll_directory: lets Python find tesserocr's direct deps when
# loading the .pyd via LOAD_LIBRARY_SEARCH_USER_DIRS.
for _d in _conda_dll_dirs:
os.add_dll_directory(_d)
# 2) Prepend to PATH: when ctypes.WinDLL loads tesseract51.dll by absolute
# path, Windows resolves *that DLL's* own transitive deps (leptonica,
# zlib, libpng, …) using the standard search order: app-dir → System32
# → Windows-dir → cwd → PATH. Those libs live in Library\bin, not
# System32, so without PATH they won't be found.
os.environ['PATH'] = os.pathsep.join(_conda_dll_dirs) + os.pathsep + os.environ.get('PATH', '')
# 3) Pre-load tesseract51.dll now (PATH is set, so all its deps resolve).
# Once it's in the DLL cache the .pyd import below reuses it.
_tess = os.path.join(sys.prefix, 'Library', 'bin', 'tesseract51.dll')
if os.path.isfile(_tess):
ctypes.WinDLL(_tess)
]:
if os.path.isdir(_d):
os.add_dll_directory(_d)
from tesserocr import PyTessBaseAPI, OEM
import numpy as np
import cv2
+6 -11
View File
@@ -1,22 +1,17 @@
# Fix: On Windows, Python 3.8+ requires os.add_dll_directory for conda-forge DLLs
# that tesserocr depends on (tesseract51.dll, leptonica-1.78.0.dll).
# This must run BEFORE any import that might trigger tesserocr loading.
# os.add_dll_directory registers dirs with LOAD_LIBRARY_SEARCH_USER_DIRS,
# which applies to the .pyd and all its transitive DLL dependencies.
import os, sys
if sys.platform == "win32":
import ctypes
_bundled = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "conda_env", "Library", "bin")
_conda_dll_dirs = [d for d in [
_bundled,
for _d in [
os.path.join(sys.prefix, "Library", "bin"),
os.path.join(sys.prefix, "Library", "mingw-w64", "bin"),
os.path.join(sys.prefix, "Library", "usr", "bin"),
] if os.path.isdir(d)]
for _d in _conda_dll_dirs:
os.add_dll_directory(_d)
os.environ['PATH'] = os.pathsep.join(_conda_dll_dirs) + os.pathsep + os.environ.get('PATH', '')
_tess = os.path.join(sys.prefix, "Library", "bin", "tesseract51.dll")
if os.path.isfile(_tess):
ctypes.WinDLL(_tess)
]:
if os.path.isdir(_d):
os.add_dll_directory(_d)
from dataclasses import dataclass
from input_layer import keyboard
+5 -9
View File
@@ -1,18 +1,14 @@
# Fix: On Windows, Python 3.8+ requires os.add_dll_directory for conda-forge DLLs
# that tesserocr depends on (tesseract51.dll, leptonica-1.78.0.dll).
import os, sys
if sys.platform == "win32":
import ctypes
_conda_dll_dirs = [d for d in [
for _d in [
os.path.join(sys.prefix, "Library", "bin"),
os.path.join(sys.prefix, "Library", "mingw-w64", "bin"),
os.path.join(sys.prefix, "Library", "usr", "bin"),
] if os.path.isdir(d)]
for _d in _conda_dll_dirs:
os.add_dll_directory(_d)
os.environ['PATH'] = os.pathsep.join(_conda_dll_dirs) + os.pathsep + os.environ.get('PATH', '')
_tess = os.path.join(sys.prefix, "Library", "bin", "tesseract51.dll")
if os.path.isfile(_tess):
ctypes.WinDLL(_tess)
]:
if os.path.isdir(_d):
os.add_dll_directory(_d)
from beautifultable import BeautifulTable
import logging