windows.md
1> **Источник:** https://python-all.ru/2.6/extending/windows.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 4. Сборка расширений 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## 4.1. Подход «Кулинарная книга»1819В Windows, как и в Unix, есть два подхода к сборке модулей расширения: использовать пакет [`distutils`](https://python-all.ru/2.6/library/distutils.html#module-distutils) для управления процессом сборки или делать всё вручную. Подход distutils хорошо работает для большинства расширений; документация по использованию [`distutils`](https://python-all.ru/2.6/library/distutils.html#module-distutils) для сборки и упаковки модулей расширения доступна в [*Distributing Python Modules*](https://python-all.ru/2.6/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. **Creating your own project** – Choose a name and create a directory for it. Copy your C sources into it. Note that the module source file name does not necessarily have to match the module name, but the name of the initialization function should match the module name – you can only import a module `spam` if its initialization function is called `initspam()`, and it should call [`Py_InitModule()`](https://python-all.ru/2.6/c-api/allocation.html#Py_InitModule) with the string `"spam"` as its first argument (use the minimal `example.c` in this directory as a guide). By convention, it lives in a file called `spam.c` or `spammodule.c`. The output file should be called `spam.pyd` (in Release mode) or `spam_d.pyd` (in Debug mode). The extension `.pyd` was chosen to avoid confusion with a system library `spam.dll` to which your module could be a Python interface.4647 Изменено в версии 2.5: Ранее также распознавались имена файлов, такие как `spam.dll` (в режиме release) или `spam_d.dll` (в режиме debug).4849 Теперь возможны следующие варианты:508. **Скопируйте `example.sln` и `example.vcproj`, переименуйте их в**5152 `spam.*`, и отредактируйте их вручную, или539. Создайте совершенно новый проект; инструкции ниже.5455 В любом случае скопируйте `example_nt\example.def` в `spam\spam.def` и отредактируйте новый `spam.def` так, чтобы его вторая строка содержала строку '`initspam`'. Если вы создали новый проект самостоятельно, добавьте файл `spam.def` в проект сейчас. (Это небольшой файл всего из двух строк. Альтернативный подход – забыть о файле `.def` и добавить параметр */export:initspam* куда-нибудь в настройки компоновщика, вручную отредактировав параметр в диалоге Project Properties).5610. **Создание совершенно нового проекта** – Используйте диалог *File ‣ New ‣ Project*, чтобы создать новую рабочую область проекта. Выберите **Visual C++ Projects/Win32/ Win32 Project**, введите имя (`spam`) и убедитесь, что расположение (Location) указывает на родительский каталог созданного вами каталога `spam` (который должен быть прямым подкаталогом дерева сборки Python, рядом с `Include` и `PC`). Выберите Win32 в качестве платформы (в моей версии это единственный вариант). Убедитесь, что переключатель Create new workspace выбран. Нажмите OK.5758 Теперь создайте файл `spam.def`, как указано в предыдущем разделе. Добавьте исходные файлы в проект с помощью *Project ‣ Add Existing Item*. Установите фильтр `*.*` и выберите `spam.c` и `spam.def`, затем нажмите OK. (Можно добавлять их по одному – тоже подойдет.)5960 Теперь откройте диалог *Project ‣ spam properties*. Нужно изменить лишь несколько настроек. Убедитесь, что в раскрывающемся списке **Settings for:** выбрано **All Configurations**. Перейдите на вкладку C/C++. В выпадающем меню вверху выберите категорию General. В поле ввода с надписью **Additional Include Directories** введите следующий текст:6162 ```c63 ..\Include,..\PC64 ```6566 Затем на вкладке Linker выберите категорию General и введите:6768 ```c69 ..\PCbuild70 ```7172 в текстовое поле с надписью **Additional library Directories**.7374 Теперь нужно добавить некоторые настройки, зависящие от режима:7576 Выберите **Release** в раскрывающемся списке **Configuration**. Перейдите на вкладку **Link**, выберите категорию **Input** и добавьте `pythonXY.lib` в список в поле **Additional Dependencies**.7778 Выберите **Debug** в раскрывающемся списке **Configuration** и добавьте `pythonXY_d.lib` в список в поле **Additional Dependencies**. Затем перейдите на вкладку C/C++, выберите **Code Generation** и в раскрывающемся списке **Runtime library** выберите **Multi-threaded Debug DLL**.7980 Снова выберите **Release** из раскрывающегося списка **Configuration**. В раскрывающемся списке **Runtime library** выберите **Multi-threaded DLL**.8182Если ваш модуль создаёт новый тип, при использовании этой строки могут возникнуть проблемы:8384```c85PyObject_HEAD_INIT(&PyType_Type)86```8788Измените на:8990```c91PyObject_HEAD_INIT(NULL)92```9394и добавьте следующее в функцию инициализации модуля:9596```c97MyObject_Type.ob_type = &PyType_Type;98```99100Обратитесь к разделу 3 [Python FAQ](https://python-all.ru/2.6/extending/windows.html) за подробностями о том, почему это необходимо сделать.101102## 4.2. Различия между Unix и Windows103104В Unix и Windows используются совершенно разные парадигмы загрузки кода во время выполнения. Прежде чем пытаться собрать модуль, который может загружаться динамически, следует понимать, как работает ваша система.105106В Unix файл shared object (`.so`) содержит код для использования программой, а также имена функций и данных, которые он ожидает найти в программе. Когда файл присоединяется к программе, все ссылки на эти функции и данные в коде файла изменяются, чтобы указывать на фактические расположения в памяти программы, где размещены функции и данные. По сути, это операция компоновки.107108В Windows файл динамической библиотеки (DLL) (`.dll`) не имеет висячих ссылок. Вместо этого доступ к функциям или данным осуществляется через таблицу поиска. Таким образом, код DLL не нужно корректировать во время выполнения для обращения к памяти программы; вместо этого код уже использует таблицу поиска DLL, а сама таблица изменяется во время выполнения, чтобы указывать на нужные функции и данные.109110В Unix существует только один тип библиотечного файла (`.a`), который содержит код из нескольких объектных файлов (`.o`). На этапе компоновки для создания файла shared object (`.so`) компоновщик может обнаружить, что не знает, где определён идентификатор. Компоновщик будет искать его в объектных файлах библиотек; если найдёт, он включит весь код из этого объектного файла.111112В Windows существует два типа библиотек: статическая библиотека и библиотека импорта (обе называются `.lib`). Статическая библиотека похожа на файл `.a` в Unix; она содержит код, который будет включён по мере необходимости. Библиотека импорта в основном используется только для подтверждения компоновщику, что определённый идентификатор является допустимым и будет присутствовать в программе при загрузке DLL. Таким образом, компоновщик использует информацию из библиотеки импорта для построения таблицы поиска для идентификаторов, которые не включены в DLL. Когда приложение или DLL компонуется, может быть создана библиотека импорта, которая понадобится для всех будущих DLL, зависящих от символов из этого приложения или DLL.113114Предположим, вы собираете два модуля динамической загрузки, B и C, которые должны совместно использовать другой блок кода A. В Unix вы бы *не* передавали `A.a` компоновщику для `B.so` и `C.so`; это привело бы к его включению дважды, так что B и C имели бы по собственной копии. В Windows сборка `A.dll` также создаст `A.lib`. Вы *действительно* передаёте `A.lib` компоновщику для B и C. `A.lib` не содержит код; он содержит только информацию, которая будет использоваться во время выполнения для доступа к коду A.115116В Windows использование библиотеки импорта отчасти похоже на использование `import spam`; это даёт доступ к именам из spam, но не создаёт отдельную копию. В Unix компоновка с библиотекой больше похожа на `from spam import *`; она создаёт отдельную копию.117118## 4.3. Использование DLL на практике119120Python для Windows собирается в Microsoft Visual C++; использование других компиляторов может работать или не работать (хотя Borland, похоже, работает). Остальная часть этого раздела относится только к MSVC++.121122При создании DLL в Windows необходимо передавать `pythonXY.lib` компоновщику. Чтобы собрать две DLL, spam и ni (которая использует функции C из spam), можно использовать следующие команды:123124```c125cl /LD /I/python/include spam.c ../libs/pythonXY.lib126cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib127```128129Первая команда создала три файла: `spam.obj`, `spam.dll` и `spam.lib`. `Spam.dll` не содержит никаких функций Python (таких как [`PyArg_ParseTuple()`](https://python-all.ru/2.6/c-api/arg.html#PyArg_ParseTuple)), но благодаря `pythonXY.lib` он знает, как найти код Python.130131Вторая команда создала `ni.dll` (а также `.obj` и `.lib`), который знает, как найти необходимые функции из spam, а также из исполняемого файла Python.132133Не каждый идентификатор экспортируется в таблицу поиска. Если вы хотите, чтобы другие модули (включая Python) могли видеть ваши идентификаторы, необходимо указать `_declspec(dllexport)`, например `void _declspec(dllexport) initspam(void)` или `PyObject _declspec(dllexport) *NiGetSpamData(void)`.134135Developer Studio добавит много ненужных библиотек импорта, увеличив размер исполняемого файла примерно на 100 КБ. Чтобы избавиться от них, в диалоге Project Settings на вкладке Link укажите *ignore default libraries*. Добавьте правильную `msvcrtxx.lib` в список библиотек.136