windows.md
1> **Источник:** https://python-all.ru/3.0/extending/windows.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Сборка расширений C и C++ в Windows89В этой главе кратко объясняется, как создать модуль расширения для Windows на Python с помощью Microsoft Visual C++, после чего приводится более подробная справочная информация о том, как это работает. Материал полезен как программисту Windows, осваивающему сборку расширений Python, так и программисту Unix, заинтересованному в создании программного обеспечения, которое успешно собирается как на Unix, так и на Windows.1011Авторам модулей рекомендуется использовать подход distutils для сборки модулей расширения вместо описанного в этом разделе. При этом всё равно понадобится компилятор C, который использовался для сборки Python; обычно это Microsoft Visual C++.1213> **Примечание**14>15> В этой главе упоминается ряд имен файлов, содержащих закодированный номер версии Python. Эти имена файлов представлены с номером версии в виде `XY`; на практике `'X'` – это номер старшей версии, а `'Y'` – номер младшей версии используемого выпуска Python. Например, если вы используете Python 2.2.1, `XY` на самом деле будет `22`.1617## Подход из сборника рецептов1819В Windows, как и в Unix, есть два подхода к сборке модулей расширения: использовать пакет [`distutils`](https://python-all.ru/3.0/library/distutils.html#module-distutils) для управления процессом сборки или делать всё вручную. Подход distutils хорошо работает для большинства расширений; документация по использованию [`distutils`](https://python-all.ru/3.0/library/distutils.html#module-distutils) для сборки и упаковки модулей расширения доступна в [*Distributing Python Modules*](https://python-all.ru/3.0/distutils/index.html#distutils-index). В этом разделе описывается ручной подход к сборке расширений Python, написанных на C или C++.2021Чтобы собирать расширения по этим инструкциям, нужна копия исходных кодов Python той же версии, что и установленный Python. Потребуется Microsoft Visual C++ «Developer Studio»; файлы проекта предоставляются для VC++ версии 7.1, но можно использовать и более старые версии VC++. Обратите внимание: следует использовать ту же версию VC++, которая использовалась для сборки самого Python. Примеры файлов, описанные здесь, распространяются с исходниками Python в каталоге `PC\example_nt\`.22231. **Скопировать файлы примеров** – Каталог `example_nt` – подкаталог `PC`, чтобы все PC-специфичные файлы находились в одном каталоге в дистрибутиве исходников. Однако использовать каталог `example_nt` непосредственно из этого расположения нельзя. Сначала скопируйте или переместите его на уровень выше, чтобы `example_nt` стал соседним с каталогами `PC` и `Include`. Всю работу выполняйте в этом новом месте.242. **Открыть проект** – В VC++ используйте диалог *File ‣ Open Solution* (не *File ‣ Open*!). Перейдите к `example.sln` в *копии* каталога `example_nt`, созданной на предыдущем шаге, и выберите его. Нажмите Open.253. **Собрать пример DLL** – Чтобы проверить, что всё настроено правильно, попробуйте собрать:264. Выберите конфигурацию. Это необязательный шаг. Выберите *Build ‣ Configuration Manager ‣ Active Solution Configuration*, а затем **Release** или **Debug**. Если пропустить этот шаг, VC++ по умолчанию использует конфигурацию Debug.275. Соберите библиотеку DLL. Выберите *Build ‣ Build Solution*. Будут созданы все промежуточные и результирующие файлы в подкаталоге `Debug` или `Release`, в зависимости от выбранной на предыдущем шаге конфигурации.286. **Тестирование DLL в режиме отладки** – После успешной сборки Debug откройте окно DOS и перейдите в каталог `example_nt\Debug`. Теперь вы сможете повторить следующий сеанс (`C>` – приглашение DOS, `>>>` – приглашение Python; заметьте, что информация о сборке и различный отладочный вывод Python могут не полностью совпадать с этим дампом экрана):2930 ```c31 C>..\..\PCbuild\python_d32 Adding parser accelerators ...33 Done.34 Python 2.2 (#28, Dec 19 2001, 23:26:37) [MSC 32 bit (Intel)] on win3235 Type "copyright", "credits" or "license" for more information.36 >>> import example37 [4897 refs]38 >>> example.foo()39 Hello, world40 [4903 refs]41 >>>42 ```4344 Поздравляем! Вы успешно собрали свой первый модуль расширения Python.457. **Создание собственного проекта** – выберите имя и создайте для него каталог. Скопируйте в него свои исходники на C. Обратите внимание: имя файла исходного модуля не обязательно должно совпадать с именем модуля, но имя функции инициализации должно ему соответствовать – можно импортировать модуль `spam`, только если его функция инициализации называется `initspam`, и она должна вызывать [`Py_InitModule`](https://python-all.ru/3.0/c-api/allocation.html#Py_InitModule) со строкой `"spam"` в качестве первого аргумента (воспользуйтесь минимальным `example.c` в этом каталоге как руководством). По соглашению он находится в файле `spam.c` или `spammodule.c`. Выходной файл должен называться `spam.pyd` (в режиме Release) или `spam_d.pyd` (в режиме Debug). Расширение `.pyd` было выбрано, чтобы избежать путаницы с системной библиотекой `spam.dll`, для которой ваш модуль может быть интерфейсом Python.4647 Теперь возможны следующие варианты:488. **Скопируйте `example.sln` и `example.vcproj`, переименуйте их в**4950 `spam.*`, и отредактируйте их вручную, или519. Создайте совершенно новый проект; инструкции ниже.5253 В любом случае скопируйте `example_nt\example.def` в `spam\spam.def` и отредактируйте новый `spam.def` так, чтобы его вторая строка содержала строку '`initspam`'. Если вы создали новый проект самостоятельно, добавьте файл `spam.def` в проект сейчас. (Это небольшой файл всего из двух строк. Альтернативный подход – забыть о файле `.def` и добавить параметр */export:initspam* куда-нибудь в настройки компоновщика, вручную отредактировав параметр в диалоге Project Properties).5410. **Создание совершенно нового проекта** – Используйте диалог *File ‣ New ‣ Project*, чтобы создать новую рабочую область проекта. Выберите **Visual C++ Projects/Win32/ Win32 Project**, введите имя (`spam`) и убедитесь, что расположение (Location) указывает на родительский каталог созданного вами каталога `spam` (который должен быть прямым подкаталогом дерева сборки Python, рядом с `Include` и `PC`). Выберите Win32 в качестве платформы (в моей версии это единственный вариант). Убедитесь, что переключатель Create new workspace выбран. Нажмите OK.5556 Теперь создайте файл `spam.def`, как указано в предыдущем разделе. Добавьте исходные файлы в проект с помощью *Project ‣ Add Existing Item*. Установите фильтр `*.*` и выберите `spam.c` и `spam.def`, затем нажмите OK. (Можно добавлять их по одному – тоже подойдет.)5758 Теперь откройте диалог *Project ‣ spam properties*. Нужно изменить лишь несколько настроек. Убедитесь, что в раскрывающемся списке **Settings for:** выбрано **All Configurations**. Перейдите на вкладку C/C++. В выпадающем меню вверху выберите категорию General. В поле ввода с надписью **Additional Include Directories** введите следующий текст:5960 ```c61 ..\Include,..\PC62 ```6364 Затем на вкладке Linker выберите категорию General и введите:6566 ```c67 ..\PCbuild68 ```6970 в текстовое поле с надписью **Additional library Directories**.7172 Теперь нужно добавить некоторые настройки, зависящие от режима:7374 Выберите **Release** в раскрывающемся списке **Configuration**. Перейдите на вкладку **Link**, выберите категорию **Input** и добавьте `pythonXY.lib` в список в поле **Additional Dependencies**.7576 Выберите **Debug** в раскрывающемся списке **Configuration** и добавьте `pythonXY_d.lib` в список в поле **Additional Dependencies**. Затем перейдите на вкладку C/C++, выберите **Code Generation** и в раскрывающемся списке **Runtime library** выберите **Multi-threaded Debug DLL**.7778 Снова выберите **Release** из раскрывающегося списка **Configuration**. В раскрывающемся списке **Runtime library** выберите **Multi-threaded DLL**.7980Если ваш модуль создаёт новый тип, при использовании этой строки могут возникнуть проблемы:8182```c83PyObject_HEAD_INIT(&PyType_Type)84```8586Измените на:8788```c89PyObject_HEAD_INIT(NULL)90```9192и добавьте следующее в функцию инициализации модуля:9394```c95MyObject_Type.ob_type = &PyType_Type;96```9798Обратитесь к разделу 3 [Python FAQ](https://python-all.ru/3.0/extending/windows.html) за подробностями о том, почему это необходимо сделать.99100## Различия между Unix и Windows101102В Unix и Windows используются совершенно разные парадигмы загрузки кода во время выполнения. Прежде чем пытаться собрать модуль, который может загружаться динамически, следует понимать, как работает ваша система.103104В Unix файл shared object (`.so`) содержит код для использования программой, а также имена функций и данных, которые он ожидает найти в программе. Когда файл присоединяется к программе, все ссылки на эти функции и данные в коде файла изменяются, чтобы указывать на фактические расположения в памяти программы, где размещены функции и данные. По сути, это операция компоновки.105106В Windows файл динамической библиотеки (DLL) (`.dll`) не имеет висячих ссылок. Вместо этого доступ к функциям или данным осуществляется через таблицу поиска. Таким образом, код DLL не нужно корректировать во время выполнения для обращения к памяти программы; вместо этого код уже использует таблицу поиска DLL, а сама таблица изменяется во время выполнения, чтобы указывать на нужные функции и данные.107108В Unix существует только один тип библиотечного файла (`.a`), который содержит код из нескольких объектных файлов (`.o`). На этапе компоновки для создания файла shared object (`.so`) компоновщик может обнаружить, что не знает, где определён идентификатор. Компоновщик будет искать его в объектных файлах библиотек; если найдёт, он включит весь код из этого объектного файла.109110В Windows существует два типа библиотек: статическая библиотека и библиотека импорта (обе называются `.lib`). Статическая библиотека похожа на файл `.a` в Unix; она содержит код, который будет включён по мере необходимости. Библиотека импорта в основном используется только для подтверждения компоновщику, что определённый идентификатор является допустимым и будет присутствовать в программе при загрузке DLL. Таким образом, компоновщик использует информацию из библиотеки импорта для построения таблицы поиска для идентификаторов, которые не включены в DLL. Когда приложение или DLL компонуется, может быть создана библиотека импорта, которая понадобится для всех будущих DLL, зависящих от символов из этого приложения или DLL.111112Предположим, вы собираете два модуля динамической загрузки, B и C, которые должны совместно использовать другой блок кода A. В Unix вы бы *не* передавали `A.a` компоновщику для `B.so` и `C.so`; это привело бы к его включению дважды, так что B и C имели бы по собственной копии. В Windows сборка `A.dll` также создаст `A.lib`. Вы *действительно* передаёте `A.lib` компоновщику для B и C. `A.lib` не содержит код; он содержит только информацию, которая будет использоваться во время выполнения для доступа к коду A.113114В Windows использование библиотеки импорта отчасти похоже на использование `import spam`; это даёт доступ к именам из spam, но не создаёт отдельную копию. В Unix компоновка с библиотекой больше похожа на `from spam import *`; она создаёт отдельную копию.115116## Использование DLL на практике117118Python для Windows собирается в Microsoft Visual C++; использование других компиляторов может работать или не работать (хотя Borland, похоже, работает). Остальная часть этого раздела относится только к MSVC++.119120При создании DLL в Windows необходимо передавать `pythonXY.lib` компоновщику. Чтобы собрать две DLL, spam и ni (которая использует функции C из spam), можно использовать следующие команды:121122```c123cl /LD /I/python/include spam.c ../libs/pythonXY.lib124cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib125```126127Первая команда создала три файла: `spam.obj`, `spam.dll` и `spam.lib`. `Spam.dll` не содержит никаких функций Python (таких как [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple)), но он умеет находить код Python благодаря `pythonXY.lib`.128129Вторая команда создала `ni.dll` (а также `.obj` и `.lib`), который знает, как найти необходимые функции из spam, а также из исполняемого файла Python.130131Не каждый идентификатор экспортируется в таблицу поиска. Если вы хотите, чтобы другие модули (включая Python) могли видеть ваши идентификаторы, необходимо указать `_declspec(dllexport)`, например `void _declspec(dllexport) initspam(void)` или `PyObject _declspec(dllexport) *NiGetSpamData(void)`.132133Developer Studio добавит много ненужных библиотек импорта, увеличив размер исполняемого файла примерно на 100 КБ. Чтобы избавиться от них, в диалоге Project Settings на вкладке Link укажите *ignore default libraries*. Добавьте правильную `msvcrtxx.lib` в список библиотек.134