5. 在Windows上构建C和C++扩展

本章简要介绍了如何使用微软Visual C++为Python创建Windows扩展模块,并遵循其如何工作的更详细的背景信息。解释性材料对于学习如何构建Python扩展的Windows程序员和对生成可以在Unix和Windows上成功构建的软件感兴趣的Unix程序员都很有用。

鼓励模块作者使用distutils方法来构建扩展模块,而不是本节中描述的方法。您仍然需要用于构建Python的C编译器;通常是微软Visual C++。

注解

本章提到了许多包含编码的python版本号的文件名。这些文件名用版本号表示,显示为 XY 在实践中 'X' 将是主要版本号和 'Y' 将是您正在使用的Python版本的次要版本号。例如,如果您使用的是python 2.2.1, XY 实际上 22 .

5.1. 秘诀方法

有两种方法可以在Windows上构建扩展模块,就像在UNIX上一样:使用 distutils 包来控制构建过程,或者手动执行操作。distutils方法对大多数扩展都很有效;有关使用的文档 distutils 生成和打包扩展模块可在 分发python模块(旧版本) . 如果您发现您确实需要手动操作,那么研究 winsound 标准库模块。

5.2. Unix和Windows之间的区别

Unix和Windows使用完全不同的模式来运行时加载代码。在尝试构建可动态加载的模块之前,请注意系统的工作方式。

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

在Windows中,动态链接库 (.dll )文件没有悬空引用。相反,对函数或数据的访问通过查找表进行。因此,不必在运行时修复dll代码以引用程序的内存;相反,代码已经使用了dll的查找表,并且在运行时修改了查找表以指向函数和数据。

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

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

假设您正在构建两个动态加载模块b和c,它们应该共享另一个代码块a。在UNIX上,您将 not 通过 A.a 到链接器 B.soC.so ;这将导致它被包括两次,这样B和C将各自拥有自己的副本。在Windows中,构建文件 A.dll 也将建造 A.lib . 你 do 通过 A.lib 到B和C的链接器。 A.lib 不包含代码;它只包含在运行时用于访问A的代码的信息。

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

5.3. 在实践中使用DLLs

Windows Python是在微软Visual C++中构建的;使用其他编译器可能会或可能不起作用(尽管Borland似乎)。本节的其余部分是特定于MSVC++的。

在Windows中创建DLL时,必须通过 pythonXY.lib 到链接器。要构建两个DLL,SPAM和NI(使用SPAM中的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.lib . Spam.dll 不包含任何python函数(例如 PyArg_ParseTuple() 但是它知道如何找到python代码,这要归功于 pythonXY.lib .

创建的第二个命令 ni.dll (和 .obj.lib ,它知道如何从垃圾邮件和Python可执行文件中找到必要的函数。

并非所有标识符都导出到查找表中。如果您希望任何其他模块(包括python)能够看到您的标识符,您必须说 _declspec(dllexport) ,如 void _declspec(dllexport) initspam(void)PyObject _declspec(dllexport) *NiGetSpamData(void) .

DeveloperStudio将提供许多您不真正需要的导入库,为您的可执行文件添加大约100K。要清除它们,请使用“项目设置”对话框的“链接”选项卡指定 忽略默认库 . 添加正确 msvcrtxx.lib 到库列表。