【问题标题】:ctypes.cdll.LoadLibrary on a dll in a zipped directory压缩目录中 dll 上的 ctypes.cdll.LoadLibrary
【发布时间】:2020-02-22 02:13:37
【问题描述】:

我呼吁 StackOverflow 的力量,我可以部署这个软件!

本软件使用 ctypes.cdll.LoadLibrary 加载 dll。部署时(由 py2exe 破坏),dll 被隐藏在包含目录结构的 zip 中。 dll 在这个结构中有几个层次。 (我不确定这是否是相关细节。)LoadLibrary 失败,因为它找不到 dll,因为它的路径是 ...\site.zip\app\dll32\lfx.dll

我在 SO 或 Google 上找不到任何相关信息。我正在考虑对 LoadLibrary 调用执行 try-except,并在 except 块中检查给定路径是否引用压缩位置,解压缩并重试。

有没有更优雅的方式从 zip 加载 dll?

【问题讨论】:

  • 为什么 .dll.zip 中? .py 调用它的脚本不是也在那个.zip 中吗?您需要提供更多详细信息。如果整个事情在 Py2Exe 上下文之外工作,它应该在其中工作。
  • 在这个项目中,依赖部分是通过将项目分成包来管理的。主包依赖于另一个包装 .dll 的包。依赖包使用基于__file__的相对路径来定位和加载.dll。使用 py2exe 上的 --bundle 2 选项,依赖包 - 以及 .dll - 最终出现在 .zip 文件中。所以相对路径最终是上面提到的形式,无法加载ctypes.cdll.LoadLibrary()

标签: python python-2.7 zip ctypes


【解决方案1】:

上市[Python 3.Docs]: ctypes - A foreign function library for Python

为了加载 .dllCTypes 使用:

两者都需要 FS 上存在的有效文件名(或 NULL,但这无关紧要)。您提供的文件名不符合条件,因为 FS 不会自动处理路径中存在的 .zip(或其他)文件。
所以.dll需要在加载前解压。有很多方法可以做到这一点,这里有一种使用 上下文管理器 ([Python 3.Docs]: Data model - With Statement Context Managers) 或 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,加载它,最后(当退出 with 时)清理所有内容(除了中间目录)
  • 可以添加更多错误处理
  • 我没有测试 Nix 部分

输出

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.

【讨论】:

猜你喜欢
  • 2018-12-10
  • 2019-12-09
  • 2016-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多