Leitfaden zum Implementieren von In-Process Erweiterungen
In-Process-Erweiterungen werden in alle Prozesse geladen, die sie auslösen. Beispielsweise kann eine Shell-Namespaceerweiterung in einen beliebigen Prozess geladen werden, der entweder direkt oder indirekt auf den Shellnamespace zuzugreifen hat. Der Shellnamespace wird von vielen Shellvorgängen verwendet, z. B. der Anzeige eines allgemeinen Dateidialogfelds, dem Start eines Dokuments über die zugehörige Anwendung oder dem Abrufen des Symbols, das zum Darstellen einer Datei verwendet wird. Da In-Process-Erweiterungen in beliebige Prozesse geladen werden können, muss darauf achten, dass sie sich nicht negativ auf die Hostanwendung oder andere In-Process-Erweiterungen auswirken.
Eine Runtime mit besonderem Hinweis ist die Common Language Runtime (CLR), auch als verwalteter Code oder als .NET Framework. Microsoft empfiehlt, keine verwalteten In-Process-Erweiterungen in Windows Explorer oder Windows Internet Explorer zu schreiben, und betrachtet sie nicht als unterstütztes Szenario.
In diesem Thema werden Faktoren erläutert, die sie berücksichtigen sollten, wenn Sie bestimmen, ob eine andere Laufzeit als die CLR für die Verwendung durch In-Process-Erweiterungen geeignet ist. Beispiele für andere Laufzeiten sind Java, Visual Basic, JavaScript/ECMAScript, Runtime und die C/C++-Laufzeitbibliothek. Dieses Thema enthält auch einige Gründe dafür, dass verwalteter Code in In-Process-Erweiterungen nicht unterstützt wird.
Versionskonflikte
Ein Versionskonflikt kann durch eine Laufzeit auftreten, die das Laden mehrerer Laufzeitversionen innerhalb eines einzelnen Prozesses nicht unterstützt. Versionen der CLR vor Version 4.0 fallen in diese Kategorie. Wenn das Laden einer Laufzeitversion das Laden anderer Versionen derselben Laufzeit aus schließt, kann dies zu einem Konflikt führen, wenn die Hostanwendung oder eine andere In-Process-Erweiterung eine in Konflikt stehende Version verwendet. Im Fall eines Versionskonflikts mit einer anderen Prozesserweiterung kann der Konflikt schwierig zu reproduzieren sein, da der Fehler die richtigen in Konflikt stehenden Erweiterungen erfordert und der Fehlermodus von der Reihenfolge abhängt, in der die in Konflikt stehenden Erweiterungen geladen werden.
Stellen Sie sich eine In-Process-Erweiterung vor, die mit einer Version der CLR vor Version 4.0 geschrieben wurde. Für jede Anwendung auf dem Computer, auf dem ein Dialogfeld zum Öffnen einer Datei verwendet wird, kann möglicherweise der verwaltete Code des Dialogfelds und die damit gestützte CLR-Abhängigkeit in den Prozess der Anwendung geladen werden. Die Anwendung oder Erweiterung, die zuerst eine ClR-Version vor 4.0 in den Prozess der Anwendung laden soll, schränkt ein, welche Versionen der CLR anschließend von diesem Prozess verwendet werden können. Wenn eine verwaltete Anwendung mit einem Dialogfeld Öffnen auf einer in Konflikt stehenden Version der CLR basiert, kann die Erweiterung möglicherweise nicht ordnungsgemäß ausgeführt werden und zu Fehlern in der Anwendung führen. Umgekehrt schlägt der Vorgang fehl, wenn die Erweiterung als erstes in einen Prozess geladen wird und eine in Konflikt stehende Version von verwaltetem Code danach versucht, zu starten (möglicherweise lädt eine verwaltete Anwendung oder eine ausgeführte Anwendung die CLR bei Bedarf). Für den Benutzer scheint es so, als ob einige Features der Anwendung nach dem Zufallsprinzip nicht mehr funktionieren, oder die Anwendung stürzt scheinbar ab.
Beachten Sie, dass Versionen der CLR, die gleich oder höher als Version 4.0 sind, im Allgemeinen nicht anfällig für das Versionsproblem sind, da sie so konzipiert sind, dass sie nebeneinander und mit den meisten Versionen vor 4.0 der CLR koexistieren (mit Ausnahme von Version 1.0, die nicht mit anderen Versionen koexistieren kann). Es können jedoch andere Probleme als Versionskonflikte auftreten, wie im weiteren Verlauf dieses Themas erläutert.
Leistungsprobleme
Leistungsprobleme können bei Laufzeiten auftreten, die beim Laden in einen Prozess zu erheblichen Leistungsproblemen führen. Die Leistungsstrafe kann in Form von Speicherauslastung, CPU-Auslastung, verstrichener Zeit oder sogar adressiertem Speicherplatzverbrauch sein. CLR, JavaScript/ECMAScript und Java sind als Runtimes mit hohem Einfluss bekannt. Da In-Process-Erweiterungen in viele Prozesse geladen werden können und dies häufig in leistungsbezogenen Momenten erfolgen (z. B. beim Vorbereiten eines Menüs für die Anzeige des Benutzers), können sich Laufzeiten mit großen Auswirkungen negativ auf die allgemeine Reaktionsfähigkeit auswirken.
Eine Laufzeit mit hohen Auswirkungen, die erhebliche Ressourcen verbraucht, kann zu einem Fehler im Hostprozess oder einer anderen In-Process-Erweiterung führen. Beispielsweise kann eine Laufzeit mit hoher Auswirkung, die Hunderte von Megabyte Adressraum für den Heap verbraucht, dazu führen, dass die Hostanwendung kein großes Dataset laden kann. Da In-Process-Erweiterungen außerdem in mehrere Prozesse geladen werden können, kann sich ein hoher Ressourcenverbrauch in einer einzelnen Erweiterung schnell in einen hohen Ressourcenverbrauch im gesamten System multiplizieren.
Wenn eine Laufzeit geladen bleibt oder auf andere Weise weiterhin Ressourcen nutzt, auch wenn die Erweiterung, die diese Runtime verwendet, entladen wurde, ist diese Laufzeit nicht für die Verwendung in einer Erweiterung geeignet.
Spezifische Probleme .NET Framework
In den folgenden Abschnitten werden Beispiele für Probleme erläutert, die bei der Verwendung von verwaltetem Code für Erweiterungen gefunden wurden. Dabei handelt es sich nicht um eine vollständige Liste aller möglichen Probleme, die auftreten können. Die hier erläuterten Probleme sind sowohl Gründe dafür, dass verwalteter Code in Erweiterungen nicht unterstützt wird, als auch Punkte, die bei der Auswertung der Verwendung anderer Runtimes zu berücksichtigen sind.
Re-entrancy
Wenn die CLR einen Singlethread-Apartmentthread (STA) blockiert, z. B. aufgrund einer Monitor.Enter-, WaitHandle.WaitOne- oder einer strittigen Lock-Anweisung, geht die CLR in ihrer Standardkonfiguration in eine geschachtelte Meldungsschleife ein, während sie wartet. Vielen Erweiterungsmethoden ist die Verarbeitung von Nachrichten untersagt, und diese unvorhersehbare und unerwartete Reentranz kann zu anomalem Verhalten führen, das schwer zu reproduzieren und zu diagnostizieren ist.
Das Multithread-Apartment
Die CLR erstellt Runtime Callable Wrapper für Component Object Model (COM)-Objekte. Diese gleichen Runtime Callable Wrapper werden später durch den Finalizer der CLR zerstört, der Teil des Multithread-Apartment (MTA) ist. Das Verschieben des Proxys vom STA zum MTA erfordert marshallen, aber nicht alle schnittstellen, die von Erweiterungen verwendet werden, können gemarshallt werden.
Nicht deterministische Objektlebensdauer
Die CLR verfügt über schwächere Garantien für die Objektlebensdauer als nativen Code. Viele Erweiterungen haben Verweisanzahlanforderungen für Objekte und Schnittstellen, und das von der CLR eingesetzte Garbage Collection-Modell kann diese Anforderungen nicht erfüllen.
- Wenn ein CLR-Objekt einen Verweis auf ein COM-Objekt abruft, wird der com-Objektverweis, der vom Runtime Callable Wrapper gehalten wird, erst freigegeben, wenn der Runtime Callable Wrapper garbage-collected durchgeführt wird. Das nicht deterministische Releaseverhalten kann mit einigen Schnittstellenverträgen in Konflikt stehen. Beispielsweise erfordert die IPersistPropertyBag::Load-Methode, dass kein Verweis auf den Eigenschaftensammler vom -Objekt beibehalten wird, wenn die Load-Methode zurückgegeben wird.
- Wenn ein CLR-Objektverweis an systemeigenen Code zurückgegeben wird, gibt der Runtime Callable Wrapper seinen Verweis auf das CLR-Objekt auf, wenn der letzte Aufruf von Release durch den Runtime Callable Wrapper erfolgt, das zugrunde liegende CLR-Objekt jedoch erst abgeschlossen wird, nachdem es garbage collection erfolgt ist. Die nicht deterministische Finalisierung kann mit einigen Schnittstellenverträgen in Konflikt stehen. Beispiel: Miniaturansichtshandler müssen alle Ressourcen sofort wieder frei geben, wenn ihre Verweisanzahl auf 0 (null) fällt.
Zulässige Verwendungsmöglichkeiten von verwaltetem Code und anderen Laufzeiten
Es ist akzeptabel, verwalteten Code und andere Laufzeiten zu verwenden, um Out-of-Process-Erweiterungen zu implementieren. Beispiele für Out-of-Process-Shellerweiterungen sind:
- Vorschauhandler
- Befehlszeilenbasierte Aktionen, z. B. diejenigen, die unter den Unterschlüsseln des \ Shellverbbefehls \ registriert sind.
- COM-Objekte, die auf einem lokalen Server für Shellerweiterungspunkte implementiert sind, die eine Out-of-Process-Aktivierung ermöglichen.
Einige Erweiterungen können entweder als In-Process- oder Out-of-Process-Erweiterungen implementiert werden. Sie können diese Erweiterungen als Out-of-Process-Erweiterungen implementieren, wenn sie diese Anforderungen für In-Process-Erweiterungen nicht erfüllen. Die folgende Liste enthält Beispiele für Erweiterungen, die entweder als In-Process- oder Out-of-Process-Erweiterung implementiert werden können:
- IExecuteCommand, die einem DelegateExecute-Eintrag zugeordnet ist, der unter einem \ Shellverbbefehlsunterschlüssel registriert \ ist.
- IDropTarget, das der CLSID zugeordnet ist, die unter einem \ Shellverb \ DropTarget-Unterschlüssel registriert ist.
- IExplorerCommandState, der einem CommandStateHandler-Eintrag zugeordnet ist, der unter einem \ Shellverbunterschlüssel registriert ist.