Проблема pyinstaller и ctypes

Написал код для дистанционного управления устройством через телеграм бота, скомпилировал в exe при помощи pyinstaller -F -w bot.py, а при попытке открыть exe файл, я получаю ошибку. Нужный DLL как и полагается находится ровно в той же папке что и exe файл Текст ошибки и код ниже:

import ctypes
import os
import telebot
CWD = os.path.dirname(__file__)
dllpath = os.path.join(CWD, "FCS.dll")
FuncsDLL = ctypes.CDLL(dllpath)

sumF = FuncsDLL.addNum
sumF.argtypes = [ctypes.c_int]
sumF.restype = ctypes.c_int

off = FuncsDLL.off
off.argtypes = []
off.restype = None

fmsbx = FuncsDLL.fmsgbox
fmsbx.argtypes = []
fmsbx.restype = None

lock = FuncsDLL.sclock
lock.argtypes = []
lock.restype = None

hideAll = FuncsDLL.sclock
hideAll.argtypes = []
hideAll.restype = None


bot = telebot.TeleBot("token")
last_message_id = 0
pref = "v1 "
@bot.message_handler(func=lambda msg: msg.message_id > last_message_id)
def handler(msg):
    global last_message_id
    last_message_id = msg.message_id
    if msg.text == pref+"off":
        off()
    if msg.text == pref+"error":
        fmsbx()
    if msg.text == pref+"lock":
        lock()
    if msg.text == pref+"hideAll":
        hideAll()

bot.polling(none_stop=True)

Ошибка:

Traceback (most recent call last):
  File "PyInstaller\loader\pyimod03_ctypes.py", line 53, in __init__
  File "ctypes\__init__.py", line 379, in __init__
FileNotFoundError: Could not find module 'C:\Users\DimoNULL\AppData\Local\Temp\_MEI6122\FCS.dll' (or one of its dependencies). Try using the full path with constructor syntax.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tele.py", line 6, in <module>
  File "PyInstaller\loader\pyimod03_ctypes.py", line 55, in __init__
pyimod03_ctypes.install.<locals>.PyInstallerImportError: Failed to load dynlib/dll 'C:\\Users\\DimoNULL\\AppData\\Local\\Temp\\_MEI6122\\FCS.dll'. Most likely this dynlib/dll was not found when the application was frozen.

Ответы (1 шт):

Автор решения: Amgarak

Когда вы запускаете свой скрипт (не упакованный), путь к текущему рабочему каталогу (CWD) будет тем каталогом, в котором находится ваш скрипт. В моём случае, скрипт запускается из каталога "C:\Users\Amgarak\Desktop", поэтому CWD будет равен этому пути.

import os
print(__file__) # C:\Users\Amgarak\Desktop\133.py
CWD = os.path.dirname(__file__)
print(CWD) # C:\Users\Amgarak\Desktop

Но если упаковать свой скрипт в исполняемый файл, то при запуске exe он будет создавать временный каталог для распаковки всех необходимых файлов, включая сам скрипт и любые другие файлы, которые он использует. Путь к этому временному каталогу будет выглядеть примерно так: "C:\Users\Amgarak\AppData\Local\Temp_MEI173362".

import os
print(__file__) # C:\Users\Amgarak\AppData\Local\Temp\_MEI202722\133.py
CWD = os.path.dirname(__file__)
print(CWD) # C:\Users\Amgarak\AppData\Local\Temp\_MEI202722\

Соответственно скрипт запускается из временного каталога, CWD будет указывать на этот временный каталог, а не на каталог, где лежит exe. Поэтому при попытке создать путь к файлу "FCS.dll" с помощью os.path.dirname, будет указывать на каталог временных файлов, а не на каталог c exe.

print(sys.executable) # C:\Users\Amgarak\Desktop\output\133.exe
exe_dir = os.path.dirname(sys.executable)
print(exe_dir) # C:\Users\Amgarak\Desktop\output

Таким образом можно получить путь, где лежит непосредственно наш exe. Впрочем, можно воспользоваться и относительным путем:

with open('output.txt', 'r') as file:
    value = file.read()
    print(value) # Python generation!

Мой файл 'output.txt' лежит в той же директории что и запускаемый exe.


Так что, если dll в той же папке что и exe, можете использовать имя dll без указания пути:

FuncsDLL = ctypes.CDLL("FCS.dll")
→ Ссылка