releaseHandleFailed-MDA
Aktualisiert: November 2007
Der releaseHandleFailed-MDA (Managed Debugging Assistant, Assistent für verwaltetes Debuggen) wird aktiviert, um einen Entwickler zu benachrichtigen, wenn die ReleaseHandle-Methode einer von SafeHandle oder CriticalHandle abgeleiteten Klasse false zurückgibt.
Symptome
Ressourcen- oder Speicherverluste. Wenn die ReleaseHandle-Methode der von SafeHandle oder CriticalHandle abgeleiteten Klasse fehlschlägt, wurde die von der Klasse gekapselte Ressource möglicherweise nicht freigegeben oder bereinigt.
Ursache
Benutzer müssen die Implementierung der ReleaseHandle-Methode bereitstellen, wenn sie Klassen erstellen, die von SafeHandle oder CriticalHandle abgeleitet werden. Deshalb hängen die Umstände von der einzelnen Ressource ab. Die Anforderungen lauten jedoch wie folgt:
SafeHandle-Typen und CriticalHandle-Typen stellen Wrapper für entscheidende Prozessressourcen dar. Ein Speicherverlust würde den Prozess über Zeit unbrauchbar machen.
Die ReleaseHandle-Methode darf beim Ausführen ihrer Funktion nicht fehlschlagen. Wenn ein Prozess eine Ressource dieser Art abruft, ist ReleaseHandle die einzige Möglichkeit, diese freizugeben. Ein Fehlschlagen bedeutet deshalb einen Ressourcenverlust.
Jeder Fehler, der bei der Ausführung von ReleaseHandle auftritt und die Freigabe der Ressource verhindert, stellt einen Fehler in der Implementierung der ReleaseHandle-Methode selbst dar. Es liegt in der Verantwortung des Programmierers, die Erfüllung des Vertrags sicherzustellen, auch wenn im Code zum Ausführen der Funktion Code aufgerufen wird, der von einer anderen Person stammt.
Lösung
Untersuchen Sie den Code, der den spezifischen SafeHandle-Typ (oder den CriticalHandle-Typ) verwendet, der die MDA-Benachrichtigung ausgelöst hat. Suchen Sie dabei nach Stellen, an denen der Rohdaten-Handlewert aus dem SafeHandle extrahiert und an einen anderen Ort kopiert wird. Dies ist meist der Grund für Fehler in Implementierungen von SafeHandle oder CriticalHandle, weil die Verwendung des Rohdaten-Handlewerts dann nicht mehr von der Laufzeit verfolgt wird. Wenn die Kopie des Rohdatenhandles anschließend geschlossen wird, kann dies später zu einem fehlgeschlagenen Aufruf von ReleaseHandle führen, weil versucht wird, dasselbe, inzwischen ungültig gewordene Handle zu schließen.
Es gibt eine Anzahl von Möglichkeiten, ein Handle auf unzulässige Art zu duplizieren:
Suchen Sie nach Aufrufen der DangerousGetHandle-Methode. Diese Methode sollte äußerst selten aufgerufen werden, und jeder Aufruf muss durch Aufrufe der DangerousAddRef-Methode und der DangerousRelease-Methode umschlossen sein. Diese beiden Methoden geben den Codebereich an, in dem der Rohdaten-Handlewert sicher verwendet werden kann. Außerhalb dieses Bereichs, oder wenn der Verweiszähler erst gar nicht hochgezählt wurde, kann das Handle jederzeit durch einen Aufruf von Dispose oder Close in einem anderen Thread ungültig gemacht werden. Nachdem Sie alle Vorkommen von DangerousGetHandle untersucht haben, sollten Sie den Pfad überprüfen, dem das Rohdatenhandle folgt, um sicherzustellen, dass dieses nicht an eine Komponente übergeben wird, die später CloseHandle oder eine andere systemeigene Methode auf niedriger Ebene aufruft, mit der das Handle freigegeben wird.
Stellen Sie sicher, dass der Code, der SafeHandle mit einem gültigen Rohdatenhandle initialisiert, auch wirklich das Handle besitzt. Wenn Sie ein SafeHandle um ein Handle bilden, das nicht im Besitz des Codes ist, ohne den ownsHandle-Parameter im Basiskonstruktor auf false festzulegen, können sowohl das SafeHandle als auch der wirkliche Besitzer des Handles versuchen, das Handle zu schließen. Dies führt zu einem Fehler in ReleaseHandle, wenn das SafeHandle den Schließvorgang nicht zuerst ausführen kann.
Wenn ein SafeHandle zwischen Anwendungsdomänen gemarshallt wird, prüfen Sie ob die verwendete SafeHandle-Ableitung als serialisierbar gekennzeichnet wurde. In den seltenen Fällen, in denen eine von SafeHandle abgeleitete Klasse serialisierbar gemacht wurde, muss sie die ISerializable-Schnittstelle implementieren oder eines der anderen Verfahren zum manuellen Steuern der Serialisierung und Deserialisierung verwenden. Dies ist erforderlich, weil bei der Serialisierung standardmäßig ein bitweiser Klon des eingeschlossenen Rohdaten-Handlewerts erstellt wird. Dies führt zu zwei SafeHandle-Instanzen, die scheinbar beide dasselbe Handle besitzen. Beide Klassen versuchen früher oder später, ReleaseHandle für dasselbe Handle aufzurufen. Das zweite SafeHandle, das diesen Vorgang versucht, schlägt fehl. Das richtige Vorgehen beim Serialisieren eines SafeHandle umfasst einen Aufruf der DuplicateHandle-Funktion oder einer ähnlichen Funktion für den systemeigenen Handletyp, um eine unabhängige gültige Kopie zu erstellen. Wenn Ihr Handletyp dies nicht unterstützt, kann der SafeHandle-Typ, der als Wrapper fungiert, nicht serialisierbar gemacht werden.
Möglicherweise können Sie herausfinden, an welcher Stelle ein Handle vorzeitig geschlossen wird, was schließlich beim Aufruf der ReleaseHandle-Methode zu einem Fehler führt, indem Sie einen Debughaltepunkt in die systemeigene Routine einfügen, die zum Freigeben des Handles verwendet wird, z. B. in die CloseHandle-Funktion. U. U. ist dies in Belastungsszenarios oder sogar in mittelgroßen Funktionstests aufgrund des starken Datenverkehrs, den solche Routinen oft verarbeiten müssen, nicht möglich. Es kann nützlich sein, den Code zu instrumentieren, der die systemeigene Freigabemethode aufruft, um die Identität des Aufrufers oder möglicherweise eine vollständige Stapelüberwachung sowie den Wert des Handles aufzurufen, das freigegeben wird. Der Handlewert kann mit dem von diesem MDA gemeldeten Wert verglichen werden.
Beachten Sie, dass einige systemeigene Handletypen, z. B. alle Win32-Handles, die mit der CloseHandle-Funktion freigegeben werden, denselben Handlenamespace verwenden. Eine fehlerhafte Freigabe eines Handletyps kann zu Problemen mit einem anderen Handletyp führen. Wenn beispielsweise ein Win32-Ereignishandle versehentlich zweimal geschlossen wird, kann dies dazu führen, dass ein davon anscheinend unabhängiges Dateihandle vorzeitig geschlossen wird. Dies ist der Fall, wenn das Handle freigegeben wird und der Handlewert zum Verfolgen einer anderen Ressource, die möglicherweise einen anderen Typ aufweist, verfügbar wird. Falls dies geschieht und eine fehlerhafte zweite Freigabe erfolgt, wird möglicherweise das Handle eines unabhängigen Threads ungültig gemacht.
Auswirkungen auf die Laufzeit
Dieser MDA hat keine Auswirkungen auf die CLR.
Ausgabe
Eine Meldung, die angibt, dass ein SafeHandle oder ein CriticalHandle das Handle nicht ordnungsgemäß freigegeben hat. Beispiel:
"A SafeHandle or CriticalHandle of type 'MyBrokenSafeHandle'
failed to properly release the handle with value 0x0000BEEF. This
usually indicates that the handle was released incorrectly via
another means (such as extracting the handle using DangerousGetHandle
and closing it directly or building another SafeHandle around it."
Konfiguration
<mdaConfig>
<assistants>
<releaseHandleFailed/>
</assistants>
</mdaConfig>
Beispiel
Im Folgenden finden Sie ein Codebeispiel, das den releaseHandleFailed-MDA aktivieren kann.
bool ReleaseHandle()
{
// Calling the Win32 CloseHandle function to release the
// native handle wrapped by this SafeHandle. This method returns
// false on failure, but should only fail if the input is invalid
// (which should not happen here). The method specifically must not
// fail simply because of lack of resources or other transient
// failures beyond the user’s control. That would make it unacceptable
// to call CloseHandle as part of the implementation of this method.
return CloseHandle(handle);
}
Siehe auch
Konzepte
Diagnostizieren von Fehlern mit Assistenten für verwaltetes Debuggen
Übersicht über das Interop-Marshalling