【问题标题】:Embedding Python on Windows: why does it have to be a DLL?在 Windows 上嵌入 Python:为什么它必须是 DLL?
【发布时间】:2010-10-17 11:38:53
【问题描述】:

我正在尝试编写一个嵌入 Python 的软件插件。在 Windows 上,插件在技术上是一个 DLL(这可能是相关的)。 Python Windows FAQ 说:

1.直接将 Python 构建到您的 .exe 文件中。在 Windows 上,Python 必须是一个 DLL 来处理本身就是 DLL 的导入模块。 (这是第一个未记录的关键事实。)相反,链接到 pythonNN.dll;它通常安装在 C:\Windows\System 中。 NN 是 Python 版本,一个数字,例如 Python 2.3 的“23”。

我的问题是为什么 Python 必须是 DLL?如果在我的情况下,主机应用程序不是 .exe,而是 DLL,我可以在其中构建 Python 吗?或者,也许,这个注释意味着第三方 C 扩展依赖于 pythonN.N.dll 存在而其他 DLL 不会这样做?假设我真的想要一个 DLL,我应该怎么做?

我看到有dynload_win.c 文件,它似乎是在Windows 上导入C 扩展的模块,据我所知,它扫描扩展文件以查找它导入的pythonX.X.dll;但我没有使用 Windows 的经验,也不太了解那里的所有代码。

【问题讨论】:

    标签: python windows dll


    【解决方案1】:

    您需要将pythonXY.dll 作为 DLL 链接,而不是将相关代码直接链接到您的可执行文件中,否则 Python 运行时无法加载 其他 DLL(它依赖的扩展模块on.) 如果您制作自己的 DLL,理论上您可以直接链接该 DLL 中的所有 Python 代码,因为它最终不会出现在可执行文件中,但仍会出现在 DLL 中。但是,您必须注意正确地进行链接,因为几乎没有任何标准工具(如 distutils)会为您执行此操作。

    但是,无论您如何嵌入 Python,您都无法使用 DLL,也无法仅使用任何 DLL。 ABI 在 Python 版本之间会发生变化,因此如果您针对 Python 2.6 编译代码,则需要 python26.dll;你不能使用python25.dllpython27.dll。 Python 不仅仅是一个 DLL;它还需要它的标准库,其中包括扩展模块(它们本身就是 DLL,尽管它们具有 .pyd 扩展名。)您遇到的 dynload_win.c 中的代码用于加载 那些 DLL,以及与pythonXY.dll的加载无关。

    简而言之,为了在您的插件中嵌入 Python,您需要随插件一起提供 Python,或者要求已经安装了正确的 Python 版本。

    【讨论】:

    • 如果我自己编译标准库扩展并将它们链接到我的 DLL 会怎样(这将由我的 foobar 以及我看到的通常进入 pythonNN.dll 的所有对象组成在pythoncore 项目中)?就我而言,拥有特殊版本的标准库或第三方扩展是可以的。
    • 那么您仍然需要标准库的所有其余部分(所有 .py 文件,以及 .pyc 和 .pyo 文件通常也是如此。)它们也是特定于 Python 版本的。你可以把它们拉上拉链,但你不能避免拥有它们。
    • 所以基本上,如果不将python与python dll捆绑在一起,就无法将python嵌入到您的应用程序中?
    【解决方案2】:

    (对不起,我做了一件愚蠢的事情,我先写了问题,然后注册了,现在我无法更改它或评论回复,因为 StackOverflow 的引擎不认为我是作者。我什至不能正确感谢那些回答的人:(所以这实际上是对问题和 cmets 的更新。)

    感谢所有建议,非常有价值。据我了解,通过一些努力,我可以将 Python 静态链接到自定义 DLL,前提是我自己编译其他动态加载的扩展并将它们链接到同一个 DLL。 (我知道我也需要发布标准库;我的计划是将压缩存档附加到 DLL 文件中。据我了解,我什至可以从中导入纯 Python 模块。)

    我还在dynload_win.c 中发现了一个有趣的地方。 (我知道它会加载使用 Python C API 的动态扩展,例如 _ctypes。)据我所知,它不仅会查找 init_ctypes 符号或任何扩展名,而且还会扫描 .pyd 文件的导入查找(正则表达式)python\d+\. 的表,然后将找到的符号与已知的pythonNN. 字符串进行比较,以确保为该版本的 Python 编译了扩展。如果导入表没有这样的符号或引用另一个版本,则会引发错误。

    对我来说,这意味着:

    • 如果我将扩展链接到 pythonNN.dll 并尝试从包含静态链接 Python 的自定义 DLL 加载它,它将通过检查,但是 - 好吧,我不确定:它会失败,因为有没有pythonNN.dll(即甚至在进行检查之前),否则它会愉快地加载符号?
    • 如果我将它链接到我的自定义 DLL,它会找到符号,但不会通过检查 :)

    我想我可以重写这篇文章以满足我的需要......还有其他这样的地方吗,我想知道。

    【讨论】:

      【解决方案3】:

      Python 需要是一个 dll(具有标准名称),这样您的应用程序和插件才能使用相同的 python 实例。

      插件 dll 已经预计会加载(并从中使用 python)python26.dll(或任何版本) - 如果您的 python 静态嵌入在您的 exe 中,那么将管理两个不同的 python 库实例相同的数据结构。

      如果python库根本不使用静态变量,并且编译设置完全相同相同,这应该不是问题。但是,通常简单地确保只使用一个 Python 解释器实例会更安全。

      【讨论】:

      • 我的看法:如果您查看 .pyd,例如 _ctypes.pyd,您会看到它引用了 pythonxx.dll。所以它期望一个 pythonxx.dll 存在。但是如果你有一个静态的.lib,它就不会存在。理论上 (??) 我猜一个可能有一个配套的 pythonxx.dll 所以导入的模块会很高兴,但我不确定一个模块,如果它加载,会正常工作。
      • Chris Becke,所以如果你的应用程序中有一个 python26.lib 和一个 python26.dll,那么导入的模块就可以完成了吗?
      • 这里的诀窍是有两种 .lib 文件: 静态库 .lib 文件:一种包含完整的实现,另一种只是为了告诉 MSVC 如何链接到配对的 .lib 文件。 dll 文件。后一种——听起来你有——会起作用。
      • Chris Becke,我实际上有前一个完全实现的静态 .lib 文件(构建起来很痛苦......)如果我也有 pythonxx.dll,那么我想导入会起作用,但是将使用(相同)库的不同副本,这是概率。不是一个好主意。
      【解决方案4】:

      在 *nix 上,一个进程中的所有共享对象,包括可执行文件,都将它们导出的名称贡献到一个公共池中;然后,任何共享对象都可以从池中提取任何名称并随意使用它们。这允许例如cStringIO.so 在 Python 库静态链接时从主可执行文件中提取相关的 Python 库函数。

      在 Windows 上,每个共享对象都有自己的独立名称池,它可以使用。这意味着它必须从它需要的函数中读取相关的不同共享对象。由于从主可执行文件中获取所有名称需要大量工作,因此 Python 函数被分离到它们自己的 DLL 中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-30
        • 1970-01-01
        • 1970-01-01
        • 2012-01-24
        • 2012-08-06
        • 2015-09-01
        相关资源
        最近更新 更多