5. Создание расширений C и C++ в Windows

В этой главе кратко объясняется, как создать модуль расширения Windows для Python с помощью Microsoft Visual C++, а также приводится более подробная справочная информация о его работе. Пояснительный материал полезен как для программиста Windows, учившегося строить расширения Python, так и для программиста Unix, заинтересованного в производстве программного обеспечения, которое может быть успешно построено как на Unix, так и на Windows.

Авторам модулей рекомендуется использовать distutils подход для построения модулей расширения вместо описанного в этом разделе. Компилятор C, который был используемый для построения Python, все равно понадобится; обычно Microsoft 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 вы не передаете A.a линкеру для B.so и C.so; это приведет к тому, что он будет включен дважды, так что B и C будут иметь свои собственные копии. в Windows, сборка A.dll также будет строить A.lib. Вы передаете A.lib линкеру для B и C. A.lib не содержит код; он просто содержит информацию, которая будет использоваться во время выполнения для доступа к код A.

В Windows использование импортируемой библиотеки подобно использованию import spam; она дает вам доступ к именам spam, но не создает отдельной копии. в Unix связь с библиотекой больше похожа на from spam import *; создается отдельная копия.

5.3. Использование DLL на практике

Windows Python встроена в Microsoft Visual C++; использование других компиляторов может работать или не работать (хотя Borland кажется). Остальная часть этого раздела относится к MSVC++.

При создании DLL в Windows необходимо передать pythonXY.lib линкеру. Чтобы построить две DLL, spam и ni (которые используют функции C, найденные в spam), можно использовать эти команды:

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), которая умеет находить необходимые функции из spam, а также из Python исполняемого файла.

Не каждый идентификатор экспортируется в таблицу экспорта. Если вы хотите, чтобы другие модули (включая Python) могли видеть ваши идентификаторы, вы должны сказать _declspec(dllexport), как в void _declspec(dllexport) initspam(void) или PyObject _declspec(dllexport) *NiGetSpamData(void).

Developer Studio вложит в исполняемый файл множество библиотек импорта, которые вам не очень нужны, добавляя около 100K. Чтобы избавиться от них, укажите игнорировать библиотеки по умолчанию в диалоговом окне «Параметры проекта» на вкладке «Связь». Добавьте правильный msvcrtxx.lib в список библиотек.