Python ctypes:从相对路径加载 DLL

Posted

技术标签:

【中文标题】Python ctypes:从相对路径加载 DLL【英文标题】:Python ctypes: loading DLL from from a relative path 【发布时间】:2011-02-28 03:47:31 【问题描述】:

我有一个 Python 模块 wrapper.py,它包装了一个 C DLL。 DLL 与模块位于同一文件夹中。因此,我使用下面的代码来加载它:

myDll = ctypes.CDLL("MyCDLL.dll")

如果我从它自己的文件夹中执行wrapper.py,这将有效。但是,如果我从其他地方运行它,它就会失败。这是因为 ctypes 会计算相对于当前工作目录的路径。

我的问题是,有没有一种方法可以指定 DLL 相对于包装器的路径而不是当前工作目录?这将使我能够将两者一起发布,并允许用户从任何地方运行/导入包装器。

【问题讨论】:

我使用selected answer 中的方法,但在一种情况下,我有一个从另一个 dll 导入导出的 dll,它不会加载。解决方案是先加载另一个 dll,例如:_DIRNAME = os.path.dirname(__file__); required_dll = ctypes.cdll.LoadLibrary(os.path.join(_DIRNAME, required.dll)); main_dll = ctypes.cdll.LoadLibrary(os.path.join(_DIRNAME, main.dll)) 瞧,这行得通! 【参考方案1】:

可以使用os.path.dirname(__file__)获取Python源文件所在目录。

【讨论】:

非常感谢。这正是我想要的。 这样的相对路径可以正常工作,不需要os.path.abspath() 或将此路径添加到$PATH (%PATH%)。需要注意的是,如果您的 dll 需要另一个 dll,那么您也必须加载它,在加载此之前,请参阅我在 OP 问题下的评论。【参考方案2】:

我总是将我的 DLL 所在的目录添加到路径中。这行得通:

os.environ['PATH'] = os.path.dirname(__file__) + ';' + os.environ['PATH']
windll.LoadLibrary('mydll.dll')

请注意,如果您使用 py2exe,这将不起作用(因为未设置 __file__)。在这种情况下,您需要依赖sys.executable 属性(完整说明在http://www.py2exe.org/index.cgi/WhereAmI)

【讨论】:

os.pathsep 是平台安全的方式,用于指示系统中路径条目之间使用的分隔符 $PATH%PATH% 环境变量。但是,我提倡这个答案,因为 IMO 在运行时编辑环境变量是不符合标准的; IMO 它们应该在安装过程中由setup.py 设置。相反,我认为 selected answer 可以解决问题! @MarkMikofski 太傻了。任何编辑环境变量的应用程序都是唯一能看到更改的应用程序。他们不坚持。如果他们这样做,那将是一场安全噩梦。 setup.py 无论如何都无法更改正在运行的应用程序的环境变量。随心所欲地编辑您的环境。 @tritium21 这不是我发表评论的原因,尽管我认为我对设置 PATHsys.path 感到困惑。总的来说,我认为任何稳健、简单和广泛实践的都是最好的。 IMO 将 DLL 的位置添加到 PATH 不如仅使用 DLL 完整路径强大。 @tritium21 顺便说一句:爱你的手柄 请注意,将 DLL 目录添加到 PATH 环境变量中不再适用于 Python 3.8+。您需要改为调用新的os.add_dll_directory(<path>)(您可能希望有条件地调用它 - hasattr(os, "add_dll_directory"): ... - 否则为 Python 3.7 及更低版本修改 PATH。另请参阅 docs.python.org/3.8/whatsnew/3.8.html#changes-in-the-python-api【参考方案3】:

扩展马修的回答:

import os.path
dll_name = "MyCDLL.dll"
dllabspath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + dll_name
myDll = ctypes.CDLL(dllabspath)

这仅适用于脚本,不适用于控制台或 py2exe

【讨论】:

我一般喜欢使用 os.path.sep,但是有没有不使用 ';' 的 Windows 安装路径分隔符? 此路径分隔符是目录分隔符,即 Windows 上的“\”。这与您的方法不同,通过其绝对文件路径加载 dll,而不是修改 PATH 环境变量。我想我更喜欢你的,因为如果没有提供默认系统 dll,它可以回退加载。 os.path.join 对手指和眼睛来说更容易。例如:os.path.abspath(os.path.join(os.path.dirname(__file__), dll_name)) 我认为Chris B. 将os.pathsepos.sep 混淆了。仅供参考:os.sepos.path.sep 是等价的。【参考方案4】:

另一个版本:

dll_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'MyCDLL.dll')
myDll = ctypes.CDLL(dll_file)

【讨论】:

以上是关于Python ctypes:从相对路径加载 DLL的主要内容,如果未能解决你的问题,请参考以下文章

Ctypes找不到我加载的dll的dll依赖

python使用ctypes调用dll

python使用ctypes调用dll

python使用ctypes调用dll

python使用ctypes调用dll

C# Windows 服务 - 无法从相对路径加载外部类库