Initialisierung gemischter Assemblys
Aktualisiert: November 2007
In Visual C++ .NET und Visual C++ 2003 mit der Compileroption /clr kompilierte DLLs konnten beim Laden nicht deterministische Deadlocks verursachen. Dieses Problem kann als Ladeprogrammsperre-Fehler oder Ladefehler einer gemischten DLL bezeichnet werden. In Visual C++ 2005 wurde nahezu jeglicher Nicht-Determinismus aus dem Ladeprozess bei gemischten DLLs entfernt. Es gibt jedoch weiterhin Szenarien, bei denen die Ladeprogrammsperre (deterministisch) auftreten kann. Weitere Informationen über dieses Problem finden Sie in der MSDN Library unter "Mixed DLL Loading Problem".
In Visual C++ 2005 gilt weiterhin die Einschränkung, dass Code innerhalb von DllMain nicht auf die CLR zugreifen darf. Dies bedeutet, dass DllMain keine direkten oder indirekten Aufrufe an verwaltete Funktionen übergeben sollte. Es sollte auch kein verwalteter Code in DllMain deklariert oder implementiert werden, und innerhalb von DllMain sollte keine Garbage Collection und kein automatisches Laden einer Bibliothek stattfinden.
Hinweis: |
---|
In Visual C++ 2003 wurde _vcclrit.h zur Verfügung gestellt, um die DLL-Initialisierung zu erleichtern und die Möglichkeit eines Deadlocks zu minimieren. In Visual C++ 2005 muss _vcclrit.h nicht mehr verwendet werden. Außerdem führt das Einbinden dieser Datei bei der Kompilierung zu Warnmeldungen. Es wird empfohlen, die Abhängigkeiten von dieser Datei mithilfe der unter Gewusst wie: Entfernen der Abhängigkeit von _vcclrit.h beschriebenen Schritte zu entfernen. Eine weniger optimale Lösung besteht darin, die Warnungen durch die Definition von _CRT_VCCLRIT_NO_DEPRECATE vor dem Einbinden von including _vcclrit.h zu unterdrücken oder die Warnmeldungen einfach zu ignorieren. |
Ursachen für die Ladeprogrammsperre
Seit der Einführung der .NET-Plattform gibt es zwei verschiedene Mechanismen zum Laden von Ausführungsmodulen (EXE oder DLL): einen für Windows, der für nicht verwaltete Module verwendet wird, und einen für die .NET Common Language Runtime (CLR), mit der .NET-Assemblys geladen werden. Das Problem beim Laden gemischter DLLs steht mit dem Microsoft Windows OS Loader in Verbindung.
Wenn eine Assembly, die nur .NET-Konstrukte enthält, in einen Prozess geladen wird, kann das CLR-Ladeprogramm alle erforderlichen Lade- und Initialisierungsaufgaben selbst übernehmen. Bei gemischten Assemblys muss jedoch zusätzlich das Windows-Ladeprogramm verwendet werden, da diese systemeigenen Code und Daten enthalten können.
Das Windows-Ladeprogramm stellt sicher, dass anderer Code nicht auf Code oder Daten in der DLL zugreifen kann, bevor sie initialisiert wurde, und dass kein Code die DLL während ihrer teilweisen Initialisierung erneut laden kann. Für diesen Vorgang verwendet das Windows-Ladeprogramm einen prozessübergreifenden kritischen Abschnitt (auch "Ladeprogrammsperre" genannt), der den unsicheren Zugriff während der Modulinitialisierung verhindert. Daher ist der Ladevorgang anfällig für viele klassische Deadlock-Szenarien. In den folgenden zwei Szenarien ist das Risiko eines Deadlocks bei gemischten Assemblys erhöht:
Wenn Benutzer Funktionen ausführen, die für MSIL (Microsoft Intermediate Language) kompiliert wurden, und die Ladeprogrammsperre aktiv ist (z. B. aus DllMain oder in statischen Initialisierern), kann ein Deadlock auftreten. Denken Sie an den Fall, dass eine MSIL-Funktion auf einen Typ in einer Assembly verweist, der nicht geladen wurde. Die CLR versucht dann, die Assembly automatisch zu laden, wodurch das Windows-Ladeprogramm an der Ladeprogrammsperre blockieren kann. Da die Ladeprogrammsperre vom Code bereits früher in der Aufrufsequenz aktiviert wurde, tritt ein Deadlock auf. Wenn jedoch MSIL mit einer Ladeprogrammsperre ausgeführt wird, ist nicht sicher, dass ein Deadlock auftritt. Dadurch sind Fehler in diesem Szenario schwer zu diagnostizieren und zu beheben. In einigen Fällen, wenn z. B. die DLL des verweisenden Typs und deren Abhängigkeiten keine systemeigenen Konstrukte enthalten, muss das Windows-Ladeprogramm nicht die .NET-Assembly des verweisenden Typs laden. Außerdem könnte die erforderliche Assembly oder deren gemischte systemeigene/.NET-Abhängigkeiten bereits von anderem Code geladen worden sein. Daher kann es schwierig sein, ein Deadlock vorherzusagen, und zudem kann dies von der Konfiguration des Zielcomputers abhängig sein.
Wenn DLLs in den Versionen 1.0 und 1.1 von .NET Framework geladen werden, geht die CLR davon aus, dass keine Ladeprogrammsperre vorhanden ist, und führt verschiedene Aktionen aus, die bei einer Ladeprogrammsperre ungültig sind. Die Annahme, dass die Ladeprogrammsperre nicht vorhanden ist, ist bei reinen .NET-DLLs gültig, da jedoch gemischte DLLs systemeigene Initialisierungsroutinen ausführen, ist das systemeigene Windows-Ladeprogramm und daher die Ladeprogrammsperre erforderlich. Auch wenn der Entwickler versucht, keine MSIL-Funktionen bei der DLL-Initialisierung auszuführen, besteht daher dennoch die Möglichkeit eines nicht deterministischen Deadlocks mit den Versionen 1.0 und 1.1 von .NET Framework.
In Visual C++ 2005 wurde jeglicher Nicht-Determinismus aus dem Ladeprozess bei gemischten DLLs entfernt. Dies wurde mit folgenden Änderungen erreicht:
Die CLR geht beim Laden von gemischten DLLs nicht mehr von falschen Annahmen aus.
Die nicht verwaltete und die verwaltete Initialisierung werden in zwei getrennten und verschiedenen Phasen ausgeführt. Die nicht verwaltete Initialisierung wird zuerst (über DllMain) ausgeführt, und die verwaltete Initialisierung wird danach durch ein .NET-unterstütztes Konstrukt durchgeführt, das .cctor genannt wird. Die verwaltete Initialisierung ist für den Benutzer vollständig transparent, sofern nicht /Zl oder /NODEFAULTLIB verwendet wird. Weitere Informationen finden Sie unter /NODEFAULTLIB (Bibliotheken ignorieren) und /Zl (Kein Standardbibliotheksname).
Die Ladeprogrammsperre kann immer noch auftreten, ist jetzt allerdings reproduzierbar und wird erkannt. Wenn DllMain MSIL-Anweisungen enthält, generiert der Compiler die Warnung Compilerwarnung (Stufe 1) C4747. Außerdem erkennen und melden die CRT oder die CLR Versuche, MSIL bei einer Programmladesperre auszuführen. Die CRT-Erkennung führt zu einem diagnostischen Laufzeitfehler: C-Laufzeitfehler R6033.
Im Rest des Dokuments werden die übrigen Szenarien beschrieben, in denen MSIL bei einer Ladeprogrammsperre ausgeführt werden könnte, und es werden Lösungen für das Problem in den einzelnen Szenarien und Debugging-Verfahren angeboten.
Szenarien und Problemumgehungen
Es gibt eine Reihe von verschiedenen Szenarien, in denen der Benutzercode MSIL trotz Ladeprogrammsperre ausführen kann. Der Entwickler muss für jedes dieser Szenarien sicherstellen, dass bei der Benutzercodeimplementierung nicht versucht wird, MSIL-Anweisungen auszuführen. In den folgenden Abschnitten werden alle Möglichkeiten beschrieben, und es wird erläutert, wie die Probleme in den meisten Fällen behoben werden können.
DllMain
Statische Initialisierer
Vom Benutzer festgelegte Funktionen für das Starten
Benutzerdefinierte Gebietsschemas
DllMain
Die DllMain-Funktion ist ein benutzerdefinierter Einstiegspunkt für eine DLL. Wenn vom Benutzer nicht anders angegeben, wird DllMain jedes Mal aufgerufen, wenn ein Prozess oder Thread aus der enthaltenen DLL verbunden oder getrennt wird. Da dieser Aufruf bei vorhandener Ladeprogrammsperre auftreten kann, sollte keine vom Benutzer festgelegte DllMain-Funktion in MSIL kompiliert werden. Weiterhin kann keine Funktion in der Aufrufstruktur, die sich in DllMain befindet, in MSIL kompiliert werden. Um diese Probleme zu beheben, sollten Sie den Codeblock, der DllMain definiert, mit #pragma unmanaged ändern. Wiederholen Sie diese Vorgehensweise für jede von DllMain aufgerufene Funktion.
Wenn diese Funktionen eine Funktion aufrufen müssen, die eine MSIL-Implementierung für andere Aufrufkontexte benötigt, kann eine Vervielfältigungsstrategie verwendet werden, bei der sowohl eine .NET-Version als auch eine systemeigene Version derselben Funktion erstellt werden.
Wenn DllMain nicht benötigt wird oder bei einer Ladeprogrammsperre nicht ausgeführt werden muss, kann auch die vom Benutzer bereitgestellte DllMain-Implementierung entfernt und das Problem auf diese Weise behoben werden.
Wenn die DllMain versucht, MSIL direkt auszuführen, führt dies zu einer Compilerwarnung (Stufe 1) C4747. Durch den Compiler können jedoch keine Fälle festgestellt werden, bei denen durch DllMain eine Funktion in einem anderen Modul aufgerufen wird, welches wiederum versucht, MSIL auszuführen.
Weitere Informationen über dieses Szenario finden Sie unter "Schwierigkeiten bei der Fehlerdiagnose".
Initialisieren von statischen Objekten
Das Initialisieren statischer Objekte kann zu einem Deadlock führen, wenn ein dynamischer Initialisierer erforderlich ist. In einfachen Fällen, wenn z. B. lediglich ein zur Kompilierungszeit bekannter Wert einer statischen Variablen zugewiesen wird, ist keine dynamische Initialisierung erforderlich, und die Gefahr eines Deadlocks besteht nicht. Für durch Funktionsaufrufe initialisierte statische Variablen, Konstruktoraufrufe oder zur Kompilierungszeit nicht auswertbare Ausdrücke ist jedoch zum Ausführen während der Modulinitialisierung Code erforderlich.
Der folgende Code enthält Beispiele statischer Initialisierer, die einen dynamischen Initialisierer erfordern: ein Funktionsaufruf, eine Objektkonstruktion und eine Zeigerinitialisierung. (Diese Beispiele sind nicht statisch, sondern werden als global definiert angenommen, was die gleichen Auswirkungen hat.)
// dynamic initializer function generated
int a = init();
CObject o(arg1, arg2);
CObject* op = new CObject(arg1, arg2);
Das Risiko eines Deadlocks hängt davon ab, ob das übergeordnete Modul mit /clr kompiliert wurde und ob MSIL ausgeführt wird. Insbesondere kann ein Deadlock auftreten, wenn die statische Variable ohne /clr kompiliert wird (oder sich in einem #pragma unmanaged-Block befindet) und wenn der dynamische Initialisierer der Variablen zur Ausführung von MSIL-Anweisungen führt. Der Grund hierfür ist, dass bei ohne /clr kompilierten Modulen die Initialisierung statischer Variablen von DllMain durchgeführt wird. Im Gegensatz dazu werden mit /clr kompilierte statische Variablen durch .cctor initialisiert, nachdem die nicht verwaltete Initialisierungsphase abgeschlossen und die Ladeprogrammsperre aufgehoben wurde.
Es gibt eine Reihe von Möglichkeiten, einen durch dynamische Initialisierung von statischen Variablen hervorgerufenen Deadlock zu verhindern (in ungefährer Reihenfolge der zur Problembehebung benötigten Zeit):
Die Quelldatei, die die statische Variable enthält, kann mit /clr kompiliert werden.
Alle Funktionen, die von der statischen Variablen aufgerufen werden, können mit der #pragma unmanaged-Direktive in systemeigenen Code kompiliert werden.
Duplizieren Sie manuell den Code, von dem die statische Variable abhängig ist, und erstellen Sie so eine .NET-Version und eine systemeigene Version mit unterschiedlichen Namen. Entwickler können dann die systemeigene Version von systemeigenen statischen Initialisierern und die .NET-Version an anderer Stelle aufrufen.
Vom Benutzer festgelegte Funktionen für das Starten
Es gibt mehrere durch den Benutzer zur Verfügung gestellte Funktionen, von denen Bibliotheken während der Initialisierung beim Start abhängig sind. Wenn z. B. in C++ Operatoren wie new und delete global überladen werden, werden die vom Benutzer bereitgestellten Versionen an jeder Stelle verwendet, einschließlich der STL-Initialisierung und STL-Zerstörung. Dies führt dazu, dass STL und vom Benutzer bereitgestellte statische Initialisierer alle vom Benutzer bereitgestellten Versionen dieser Operatoren aufrufen.
Wenn die vom Benutzer bereitgestellten Versionen in MSIL kompiliert werden, versuchen diese Initialisierer während der Ladeprogrammsperre MSIL-Anweisungen auszuführen. Ein vom Benutzer bereitgestellter malloc hat die gleichen Folgen. Um dieses Problem zu beheben, müssen all diese Überladungen oder vom Benutzer bereitgestellten Definitionen mit der #pragma unmanaged-Direktive als systemeigener Code implementiert werden.
Weitere Informationen über dieses Szenario finden Sie unter "Schwierigkeiten bei der Fehlerdiagnose".
Benutzerdefinierte Gebietsschemas
Wenn ein benutzerdefiniertes globales Gebietsschema bereitgestellt wird, wird dieses Gebietsschema dann zur Initialisierung aller E/A-Streams verwendet, einschließlich der statisch initialisierten. Wenn dieses globale Gebietsschemaobjekt in MSIL kompiliert wird, können die in MSIL kompilierten Memberfunktionen des Gebietsschemaobjekts während der Ladeprogrammsperre ausgeführt werden.
Für die Problembehebung ergeben sich drei Möglichkeiten:
Die Quelldateien, die alle globalen E/A-Streamdefinitionen enthalten, können mit der /clr-Option kompiliert werden. Dadurch wird verhindert, dass ihre statischen Initialisierer während der Ladeprogrammsperre ausgeführt werden.
Die Funktionsdefinitionen des benutzerdefinierten Gebietsschemas können mit der #pragma unmanaged-Direktive in systemeigenen Code kompiliert werden.
Legen Sie das benutzerdefinierte Gebietsschema während der Ladeprogrammsperre nicht als globales Gebietsschema fest. Konfigurieren Sie dann explizit während der Initialisierung mit dem benutzerdefinierten Gebietsschema erstellte E/A-Streams.
Schwierigkeiten bei der Fehlerdiagnose
In manchen Fällen ist es nicht einfach, die Ursache für einen Deadlock zu ermitteln. In den folgenden Unterabschnitten werden diese Szenarien und Möglichkeiten der Problembehebung erläutert.
Implementierung in Headern
In Visual C++ .NET und Visual C++ .NET 2003 sowie bei bestimmten Szenarien in Visual C++ 2005 kann das Implementieren von Funktionen in Headerdateien die Fehlerdiagnose erschweren. Für Inlinefunktionen als auch Vorlagencode werden in einer Headerdatei angegebene Funktionen benötigt. In C++ gibt es die One Definition Rule, durch die Implementierungen von Funktionen mit demselben Namen als semantisch gleichwertig angesehen werden. Aus diesem Grund müssen mehrere Implementierungen einer gegebenen Funktion bei der Einbindung von Objektdateien durch den C++-Linker nicht berücksichtigt werden.
In Visual C++ .NET und Visual C++ .NET 2003 wählt der Linker einfach die größte dieser semantisch gleichwertigen Definitionen aus, um Deklarationen und Szenarien anzupassen, wenn für unterschiedliche Dateien verschiedene Optimierungsoptionen verwendet werden. Dadurch tritt für gemischte systemeigene DLLs oder .NET-DLLs ein Problem auf.
Da der gleiche Header sowohl von CPP-Dateien mit aktiviertem als auch deaktiviertem /clr eingefügt bzw. eine #include-Direktive in einen #pragma unmanaged-Block eingebunden werden kann, besteht die Möglichkeit, dass sowohl MSIL-Versionen als auch systemeigene Versionen von Funktionen vorhanden sind, die Implementierungen in Headern bereitstellen. MSIL-Implementierungen und systemeigene Implementierungen haben unterschiedliche Semantiken in Bezug auf die Initialisierung während der Ladeprogrammsperre, was zu einer effektiven Verletzung der One Definition Rule führt. Aus diesem Grund kann der Linker bei Auswahl der größten Implementierung auch die MSIL-Version einer Funktion auswählen. Dies ist selbst dann der Fall, wenn die Funktion an anderer Stelle explizit mit der #pragma unmanaged-Direktive in systemeigenen Code kompiliert wurde. Um sicherzustellen, dass eine MSIL-Version einer Vorlage oder Inlinefunktion niemals während der Ladeprogrammsperre aufgerufen wird, muss jede Definition jeder während der Ladeprogrammsperre aufgerufenen Funktion mit der #pragma unmanaged-Direktive geändert werden. Wenn die Headerdatei von einem Drittanbieter stammt, lässt sich dies am einfachsten erreichen, indem die #include-Direktive der problembehafteten Headerdatei durch die #pragma unmanaged-Direktive mit der PUSH-Anweisung und der POP-Anweisung bearbeitet wird. (Ein Beispiel finden Sie unter managed, unmanaged.) Diese Strategie ist jedoch wirkungslos für Header, die anderen Code enthalten, durch den .NET-APIs direkt aufgerufen werden.
In Visual C++ 2005 wählt der Linker bei einer Ladeprogrammsperre der Einfachheit halber die systemeigene anstelle der verwalteteten Implementierung, wenn beide verfügbar sind. Dadurch lassen sich die oben beschriebenen Probleme vermeiden. Aufgrund ungelöster Probleme mit dem Compiler gibt es jedoch in dieser Version zwei Ausnahmen zu dieser Regel:
- Der Aufruf einer Inlinefunktion erfolgt über einen globalen statischen Funktionszeiger. Dieses Szenario ist besonders bemerkenswert, da virtuelle Funktionen über globale Funktionszeiger aufgerufen werden. Beispiel:
#include "definesfoo.h"
#include "definesclassC.h"
typedef void (*function_pointer_t)();
function_pointer_t foo_p = &foo;
#pragma unmanaged
void DuringLoaderlock(C & c)
{
// Either of these calls could resolve to a managed implementation,
// at link-time, even if a native implementation also exists.
c.VirtualMember();
foo_p();
}
- Bei der Kompilierung für Itanium-Plattformen gibt es einen Fehler bei der Implementierung aller Funktionszeiger. Wäre im oben gezeigten Codeausschnitt foo_p lokal in during_loaderlock() definiert, würde der Aufruf möglicherweise auch in eine verwaltete Implementierung aufgelöst.
Fehlerdiagnose im Debugmodus
Eine Diagnose von mit der Ladeprogrammsperre zusammenhängenden Problemen sollte grundsätzlich mit Debugbuilds durchgeführt werden. Releasebuilds sind unter Umständen nicht in der Lage, Diagnosen zu erstellen, und die Optimierungen im Releasemodus können einen Teil der MSIL in Szenarien während der Ladeprogrammsperre verbergen.
Gewusst wie: Debuggen von Problemen, die mit der Ladeprogrammsperre zusammenhängen
Die von der CLR bei Aufruf einer MSIL-Funktion generierte Diagnose verursacht eine Unterbrechung des Aufrufs. Dies führt wiederum dazu, dass der Visual C++ 2005-Debugger im gemischten Modus ebenfalls unterbrochen wird, wenn der Debuggee während des Prozesses ausgeführt wird. Beim Anfügen an den Prozess ist es jedoch nicht möglich, mit dem gemischten Debugger eine verwaltete Aufrufliste für den Debuggee zu erhalten.
Entwickler sollten die folgenden Schritte ausführen, um eine bestimmte während der Ladeprogrammsperre aufgerufene MSIL-Funktion zu identifizieren:
Stellen Sie sicher, dass für mscoree.dll und mscorwks.dll Symbole zur Verfügung stehen.
Um dies zu erreichen, gibt es zwei Möglichkeiten. Zum einen können die PDBs für mscoree.dll und mscorwks.dll zum Symbolsuchpfad hinzugefügt werden. Öffnen Sie hierfür das Dialogfeld für Symbolsuchpfadoptionen. (Klicken Sie im Menü Extras auf Optionen. Öffnen Sie im linken Bereich des Dialogfelds Optionen den Knoten Debuggen, und klicken Sie auf Symbole.) Fügen Sie in die Suchliste den Pfad ein, in dem sich die PDB-Dateien für mscoree.dll und mscorwks.dll befinden. Diese PDB-Dateien werden in %VSINSTALLDIR%\SDK\v2.0\symbols installiert. Klicken Sie auf OK.
Eine andere Möglichkeit besteht darin, die PDBs für mscoree.dll und mscorwks.dll von Microsoft Symbol Server herunterzuladen. Öffnen Sie zum Konfigurieren von Symbol Server das Dialogfeld für Symbolsuchpfadoptionen. (Klicken Sie im Menü Extras auf Optionen. Öffnen Sie im linken Bereich des Dialogfelds Optionen den Knoten Debuggen, und klicken Sie auf Symbole.) Fügen Sie in die Suchliste den folgenden Suchpfad hinzu: http://msdl.microsoft.com/download/symbols. Fügen Sie im Textfeld Symbol Server Cache ein Symbolcacheverzeichnis hinzu. Klicken Sie auf OK.
Legen Sie den Debugmodus auf nur systemeigen fest.
Öffnen Sie hierzu in der Projektmappe das Eigenschaftenraster für das Startprojekt. Wählen Sie in der Unterstruktur Konfigurationseigenschaften den Knoten Debuggen aus. Legen Sie das Feld Debuggertyp auf Nur systemeigen fest.
Starten Sie den Debugger (F5).
Wenn die /clr-Diagnose generiert wurde, klicken Sie auf Wiederholen und dann auf Unterbrechen.
Öffnen Sie das Fenster Aufrufliste. (Klicken Sie im Menü Debuggen zunächst auf Fenster und dann auf Aufrufliste.) Wenn die problembehaftete DllMain oder der statische Initialisierer identifiziert wurde, wird diese(r) mit einem grünen Pfeil gekennzeichnet. Wenn die problembehaftete Funktion nicht identifiziert wurde, müssen die folgenden Schritte ausgeführt werden, um sie zu finden.
Öffnen Sie das Direktfenster (Klicken Sie im Menü Debuggen auf Fenster und dann auf Direkt.)
Geben Sie im Direktfenster .load sos.dll ein, um den Debugdienst SOS zu laden.
Geben Sie im Direktfenster !dumpstack ein, um eine vollständige Auflistung des internen /clr-Stapels zu erhalten.
Suchen Sie am untersten Ende des Stapels nach der ersten Instanz von _CorDllMain (wenn DllMain das Problem verursacht) oder _VTableBootstrapThunkInitHelperStub or GetTargetForVTableEntry (wenn der statische Initialisierer das Problem verursacht). Der auf diesen Aufruf folgende Stapeleintrag ist der Aufruf der MSIL-implementierten Funktion, deren Ausführung während der Ladeprogrammsperre versucht wurde.
Zeigen Sie die in Schritt 9 identifizierte Zeilennummer und Quelldatei an, und beheben Sie das Problem mit den im Abschnitt Szenarien beschriebenen Szenarien und Lösungsvorschlägen.
Beispiel
Beschreibung
Im folgenden Beispiel wird gezeigt, wie durch das Verschieben von Code aus DllMain in den Konstruktor eines globalen Objekts Ladeprogrammsperren vermieden werden können.
In diesem Beispiel gibt es ein verwaltetes globales Objekt, dessen Konstruktor das verwaltete Objekt enthält, das sich ursprünglich in DllMain befand. Der zweite Teil des Beispiels verweist auf die Assembly und erstellt eine Instanz des verwalteten Objekts, um den Modulkonstruktor aufzurufen, der die Initialisierung übernimmt.
Code
// initializing_mixed_assemblies.cpp
// compile with: /clr /LD
#pragma once
#include <stdio.h>
#include <windows.h>
struct __declspec(dllexport) A {
A() {
System::Console::WriteLine("Module ctor initializing based on global instance of class.\n");
}
void Test() {
printf_s("Test called so linker does not throw away unused object.\n");
}
};
#pragma unmanaged
// Global instance of object
A obj;
extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
// Remove all managed code from here and put it in constructor of A.
return true;
}
Beispiel
Code
// initializing_mixed_assemblies_2.cpp
// compile with: /clr initializing_mixed_assemblies.lib
#include <windows.h>
using namespace System;
#include <stdio.h>
#using "initializing_mixed_assemblies.dll"
struct __declspec(dllimport) A {
void Test();
};
int main() {
A obj;
obj.Test();
}
Ausgabe
Module ctor initializing based on global instance of class.
Test called so linker does not throw away unused object.