获取非托管 DLL 路径以独立于版本加载 Python
Posted
技术标签:
【中文标题】获取非托管 DLL 路径以独立于版本加载 Python【英文标题】:Get unmanaged DLL path to load Python independent of the version 【发布时间】:2020-12-28 21:33:37 【问题描述】:我寻找一种方法来检索安装在用户计算机中的 dll 的路径,但路径可能会根据他们决定安装它的位置而改变。找不到任何东西,所以我用自己的发现写了这篇文章(随意添加你自己的)。
一些背景:
我正在编写一个将 Python 加载到 C++ 中的模块(我的用户的机器已经在他们的路径中安装了 Python,但是 Python 版本和路径可能因用户而异)
但是,我发现了 2 个问题:
即使我使用可用于任何 python 3 版本的函数(例如 python 3.6 需要 python36.dll),链接器也会创建 dll 版本依赖项。 必须设置 PYTHONPATH 才能找到已安装的模块。对于第一个问题,我使用 LoadLibrary 在运行时加载适当的 dll,但这仍然给用户留下了配置的负担(他必须配置他的系统中的哪个 dll,以及它的安装位置)。 如果您的用户知道他的配置,则工作正常。
这让我开始猜测:
我能够加载 python3.dll(位于 python36.dll 或 python38.dll 旁边)并且我需要 dll 的路径来计算 PTYHONPATH(并且可能使用 python3 版本来获取正确的 dll使用,如python37.dll、python38.dll等)
【问题讨论】:
【参考方案1】:除非您为 Python 定义 stable ABI,否则我不认为有一个好方法可以允许不同的 Python 次要版本(例如 3.6 或 3.8)。然而,根据我的经验,这导致 Python 的一个糟糕的子集不支持PyMemoryView
之类的东西。如果您可以将代码的 Python 部分隔离到它自己的 DLL,我还建议使用 delay load 链接器标志。这样,您就可以拥有一个配置文件,该文件在运行时读取 Python 库所在的位置并从适当的路径加载它。
【讨论】:
嗨,马特。是的,共同的函数子集很小,但老实说,我完全希望在 python3.dll 中定义其中的许多函数,因为它们在 python36 和 python38 等中是相同的。无论如何,我主要按照你的描述做了,但是延迟加载除外,因为它仍然具有名称依赖性(例如 python36.dll)。我的第一种方法是让用户端的配置(路径和 dll 名称),但事实证明,许多使用 python,但甚至不知道它安装在哪里。这就是为什么我必须自己猜测。 如果您使用稳定的 ABI,那么您将链接到 python3.dll,从而避免次要版本具有的名称依赖性。如果您想让用户更新配置文件,用户可以使用sysconfig.get_path('data')
查询他们的 python 安装目录。
感谢您的提示。稳定 ABI 的文档暗示将 Py_LIMITED_API 设置为支持的最小值应该可以工作,这将是我的首选方法。但是,Python 代码另有说明。它使用简单的#ifndef Py_LIMITED_API
,省略了很多功能。似乎甚至我选择的那些(如加载文件或解析字符串)都被遗漏了,并且一些可用的在 3.9 中已弃用【参考方案2】:
首先,使用 LoadLibraryA(或 LoadLibraryW)加载库,然后使用 GetModuleFileNameA(或 GetModuleFileNameW)获取完整路径
//#include <stdio.h>
//#include <iostream>
HMODULE pythonLib = nullptr;
pythonLib = LoadLibraryA("python3.dll");
if (pythonLib != nullptr)
char path[MAX_PATH];
GetModuleFileNameA(pythonLib, path, MAX_PATH);
std::cout << path << std::endl;
【讨论】:
以上是关于获取非托管 DLL 路径以独立于版本加载 Python的主要内容,如果未能解决你的问题,请参考以下文章