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 в список библиотек.