4. Building C and C++ Extensions on Windows¶
本章简要说明如何使用Microsoft Visual C ++为Python创建Windows扩展模块,并提供有关如何工作的更详细的背景信息。说明材料对于Windows程序员学习构建Python扩展和Unix程序员对生产可以在Unix和Windows上成功构建的软件都很有用。
鼓励模块作者使用distutils方法构建扩展模块,而不是本节中描述的模块。你仍然需要用于构建Python的C编译器;通常是Microsoft Visual C ++。
注意
本章提到了一些包含编码的Python版本号的文件名。这些文件名用显示为XY
的版本号表示;在实践中,'X'
将是主要版本号,'Y'
将是您正在使用的Python版本的次要版本号。例如,如果您使用Python 2.2.1,则XY
实际上是22
。
4.1. A Cookbook Approach¶
有两种方法在Windows上构建扩展模块,就像在Unix上一样:使用distutils
包来控制构建过程,或手动执行。distutils方法适用于大多数扩展;关于使用distutils
来构建和打包扩展模块的文档位于Distributing Python Modules (Legacy version)中。如果您发现您确实需要手动执行操作,则可以参考winsound标准库模块的项目文件。
4.2. Differences Between Unix and 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上,您不会将A.a
传递给B.so
和C.so
的链接器;这将导致它被包括两次,所以B和C每个都有自己的副本。在Windows中,构建A.dll
也会构建A.lib
。您do将A.lib
传递给B和C的链接器。A.lib
不包含代码;它只包含将在运行时访问A的代码使用的信息。
在Windows中,使用导入库就像使用import spam
;它允许您访问垃圾邮件的名称,但不会创建单独的副本。在Unix上,与库的链接更像是从 垃圾邮件 导入 * 它确实创建一个单独的副本。
4.3. Using DLLs in Practice¶
Windows Python内置在Microsoft Visual C ++;使用其他编译器可能或可能不工作(虽然Borland似乎)。本节的其余部分是MSVC ++特定的。
在Windows中创建DLL时,必须将pythonXY.lib
传递给链接器。要构建两个DLL,垃圾邮件和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.obj
,spam.dll
和spam.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)
。
Developer Studio会抛出很多你不需要的导入库,为你的可执行文件添加大约100K。要删除它们,请使用“项目设置”对话框的“链接”选项卡指定忽略默认库。将正确的msvcrtxx.lib
添加到库列表中。