Wie werden Daten in der DLL für eine Anwendung oder andere DLLs freigegeben?
Aktualisiert: November 2007
Win32-DLLs werden im Adressbereich des aufrufenden Prozesses zugeordnet. Standardmäßig verfügt jeder Prozess, der eine DLL verwendet, über eigene Instanzen aller globalen und statischen DLL-Variablen. Falls die DLL Daten mit anderen von ihr erstellten Instanzen gemeinsam nutzen muss, die von anderen Anwendungen geladen wurden, können Sie einen der folgenden Ansätze verwenden:
Erstellen Sie mithilfe des data_seg-Pragmas benannte Datenabschnitte.
Verwenden Sie Speicherabbilddateien. Siehe Dokumentation "Managing Memory-Mapped Files in Win32" in der MSDN Library.
Im Folgenden ein Beispiel zur Verwendung des data_seg-Pragmas:
#pragma data_seg (".myseg")
int i = 0;
char a[32]n = "hello world";
#pragma data_seg()
data_seg kann zum Erstellen eines neuen benannten Abschnitts verwendet werden (in diesem Beispiel ".myseg"). Normalerweise wird das Datensegment ".shared" aufgerufen, um Klarheit zu schaffen. Anschließend müssen Sie die richtigen Freigabeattribute für diesen neuen benannten Datenabschnitt in der DEF-Datei angeben. Alternativ können Sie dazu auch die Linkeroption /SECTION:.MYSEC,RWS verwenden.
Vor der Verwendung eines freigegebenen Datensegments sind einige Einschränkungen zu berücksichtigen:
Alle Variablen in einem freigegebenen Datensegment müssen statisch initialisiert werden. Im vorangehenden Beispiel wird "i" mit 0 initialisiert; "a" (32 Zeichen) wird mit "hello world" initialisiert.
Alle freigegebenen Variablen werden im angegebenen Datensegment in die kompilierte DLL eingefügt. Sehr umfangreiche Arrays können zu äußerst großen DLLs führen. Dies gilt für alle initialisierten globalen Variablen.
Prozessspezifische Informationen sollten keinesfalls in einem freigegebenen Datensegment gespeichert werden. Die meisten Win32-Datenstrukturen oder -werte (z. B. HANDLEs) sind nur im Kontext eines einzelnen Prozesses wirklich gültig.
Jeder Prozess erhält einen eigenen Adressenbereich. Es ist äußerst wichtig, dass Zeiger nicht in einer Variablen gespeichert werden, die in einem freigegebenen Datensegment enthalten ist. Ein Zeiger kann in einer Anwendung völlig zulässig sein, in einer anderen jedoch nicht.
Es besteht die Möglichkeit, dass die DLL selbst an einer anderen Adresse in den virtuellen Adressenbereichen der einzelnen Prozesse geladen wird. Die Verwendung von Zeigern auf Funktionen in der DLL oder auf andere freigegebene Variablen ist nicht sicher.
Beachten Sie, dass die drei letzteren Punkte auf Speicherabbilddateien und auf freigegebene Datensegmente zutreffen.
Speicherabbilddateien bieten einen Vorteil gegenüber freigegebenen Datenabschnitten, da der Beginn der Speicherabbilddatei bekannt ist. Entwickler können ein zeigerähnliches Verhalten implementieren, indem sie "einen Offset vom Start des freigegebenen Speicherabschnitts verwenden", und zwar in allen Daten, die sich innerhalb des freigegebenen Speicherbereichs befinden. Um diesen Vorgang zu vereinfachen und zu beschleunigen, wird die Verwendung von __based-Zeigern empfohlen. Sie sollten jedoch bedenken, dass die Basis (bzw. der Start der Speicherabbilddatei) in jedem Prozess unterschiedlich sein kann, sodass sich die Variable, in der die Basis für __based-Zeiger gespeichert ist, selbst nicht im freigegebenen Speicher befinden kann.
Diese Einschränkungen haben einen entscheidenden Einfluss auf C++-Klassen.
Klassen mit virtuellen Funktionen enthalten immer Funktionszeiger. Klassen mit virtuellen Funktionen sollten weder in freigegebenen Datensegmenten noch in Speicherabbilddateien gespeichert werden. Dies ist besonders wichtig für MFC-Klassen oder solche, die von MFC erben.
Statische Datenmember werden als Äquivalent zu globalen Variablen implementiert. Dies bedeutet, dass jeder Prozess über eine eigene Kopie der statischen Datenmember der jeweiligen Klasse verfügen würde. Klassen mit statischen Datenmembern sollten nicht freigegeben werden.
Die Initialisierungsanforderung eines freigegebenen Datensegments ist im Falle von C++-Klassen besonders problematisch. Bei einer Zeichenfolge wie CTest Counter(0); in einem freigegebenen Datensegment wird das Counter-Objekt beim Laden der DLL in jedem Prozess initialisiert. Dies kann dazu führen, dass die Objektdaten jedes Mal auf 0 (null) festgelegt werden. Bei systeminternen Datentypen verhält es sich anders: Sie werden beim Erstellen der DLL vom Linker initialisiert.
Aufgrund dieser Einschränkungen rät Microsoft davon ab, C++-Objekte für mehrere Prozesse freizugeben. Wenn Sie C++ für die Freigabe von Daten zwischen Prozessen nutzen möchten, sollten Sie grundsätzlich eine Klasse schreiben, die intern eine Speicherabbilddatei für die Datenfreigabe verwendet. Die Klasseninstanzen selbst sollten jedoch nicht freigegeben werden. Auch wenn die Entwicklung einer solchen Klasse besonders Sorgfalt erfordert, ermöglicht sie Anwendungsentwicklern die vollständige Kontrolle der Nebeneffekte, die bei gemeinsamen Nutzung von Daten auftreten können.
Weitere Informationen zum Erstellen benannter Datenabschnitte finden Sie in den folgenden Knowledge Base-Artikeln unter https://support.microsoft.com/?in=de:
"How to Share Data Between Different Mappings of a DLL" (Q125677, nur auf Englisch verfügbar)
"Specifying Shared and Nonshared Data in a DLL" (Q100634, nur auf Englisch verfügbar)
"Sharing All Data in a DLL" (Q109619, nur auf Englisch verfügbar)
"Memory in Shared Code Sections Is Not Shared Across Terminal Server Sessions" (Q251045, nur auf Englisch verfügbar)