Delen via


releaseHandleFailed MDA

Notitie

Dit artikel is specifiek voor .NET Framework. Dit geldt niet voor nieuwere implementaties van .NET, waaronder .NET 6 en nieuwere versies.

De releaseHandleFailed beheerde foutopsporingsassistent (MDA) wordt geactiveerd om ontwikkelaars op de hoogte te stellen wanneer de ReleaseHandle methode van een klasse die is afgeleid van SafeHandle of CriticalHandle retourneert false.

Symptomen

Resource- of geheugenlekken. Als de methode van de ReleaseHandle klasse die is afgeleid van SafeHandle of CriticalHandle mislukt, is de resource die door de klasse is ingekapseld mogelijk niet vrijgegeven of opgeschoond.

Oorzaak

Gebruikers moeten de implementatie van de ReleaseHandle methode opgeven als ze klassen maken die zijn afgeleid van SafeHandle of CriticalHandle; daarom zijn de omstandigheden specifiek voor de afzonderlijke resource. De vereisten zijn echter als volgt:

  • SafeHandle en CriticalHandle typen vertegenwoordigen wrappers rond essentiële procesresources. Een geheugenlek maakt het proces onbruikbaar in de loop van de tijd.

  • De ReleaseHandle methode mag niet mislukken om de functie uit te voeren. Zodra het proces een dergelijke resource verkrijgt, ReleaseHandle is dit de enige manier om deze vrij te geven. Daarom impliceert een fout dat er resources worden gelekt.

  • Fouten die optreden tijdens de uitvoering van ReleaseHandle, waardoor de release van de resource wordt belemmerd, is een fout in de implementatie van de ReleaseHandle methode zelf. Het is de verantwoordelijkheid van de programmeur om ervoor te zorgen dat aan het contract wordt voldaan, zelfs als die code code aanroept die is geschreven door iemand anders om de functie uit te voeren.

Oplossing

De code die gebruikmaakt van het specifieke SafeHandle (of CriticalHandle) type dat de MDA-melding heeft gegenereerd, moet worden gecontroleerd, op zoek naar plaatsen waar de onbewerkte ingangswaarde wordt geëxtraheerd uit de SafeHandle en elders gekopieerde waarde. Dit is de gebruikelijke oorzaak van fouten binnen SafeHandle of CriticalHandle implementaties, omdat het gebruik van de onbewerkte ingangswaarde vervolgens niet meer wordt bijgehouden door de runtime. Als de kopie van de onbewerkte ingang vervolgens wordt gesloten, kan dit ertoe leiden dat een latere ReleaseHandle aanroep mislukt omdat de sluiting wordt geprobeerd op dezelfde ingang, die nu ongeldig is.

Er zijn een aantal manieren waarop onjuiste afhandeling van duplicatie kan optreden:

  • Zoek naar aanroepen naar de DangerousGetHandle methode. Aanroepen naar deze methode moeten zeer zeldzaam zijn en alle aanroepen die u vindt, moeten worden omgeven door aanroepen naar de DangerousAddRef en DangerousRelease methoden. Met deze laatste methoden wordt het codegebied opgegeven waarin de onbewerkte ingangswaarde veilig kan worden gebruikt. Buiten deze regio of als het aantal verwijzingen nooit in de eerste plaats wordt verhoogd, kan de handlewaarde op elk gewenst moment worden ongeldig gemaakt door een aanroep naar Dispose of Close op een andere thread. Zodra alle toepassingen van DangerousGetHandle zijn opgespoord, moet u het pad volgen dat de onbewerkte ingang neemt om ervoor te zorgen dat deze niet wordt overgedragen aan een bepaald onderdeel dat uiteindelijk wordt aangeroepen CloseHandle of een andere systeemeigen methode op laag niveau waarmee de ingang wordt vrijgegeven.

  • Zorg ervoor dat de code die wordt gebruikt om de SafeHandle met een geldige onbewerkte ingangswaarde te initialiseren, eigenaar is van de ingang. Als u een rond een SafeHandle ingang vormt die uw code niet bezit zonder de ownsHandle parameter false in te stellen in de basisconstructor, kunnen zowel de eigenaar van de SafeHandle echte ingang proberen de ingang te sluiten, waardoor er een fout optreedt ReleaseHandle als de SafeHandle race verloren gaat.

  • Wanneer een SafeHandle marshall wordt uitgevoerd tussen toepassingsdomeinen, controleert u of de SafeHandle gebruikte afleiding is gemarkeerd als serialiseerbaar. In zeldzame gevallen waarin een klasse die is afgeleid van SafeHandle serializeerbaar is gemaakt, moet deze de ISerializable interface implementeren of een van de andere technieken gebruiken om het serialisatie- en deserialisatieproces handmatig te beheren. Dit is vereist omdat de standaard serialisatieactie is om een bitwise kloon te maken van de ingesloten onbewerkte ingangswaarde, wat resulteert in twee SafeHandle exemplaren die denken dat ze eigenaar zijn van dezelfde ingang. Beide proberen op een bepaald moment dezelfde ingang aan te roepen ReleaseHandle . De tweede SafeHandle om dit te doen mislukt. Het juiste verloop van de actie bij het serialiseren van een SafeHandle is het aanroepen van de DuplicateHandle functie of een vergelijkbare functie voor uw systeemeigen handletype om een afzonderlijke juridische ingang te kopiëren. Als uw ingangstype dit niet ondersteunt, kan het SafeHandle typeterugloop niet serialiseerbaar worden gemaakt.

  • Het is mogelijk om bij te houden waar een ingang vroeg wordt gesloten, wat leidt tot een fout wanneer de ReleaseHandle methode eindelijk wordt aangeroepen, door een foutopsporingsprogramma-onderbrekingspunt te plaatsen op de systeemeigen routine die wordt gebruikt om de ingang vrij te geven, bijvoorbeeld de CloseHandle functie. Dit is mogelijk niet mogelijk voor stressscenario's of zelfs middelgrote functionele tests vanwege het zware verkeer waarmee dergelijke routines vaak te maken hebben. Het kan helpen om de code te instrumenteren die de systeemeigen releasemethode aanroept, om de identiteit van de aanroeper vast te leggen, of mogelijk een volledige stacktracering en de waarde van de greep die wordt vrijgegeven. De handlewaarde kan worden vergeleken met de waarde die door deze MDA is gerapporteerd.

  • Houd er rekening mee dat sommige systeemeigen handletypen, zoals alle Win32-ingangen die kunnen worden vrijgegeven via de CloseHandle functie, dezelfde handlenaamruimte delen. Een onjuiste release van het ene ingangstype kan problemen met een ander veroorzaken. Als u bijvoorbeeld per ongeluk een Win32-gebeurtenisgreep twee keer sluit, kan dit ertoe leiden dat een niet-gerelateerde bestandsingang voortijdig wordt gesloten. Dit gebeurt wanneer de ingang wordt vrijgegeven en de ingangswaarde beschikbaar wordt voor het bijhouden van een andere resource, mogelijk van een ander type. Als dit gebeurt en wordt gevolgd door een onjuiste tweede release, is de ingang van een niet-gerelateerde thread mogelijk ongeldig.

Effect op de runtime

Deze MDA heeft geen effect op de CLR.

Uitvoer

Een bericht dat aangeeft dat een SafeHandle of een CriticalHandle greep niet goed kan worden losgelaten. Voorbeeld:

"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."

Configuratie

<mdaConfig>
  <assistants>
    <releaseHandleFailed/>
  </assistants>
</mdaConfig>

Opmerking

Hier volgt een codevoorbeeld waarmee de releaseHandleFailed MDA kan worden geactiveerd.

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);
}

Zie ook