4. 在 Windows 上构建 C 和 C 扩展

本章简要说明了如何使用 Microsoft Visual C 为 Python 创建 Windows 扩展模块,然后提供有关其工作方式的更多详细背景信息。对于 Windows 程序员学习如何构建 Python 扩展以及对有兴趣生产可以在 Unix 和 Windows 上成功构建软件的 Unix 程序员而言,该说明性材料都非常有用。

鼓励模块作者使用 distutils 方法来构建扩展模块,而不是本节中介绍的方法。您仍然需要用于构建 Python 的 C 编译器;通常是 Microsoft VisualC。

Note

本章提到了许多文件名,其中包括编码的 Python 版本号。这些文件名以版本号XY表示;实际上,'X'是您使用的 Python 版本的主要版本号,而'Y'是您使用的 Python 版本的次要版本号。例如,如果您使用的是 Python 2.2.1,则XY实际上是22

4.1. 食谱方法

与在 Unix 上一样,有两种在 Windows 上构建扩展模块的方法:使用distutils包控制构建过程,或手动执行操作。 distutils 方法适用于大多数扩展。 分发 Python 模块(旧版)中提供了有关使用distutils构建和打包扩展模块的文档。如果您发现确实需要手动执行操作,则研究winsound标准库模块的项目文件可能会很有帮助。

4.2. Unix 和 Windows 之间的差异

Unix 和 Windows 使用完全不同的范例来运行时加载代码。在try构建可以动态加载的模块之前,请了解系统的工作方式。

在 Unix 中,共享库(.so)文件包含程序要使用的代码,以及它希望在程序中找到的函数和数据的名称。当文件加入程序时,文件代码中对这些Function和数据的所有引用都会更改为指向程序中将Function和数据放置在内存中的实际位置。这基本上是链接操作。

在 Windows 中,动态链接库(.dll)文件没有悬挂的引用。取而代之的是,对Function或数据的访问将pass查找表进行。因此,DLL 代码不必在运行时固定即可引用程序的内存;相反,代码已经使用 DLL 的查找表,并且在运行时修改了查找表以指向函数和数据。

在 Unix 中,只有一种类型的库文件(.a)包含来自多个目标文件(.o)的代码。在创建共享对象文件(.so)的链接步骤中,链接器可能会发现它不知道在哪里定义了标识符。链接器将在库的目标文件中查找它。如果找到它,它将包含该目标文件中的所有代码。

在 Windows 中,有两种类型的库,静态库和 importlib(都称为.lib)。静态库就像 Unix .a文件;它包含必要时包含的代码。importlib 基本上仅用于确保链接程序某个标识符是合法的,并且在加载 DLL 时将出现在程序中。因此,链接器使用 importlib 中的信息来构建查找表,以使用 DLL 中未包含的标识符。链接应用程序或 DLL 时,可能会生成一个 importlib,该 importlib 将需要用于依赖于该应用程序或 DLL 中的符号的所有将来的 DLL。

假设您正在构建两个动态加载模块 B 和 C,它们应该共享另一个代码块 A。在 Unix 上,您A.a传递给B.soC.so的链接器;这将导致它被包含两次,因此 B 和 C 各自拥有自己的副本。在 Windows 中,构建A.dll也将构建A.lib。您将A.lib传递给 B 和 C 的链接器。A.lib不包含代码;请参见。它仅包含将在运行时用于访问 A 的代码的信息。

在 Windows 中,使用 importlib 有点像使用import spam;它使您可以访问垃圾邮件的名称,但不会创建单独的副本。在 Unix 上,与库的链接更像是from spam import *;它确实创建了一个单独的副本。

4.3. 在实践中使用 DLL

Windows Python 是用 Microsoft Visual C 内置的;使用其他编译器可能会或可能不会(尽管 Borland 似乎)。本节的其余部分特定于 MSVC。

在 Windows 中创建 DLL 时,必须将pythonXY.lib传递给链接器。要构建两个 DLL,spam 和 ni(使用垃圾邮件中的 C 函数),可以使用以下命令:

cl /LD /I/python/include spam.c ../libs/pythonXY.lib
cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib

第一个命令创建了三个文件:spam.objspam.dllspam.libSpam.dll不包含任何 Python 函数(例如PyArg_ParseTuple()),但是由于pythonXY.lib,它确实知道如何查找 Python 代码。

第二个命令创建了ni.dll(以及.obj.lib),该命令知道如何从垃圾邮件以及 Python 可执行文件中查找必要的Function。

并非每个标识符都导出到查找表。如果您希望其他模块(包括 Python)能够看到您的标识符,则必须说_declspec(dllexport),如void _declspec(dllexport) initspam(void)PyObject _declspec(dllexport) *NiGetSpamData(void)所示。

Developer Studio 将引入很多您实际上不需要的 importlib,从而为您的可执行文件增加大约 100K。要摆脱它们,请使用“项目设置”对话框的“链接”选项卡指定忽略默认库。将正确的msvcrtxx.lib添加到库列表中。