Udostępnij za pośrednictwem


releaseHandleFailed MDA

Uwaga

Ten artykuł jest specyficzny dla programu .NET Framework. Nie ma zastosowania do nowszych implementacji platformy .NET, w tym .NET 6 i nowszych wersji.

Zarządzany releaseHandleFailed asystent debugowania (MDA) jest aktywowany jest powiadamianie deweloperów, gdy ReleaseHandle metoda klasy pochodnej lub SafeHandleCriticalHandle zwraca wartość false.

Objawy

Przecieki zasobów lub pamięci. ReleaseHandle Jeśli metoda klasy pochodnej lub SafeHandleCriticalHandle kończy się niepowodzeniem, zasób hermetyzowany przez klasę może nie zostać zwolniony lub oczyszczony.

Przyczyna

Użytkownicy muszą podać implementację ReleaseHandle metody, jeśli tworzą klasy pochodzące z SafeHandle lub CriticalHandle; w związku z tym okoliczności są specyficzne dla pojedynczego zasobu. Jednak wymagania są następujące:

  • SafeHandle typy i CriticalHandle reprezentują otoki związane z istotnymi zasobami procesów. Wyciek pamięci sprawi, że proces będzie bezużyteczny w czasie.

  • Metoda ReleaseHandle nie może wykonać swojej funkcji. Gdy proces uzyska taki zasób, ReleaseHandle jest jedynym sposobem jego wydania. W związku z tym awaria oznacza przecieki zasobów.

  • Każda awaria, która występuje podczas wykonywania ReleaseHandlemetody , co utrudnia wydanie zasobu, jest usterką w implementacji ReleaseHandle samej metody. Obowiązkiem programisty jest zapewnienie realizacji kontraktu, nawet jeśli kod wywołuje kod utworzony przez inną osobę w celu wykonania jego funkcji.

Rozwiązanie

Kod, który używa określonego SafeHandle typu (lub CriticalHandle), który zgłosił powiadomienie MDA, należy przejrzeć, wyszukując miejsca, w których nieprzetworzona wartość uchwytu jest wyodrębniona z SafeHandle elementu i skopiowana gdzie indziej. Jest to zwykła przyczyna błędów w ramach SafeHandle implementacji lub CriticalHandle , ponieważ użycie nieprzetworzonej wartości dojścia nie jest już śledzone przez środowisko uruchomieniowe. Jeśli nieprzetworzona kopia dojścia zostanie później zamknięta, może to spowodować niepowodzenie późniejszego ReleaseHandle wywołania, ponieważ próba zamknięcia jest podejmowana na tym samym dojściu, co jest teraz nieprawidłowe.

Istnieje wiele sposobów, w których może wystąpić niepoprawna obsługa duplikowania:

  • Wyszukaj wywołania DangerousGetHandle metody . Wywołania tej metody powinny być niezwykle rzadkie, a wszystkie, które można znaleźć, powinny być otoczone wywołaniami DangerousAddRef metod i DangerousRelease . Te ostatnie metody określają region kodu, w którym można bezpiecznie używać nieprzetworzonej wartości dojścia. Poza tym regionem lub jeśli liczba odwołań nigdy nie jest zwiększana w pierwszej kolejności, wartość uchwytu może zostać unieważniona w dowolnym momencie przez wywołanie metody Dispose lub Close w innym wątku. Po wytropieniu wszystkich zastosowań DangerousGetHandle należy postępować zgodnie ze ścieżką nieprzetworzonego uchwytu, aby upewnić się, że nie zostanie przekazany do niektórych składników, które w końcu CloseHandle wywołają lub inną metodę natywną niskiego poziomu, która zwolni uchwyt.

  • Upewnij się, że kod używany do inicjowania SafeHandle elementu z prawidłową wartością dojścia pierwotnego jest właścicielem uchwytu. Jeśli utworzysz SafeHandle wokół uchwytu kod nie jest właścicielem bez ustawienia parametru ownsHandle w false konstruktorze podstawowym, zarówno SafeHandle właściciel uchwytu rzeczywistego, jak i rzeczywistego uchwytu może spróbować zamknąć uchwyt, co prowadzi do błędu w ReleaseHandle przypadku SafeHandle utraty wyścigu.

  • Gdy element SafeHandle jest marshalled między domenami aplikacji, upewnij SafeHandle się, że używane wyprowadzenie zostało oznaczone jako serializowalne. W rzadkich przypadkach, w których klasa pochodząca z SafeHandle klasy została zserializowana, powinna zaimplementować ISerializable interfejs lub użyć jednej z innych technik kontrolowania procesu serializacji i deserializacji ręcznie. Jest to wymagane, ponieważ domyślną akcją serializacji jest utworzenie bitowego klonu ujętej nieprzetworzonej wartości uchwytu, co powoduje, że dwa SafeHandle wystąpienia myślą, że są właścicielami tego samego uchwytu. Obaj spróbują wywołać ReleaseHandle ten sam uchwyt w pewnym momencie. SafeHandle Drugie wykonanie tej czynności zakończy się niepowodzeniem. Prawidłowym przebiegiem akcji podczas serializacji elementu SafeHandle jest wywołanie DuplicateHandle funkcji lub podobnej funkcji dla natywnego typu dojścia w celu utworzenia odrębnej kopii dojścia prawnego. Jeśli typ uchwytu nie obsługuje tego typu, SafeHandle nie można go serializować.

  • Może być możliwe śledzenie, gdzie uchwyt jest zamykany wcześnie, co prowadzi do błędu, gdy ReleaseHandle metoda zostanie ostatecznie wywołana, umieszczając punkt przerwania debugera w natywnej procedurze używanej do zwolnienia uchwytu CloseHandle , na przykład funkcji. Może to nie być możliwe w scenariuszach stresu, a nawet średnich testach funkcjonalnych ze względu na duży ruch, z jakim często zmagają się takie procedury. Może to pomóc instrumentować kod, który wywołuje natywną metodę wydania, aby przechwycić tożsamość obiektu wywołującego lub ewentualnie pełny ślad stosu oraz wartość zwalnianego uchwytu. Wartość dojścia można porównać z wartością zgłoszoną przez tę usługę MDA.

  • Należy pamiętać, że niektóre natywne typy obsługi, takie jak wszystkie dojścia Win32, które można zwolnić za pośrednictwem CloseHandle funkcji, współdzielą tę samą przestrzeń nazw dojścia. Błędne wydanie jednego typu uchwytu może powodować problemy z innym. Na przykład przypadkowe zamknięcie dojścia zdarzenia Win32 dwa razy może prowadzić do przedwczesnego zamknięcia niepowiązanego dojścia do pliku. Dzieje się tak, gdy dojście zostanie zwolnione, a wartość dojścia stanie się dostępna do śledzenia innego zasobu, potencjalnie innego typu. Jeśli tak się stanie i następuje błędne drugie wydanie, uchwyt niepowiązanego wątku może zostać unieważniony.

Wpływ na środowisko uruchomieniowe

Ta usługa MDA nie ma wpływu na CLR.

Wyjście

Komunikat wskazujący, że SafeHandle nie można poprawnie zwolnić uchwytu lub CriticalHandle . Na przykład:

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

Konfigurowanie

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

Przykład

Poniżej przedstawiono przykład kodu, który może aktywować usługę releaseHandleFailed MDA.

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

Zobacz też