Freigeben über


ReleaseHandleFailed-MDA

Hinweis

Dieser Artikel gilt für das .NET Framework. Sie gilt nicht für neuere Implementierungen von .NET, einschließlich .NET 6 und höherer Versionen.

Der releaseHandleFailed-MDA (Managed Debugging Assistant, Assistent für verwaltetes Debuggen) wird aktiviert, um einen Entwickler zu benachrichtigen, wenn die ReleaseHandle-Methode einer Klasse, die aus SafeHandle oder CriticalHandle abgeleitet wurde, false zurückgibt.

Symptome

Ressourcen- oder Arbeitsspeicherverluste. Wenn die ReleaseHandle-Methode der aus SafeHandle oder CriticalHandle abgeleiteten Klasse fehlschlägt, wurde die durch die Klasse gekapselte Ressource möglicherweise nicht freigegeben oder bereinigt.

Ursache

Benutzer müssen die Implementierung der ReleaseHandle-Methode bereitstellen, wenn sie Klassen erstellen, die aus SafeHandle oder CriticalHandle abgeleitet sind. Daher hängen die Umstände von der jeweiligen Ressource ab. Die Anforderungen sehen jedoch wie folgt aus:

  • SafeHandle- und CriticalHandle-Typen sind Wrapper für entscheidende Prozessressourcen. Ein Arbeitsspeicherverlust würde den Prozess im Lauf der Zeit unbrauchbar machen.

  • Die ReleaseHandle-Methode darf beim Ausführen ihrer Funktion nicht fehlschlagen. Sobald ein Prozess eine solche Ressource abgerufen hat, ist ReleaseHandle die einzige Möglichkeit, diese freizugeben. Ein Fehlschlagen bedeutet deshalb Ressourcenverluste.

  • Jeder Fehler, der während der Ausführung von ReleaseHandle auftritt und die Freigabe der Ressource verhindert, ist ein Fehler in der Implementierung der ReleaseHandle-Methode. Es liegt in der Verantwortung des Programmierers, die Erfüllung des Vertrags sicherzustellen, selbst wenn im Code anderer Code aufgerufen wird, für den eine andere Person die Autorisierung erteilt hat, dessen Funktion auszuführen.

Lösung

Überprüfen Sie den Code, in dem der spezielle SafeHandle-Typ (oder CriticalHandle-Typ) verwendet wird, der die MDA-Benachrichtigung ausgelöst hat. Suchen Sie nach Stellen, an denen der unformatierte Handlewert aus dem SafeHandle-Objekt extrahiert und an eine andere Stelle Ort kopiert wird. Hier liegt meist der Grund für Fehler in SafeHandle- oder CriticalHandle-Implementierungen, denn die Verwendung des unformatierten Handlewerts wird dann nicht mehr von der Laufzeitumgebung verfolgt. Wenn die Kopie des unformatierten Handles anschließend geschlossen wird, kann dies dazu führen, dass ein späterer ReleaseHandle-Aufruf fehlschlägt, weil versucht wird, dasselbe Handle zu schließen, das jetzt ungültig ist.

Es gibt eine Reihe von Möglichkeiten, die in denen eine fehlerhafte Handleduplizierung auftreten kann:

  • Suchen Sie nach Aufrufen der DangerousGetHandle-Methode. Diese Methode sollte äußerst selten aufgerufen werden, und jeder Aufruf muss durch Aufrufe der DangerousAddRef- und der DangerousRelease-Methode umschlossen sein. Diese beiden Methoden geben den Codebereich an, in dem der unformatierte Handlewert sicher verwendet werden kann. Außerhalb dieses Bereichs, oder wenn die Verweisanzahl nie von vornherein erhöht wird, kann der Handlewert jederzeit durch einen Aufruf von Dispose oder Close in einem anderen Thread ungültig gemacht werden. Nachdem Sie alle Vorkommen von DangerousGetHandle ermittelt haben, folgen Sie dem Weg des unformatierten Handles, um sich zu vergewissern, dass es nicht an eine Komponente übergeben wird, die später CloseHandle oder eine andere systemnahe Methode aufruft, in der das Handle freigegeben wird.

  • Vergewissern Sie sich, dass der Code, mit dem das SafeHandle-Objekt mit einem gültigen unformatierten Handlewert initialisiert wird, das Handle besitzt. Wenn Sie ein SafeHandle-Objekt um ein Handle herum erstellen, das sich nicht im Besitz des Codes befindet, und wenn Sie den ownsHandle-Parameter im Basiskonstruktor nicht auf false festlegen, können sowohl das SafeHandle-Objekt als auch der tatsächliche Handlebesitzer versuchen, das Handle zu schließen. Dies führt dann zu einem Fehler im ReleaseHandle-Objekt, wenn das SafeHandle-Objekt das Rennen verliert.

  • Wenn ein SafeHandle-Objekt zwischen Anwendungsdomänen gemarshallt wird, müssen Sie sicherstellen, dass die verwendete SafeHandle-Ableitung als serialisierbar gekennzeichnet wurde. In den seltenen Fällen, in denen eine aus 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 standardmäßigen Serialisierungsaktion ein bitweiser Klon des eingeschlossenen unformatierten Handlewerts erstellt wird, was dazu führt, dass für zwei SafeHandle-Instanzen angenommen wird, dass sie dasselbe Handle besitzen. Beide Instanzen werden irgendwann versuchen, ReleaseHandle für dasselbe Handle aufzurufen. Das zweite SafeHandle-Objekt, das dies versucht, schlägt dann fehl. Das richtige Vorgehen beim Serialisieren eines SafeHandle-Objekts besteht darin, für den systemeigenen Handletyp die DuplicateHandle-Funktion oder eine ähnliche Funktion aufzurufen, damit eine unabhängige gültige Handlekopie erstellt wird. Wenn Ihr Handletyp dies nicht unterstützt, kann der SafeHandle-Typ, der ihn umschließt, nicht serialisierbar gemacht werden.

  • Möglicherweise können Sie verfolgen, wo ein Handle vorzeitig geschlossen wird, wodurch sich ein Fehler ergibt, wenn schließlich die ReleaseHandle-Methode aufgerufen wird. Platzieren Sie dazu einen Debuggerhaltepunkt in dernativen Routine, in der das Handle freigegeben wird, also z. B. in der CloseHandle-Funktion. Dies ist möglicherweise für Belastungsszenarios oder sogar für mittelgroße Funktionstests wegen des umfangreichen Datenverkehrs nicht möglich, den solche Routinen oft verarbeiten müssen. Es kann nützlich sein, den Code zu instrumentieren, der die systemeigene Freigabemethode aufruft. So lässt sich die Identität des Aufrufers oder möglicherweise eine vollständige Stapelüberwachung sowie der Wert des Handles erfassen, das freigegeben wird. Der Handlewert kann mit dem von diesem MDA gemeldeten Wert verglichen werden.

  • Beachten Sie, dass für einige systemeigene Handletypen, z. B. alle Win32-Handles, die über die CloseHandle-Funktion freigegeben werden können, derselbe Handlenamespace verwendet wird. Eine fehlerhafte Freigabe eines Handletyps kann zu Problemen mit einem anderen Handletyp führen. Wird beispielsweise ein Win32-Ereignishandle versehentlich zweimal geschlossen, kann die Folge sein, dass ein davon anscheinend unabhängiges Dateihandle vorzeitig geschlossen wird. Dies geschieht, wenn das Handle freigegeben und der Handlewert verfügbar wird, um eine andere Ressource zu verfolgen, die möglicherweise einen anderen Typ hat. Falls dies passiert 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

Das Folgende ist ein Codebeispiel, in dem der releaseHandleFailed-MDA aktiviert werden 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);
}

Weitere Informationen