5. Erstellen von C- und C++-Erweiterungen unter Windows

Dieses Kapitel erklärt kurz, wie man ein Windows-Erweiterungsmodul für Python mit Microsoft Visual C++ erstellt, und liefert anschließend detailliertere Hintergrundinformationen zur Funktionsweise. Das erklärende Material ist sowohl für Windows-Programmierer, die lernen, Python-Erweiterungen zu erstellen, als auch für Unix-Programmierer, die daran interessiert sind, Software zu produzieren, die sowohl unter Unix als auch unter Windows erfolgreich erstellt werden kann, nützlich.

Modulautoren werden ermutigt, den distutils-Ansatz zum Erstellen von Erweiterungsmodulen zu verwenden, anstatt den in diesem Abschnitt beschriebenen. Sie benötigen immer noch den C-Compiler, der zum Erstellen von Python verwendet wurde; typischerweise Microsoft Visual C++.

Hinweis

Dieses Kapitel erwähnt eine Reihe von Dateinamen, die eine kodierte Python-Versionsnummer enthalten. Diese Dateinamen werden mit der Versionsnummer als XY dargestellt; in der Praxis wird 'X' die Hauptversionsnummer und 'Y' die Nebenversionsnummer der Python-Version sein, mit der Sie arbeiten. Wenn Sie beispielsweise Python 2.2.1 verwenden, ist XY tatsächlich 22.

5.1. Ein Kochbuch-Ansatz

Unter Windows gibt es zwei Ansätze zum Erstellen von Erweiterungsmodulen, genau wie unter Unix: Verwenden Sie das setuptools-Paket zur Steuerung des Build-Prozesses oder machen Sie die Dinge manuell. Der setuptools-Ansatz funktioniert gut für die meisten Erweiterungen; Dokumentation zur Verwendung von setuptools zum Erstellen und Verpacken von Erweiterungsmodulen finden Sie unter Erstellen von C- und C++-Erweiterungen mit setuptools. Wenn Sie feststellen, dass Sie die Dinge wirklich manuell tun müssen, kann es lehrreich sein, die Projektdatei für das Standardbibliotheksmodul winsound zu studieren.

5.2. Unterschiede zwischen Unix und Windows

Unix und Windows verwenden völlig unterschiedliche Paradigmen für die Laufzeitladung von Code. Bevor Sie versuchen, ein Modul zu erstellen, das dynamisch geladen werden kann, sollten Sie sich bewusst sein, wie Ihr System funktioniert.

Unter Unix enthält eine Shared Object-Datei (.so) Code, der vom Programm verwendet wird, sowie die Namen von Funktionen und Daten, die sie im Programm zu finden erwartet. Wenn die Datei mit dem Programm verknüpft wird, werden alle Verweise auf diese Funktionen und Daten im Code der Datei so geändert, dass sie auf die tatsächlichen Speicherorte im Programm zeigen, wo die Funktionen und Daten im Speicher platziert sind. Dies ist im Grunde ein Verknüpfungsvorgang.

Unter Windows hat eine dynamisch verknüpfte Bibliothek-Datei (.dll) keine hängenden Verweise. Stattdessen erfolgt ein Zugriff auf Funktionen oder Daten über eine Nachschlagetabelle. Der DLL-Code muss also zur Laufzeit nicht korrigiert werden, um auf den Speicher des Programms zu verweisen. Stattdessen verwendet der Code bereits die Nachschlagetabelle der DLL, und die Nachschlagetabelle wird zur Laufzeit modifiziert, um auf die Funktionen und Daten zu verweisen.

Unter Unix gibt es nur einen Typ von Bibliotheksdatei (.a), die Code aus mehreren Objektdateien (.o) enthält. Während des Verknüpfungsschritts zur Erstellung einer Shared Object-Datei (.so) kann der Linker feststellen, dass er nicht weiß, wo ein Bezeichner definiert ist. Der Linker sucht ihn in den Objektdateien der Bibliotheken; wenn er ihn findet, wird der gesamte Code aus dieser Objektdatei einbezogen.

Unter Windows gibt es zwei Arten von Bibliotheken, eine statische Bibliothek und eine Importbibliothek (beide heißen .lib). Eine statische Bibliothek ist wie eine Unix-Datei .a; sie enthält Code, der bei Bedarf einbezogen wird. Eine Importbibliothek wird im Wesentlichen nur dazu verwendet, dem Linker zu versichern, dass ein bestimmter Bezeichner legal ist und beim Laden der DLL im Programm vorhanden sein wird. Der Linker verwendet also die Informationen aus der Importbibliothek, um die Nachschlagetabelle für die Verwendung von Bezeichnern zu erstellen, die nicht in der DLL enthalten sind. Wenn eine Anwendung oder eine DLL verknüpft wird, kann eine Importbibliothek generiert werden, die für alle zukünftigen DLLs verwendet werden muss, die von den Symbolen in der Anwendung oder DLL abhängen.

Angenommen, Sie erstellen zwei dynamisch ladbare Module, B und C, die einen weiteren Codeblock A gemeinsam nutzen sollen. Unter Unix würden Sie A.a **nicht** an den Linker für B.so und C.so übergeben; dies würde dazu führen, dass er zweimal einbezogen wird, so dass B und C jeweils ihre eigene Kopie hätten. Unter Windows wird beim Erstellen von A.dll auch A.lib erstellt. Sie übergeben A.lib an den Linker für B und C. A.lib enthält keinen Code; es enthält nur Informationen, die zur Laufzeit verwendet werden, um auf den Code von A zuzugreifen.

Unter Windows ähnelt die Verwendung einer Importbibliothek der Verwendung von import spam; sie gibt Ihnen Zugriff auf die Namen von Spam, erstellt aber keine separate Kopie. Unter Unix ist das Verknüpfen mit einer Bibliothek eher wie from spam import *; es erstellt eine separate Kopie.

Deaktiviert die implizite, auf #pragma basierende Verknüpfung mit der Python-Bibliothek, die innerhalb der CPython-Headerdateien durchgeführt wird.

Hinzugefügt in Version 3.14.

5.3. Verwendung von DLLs in der Praxis

Windows Python wird in Microsoft Visual C++ erstellt; die Verwendung anderer Compiler funktioniert möglicherweise oder auch nicht. Der Rest dieses Abschnitts ist MSVC++-spezifisch.

Beim Erstellen von DLLs unter Windows können Sie die CPython-Bibliothek auf zwei Arten verwenden

  1. Standardmäßig löst die Aufnahme von PC/pyconfig.h direkt oder über Python.h eine implizite, konfigurationsabhängige Verknüpfung mit der Bibliothek aus. Die Headerdatei wählt pythonXY_d.lib für Debug, pythonXY.lib für Release und pythonX.lib für Release mit der Limited API aus.

    Um zwei DLLs zu erstellen, spam und ni (die C-Funktionen verwendet, die in spam gefunden werden), könnten Sie diese Befehle verwenden

    cl /LD /I/python/include spam.c
    cl /LD /I/python/include ni.c spam.lib
    

    Der erste Befehl erzeugte drei Dateien: spam.obj, spam.dll und spam.lib. Spam.dll enthält keine Python-Funktionen (wie PyArg_ParseTuple()), aber sie weiß dank der implizit verknüpften pythonXY.lib, wie sie den Python-Code finden kann.

    Der zweite Befehl erzeugte ni.dll (und .obj und .lib), die weiß, wie sie die notwendigen Funktionen aus spam und auch aus der Python-Executable findet.

  2. Manuell, indem Sie das Makro Py_NO_LINK_LIB definieren, bevor Sie Python.h einbinden. Sie müssen pythonXY.lib an den Linker übergeben.

    Um zwei DLLs zu erstellen, spam und ni (die C-Funktionen verwendet, die in spam gefunden werden), könnten Sie diese Befehle verwenden

    cl /LD /DPy_NO_LINK_LIB /I/python/include spam.c ../libs/pythonXY.lib
    cl /LD /DPy_NO_LINK_LIB /I/python/include ni.c spam.lib ../libs/pythonXY.lib
    

    Der erste Befehl erzeugte drei Dateien: spam.obj, spam.dll und spam.lib. Spam.dll enthält keine Python-Funktionen (wie PyArg_ParseTuple()), aber sie weiß dank pythonXY.lib, wie sie den Python-Code finden kann.

    Der zweite Befehl erzeugte ni.dll (und .obj und .lib), die weiß, wie sie die notwendigen Funktionen aus spam und auch aus der Python-Executable findet.

Nicht jeder Bezeichner wird in die Nachschlagetabelle exportiert. Wenn Sie möchten, dass andere Module (einschließlich Python) Ihre Bezeichner sehen können, müssen Sie _declspec(dllexport) angeben, wie in void _declspec(dllexport) initspam(void) oder PyObject _declspec(dllexport) *NiGetSpamData(void).

Developer Studio fügt viele Importbibliotheken hinzu, die Sie nicht wirklich benötigen, und erhöht Ihre ausführbare Datei um etwa 100 KB. Um sie zu entfernen, verwenden Sie den Dialog Projekteinstellungen, Registerkarte Linker, um *Standardbibliotheken ignorieren* anzugeben. Fügen Sie die korrekte msvcrtxx.lib zur Liste der Bibliotheken hinzu.