ctypes.cdll.LoadLibrary на dll в заархивированном каталоге - PullRequest
0 голосов
/ 22 февраля 2020

Я призываю мощь StackOverflow, чтобы я мог развернуть это программное обеспечение!

Это программное обеспечение использует ctypes.cdll.LoadLibrary для загрузки библиотеки DLL. При развертывании (искалеченном py2exe) dll спрятан в zip, который содержит структуру каталогов. DLL - это пара уровней глубоко в этой структуре. (Я не уверен, что это важная деталь.) LoadLibrary не работает, потому что он не может найти dll, потому что путь к нему ...\site.zip\app\dll32\lfx.dll.

Я не могу найти ничего для этого в SO или Google. Я подумываю сделать попытку-исключения для вызова LoadLibrary и, в блоке кроме, проверить, относится ли данный путь к разархивированному месту, разархивировать и повторить попытку.

Есть ли более изящный способ загрузить dll из почтового индекса?

1 Ответ

0 голосов
/ 25 февраля 2020

Listing [Python 3.Docs]: ctypes - библиотека сторонних функций для Python.

Для загрузки .dll , CTypes использует:

Оба требуют действительного имени файла, которое существует в FS (или NULL , но здесь это не имеет значения). Предоставленное вами имя файла не соответствует критериям, так как FS не обрабатывает автоматически .zip (или другие) файлы, если они присутствуют в пути.
Таким образом, .dll необходимо распаковать перед загрузкой. Есть несколько способов сделать это, вот тот, который использует Context Manager ( [Python 3.Docs]: Модель данных - с менеджерами контекста оператора ) или CM .

code00.py :

#!/usr/bin/env python

import sys
import ctypes as ct
import zipfile as zf
import os


if sys.platform[:3].lower() == "win":
    from ctypes import wintypes as wt
    unload_lib = ct.WinDLL("kernel32.dll").FreeLibrary
    unload_lib.argtypes = [wt.HMODULE]
    unload_lib.restype = wt.BOOL
else:
    unload_lib = ct.CDLL(None).dlclose
    unload_lib.argtypes = [ct.c_void_p]
    unload_lib.restype = ct.c_int


class ZippedDll():
    def __init__(self, zip_file_name, member_file_name, loader=ct.CDLL, extract_path=None, suppress_exceptions=False):
        self.zip_file_name = zip_file_name
        self.member_file_name = member_file_name
        self.loader = loader
        self.extract_path = extract_path
        self.suppress_exceptions = suppress_exceptions
        self.dll_path = None
        self.dll = None


    def __enter__(self):
        self.dll_path = os.path.join(self.extract_path, self.member_file_name) if self.extract_path else self.member_file_name
        if os.path.exists(self.dll_path):
            self.dll_path = None
            raise OSError("Target file already exists")
        with zf.ZipFile(self.zip_file_name) as zip_file:
            zip_file.extract(self.member_file_name, path=self.extract_path if self.extract_path else None)
        try:
            self.dll = self.loader(self.dll_path)
        except OSError:
            if not self.__exit__(*sys.exc_info()):
                raise
        return self.dll


    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.dll:
            unload_lib(self.dll._handle)
            self.dll = None
        if self.dll_path and os.path.isfile(self.dll_path):
            os.unlink(self.dll_path)
            self.dll_path = None
        return self.suppress_exceptions


def main(*argv):
    with ZippedDll("arch.zip", "dir00/dll00.dll", loader=ct.WinDLL) as dll:
        print(dll._name, dll.dll00Func00)


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main(*sys.argv[1:])
    print("\nDone.")

Примечания :

  • Для этого Например, я использовал архив arch.zip со следующей структурой
    • dir00 /
      • dll00.dll (из другой SO ответ) экспорт функции ( dll00Func00 )
  • CM распаковывает .dll , загружает его и в конце (при выходе из с ) все очищает (кроме промежуточных каталогов)
  • Можно добавить больше обработки ошибок
  • Я не тестировал Nix part

Выход :

e:\Work\Dev\StackOverflow\q060348430>"e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32

dir00/dll00.dll <_FuncPtr object at 0x000002A045422E18>

Done.
...