releaseHandleFailed – pomocník spravovaného ladění (MDA)

Aktivovaný releaseHandleFailed pomocník spravovaného ladění (MDA) má upozornit vývojáře, když ReleaseHandle metoda třídy odvozená z SafeHandle nebo CriticalHandle vrátí false.

Příznaky

Nevracení prostředků nebo paměti ReleaseHandle Pokud metoda třídy odvozená z SafeHandle nebo CriticalHandle selže, je možné, že prostředek zapouzdřený třídou nebyl uvolněn nebo vyčištěn.

Příčina

Uživatelé musí poskytnout implementaci ReleaseHandle metody, pokud vytvářejí třídy, které jsou odvozeny z SafeHandle nebo CriticalHandle; proto jsou okolnosti specifické pro jednotlivé prostředky. Požadavky jsou však následující:

  • SafeHandle a CriticalHandle typy představují obálky kolem důležitých prostředků procesu. Nevracení paměti způsobí, že proces bude v průběhu času nepoužitelný.

  • Metoda ReleaseHandle nesmí selhat při provádění své funkce. Jakmile proces takový prostředek získá, je jediným způsobem, ReleaseHandle jak ho uvolnit. Selhání proto znamená únik prostředků.

  • Jakékoli selhání, ke kterému dojde během provádění ReleaseHandlenástroje , což brání vydání prostředku, je chybou v implementaci ReleaseHandle samotné metody. Je zodpovědností programátora zajistit splnění kontraktu, a to i v případě, že tento kód volá kód, který k plnění své funkce vytvořil někdo jiný.

Řešení

Kód, který používá konkrétní SafeHandle typ (nebo CriticalHandle), který vyvolal oznámení MDA, by měl být zkontrolován a hledat místa, kde se nezpracovaná hodnota popisovače extrahuje z objektu SafeHandle a zkopíruje se jinam. To je obvyklá příčina selhání v rámci SafeHandle implementace nebo CriticalHandle , protože využití nezpracované hodnoty popisovače už modul runtime nesleduje. Pokud je nezpracovaná kopie popisovače následně uzavřena, může dojít k selhání pozdějšího ReleaseHandle volání, protože je pokus o zavření na stejném popisovači, který je nyní neplatný.

K nesprávné duplikaci popisovače může dojít několika způsoby:

  • Vyhledejte volání DangerousGetHandle metody . Volání této metody by měla být velmi vzácná a všechny nalezené by měly být obklopeny voláními DangerousAddRef metod a DangerousRelease . Tyto druhé metody určují oblast kódu, ve které může být nezpracovaná hodnota popisovače bezpečně použita. Mimo tuto oblast, nebo pokud se počet odkazů nikdy nezvýší na prvním místě, může být hodnota popisovače kdykoli zrušena voláním Dispose nebo Close v jiném vlákně. Jakmile jsou všechna použití DangerousGetHandle vysledována, měli byste postupovat po cestě nezpracovaného popisovače, abyste zajistili, že se nepředá nějaké komponentě, která bude nakonec volat CloseHandle nebo jinou nativní metodu nízké úrovně, která popisovač uvolní.

  • Ujistěte se, že kód použitý k inicializaci SafeHandle s platnou hodnotou nezpracovaného popisovače vlastní popisovač. Pokud vytvoříte SafeHandle kolem popisovače, který váš kód nevlastní bez nastavení parametru ownsHandle na false v základním konstruktoru SafeHandle , pak se vlastník i skutečný vlastník popisovače mohou pokusit popisovač zavřít, což vede k chybě v ReleaseHandle případě, že SafeHandle prohraje závod.

  • SafeHandle Pokud je řazení mezi doménami aplikace, ověřte, že SafeHandle použité odvození bylo označeno jako serializovatelné. Ve výjimečných případech, kdy třída odvozená z SafeHandle byla serializovatelná, by měla implementovat ISerializable rozhraní nebo použít jednu z dalších technik pro řízení serializace a deserializace proces ručně. To je nutné, protože výchozí serializační akce je vytvořit bitové klon uzavřené nezpracované hodnoty popisovače, což vede k tomu, že dvě SafeHandle instance si myslí, že vlastní stejný popisovač. Oba se v určitém okamžiku pokusí volat ReleaseHandle na stejný popisovač. SafeHandle Sekunda, která to má udělat, se nezdaří. Správný postup při serializaci SafeHandle je volání DuplicateHandle funkce nebo podobné funkce pro typ nativního popisovače, aby se jedinečná kopie popisovače. Pokud typ popisovače tuto možnost nepodporuje, SafeHandle nelze typ zabalení nastavit jako serializovatelný.

  • Umístěním zarážky ladicího programu na nativní rutinu používanou k uvolnění popisovače, například CloseHandle funkce, může být možné sledovat, kde je popisovač zavřený předčasně, což vede k selháníReleaseHandle. To nemusí být možné v případě zátěžových scénářů nebo dokonce středně velkých funkčních testů kvůli vysokému provozu, se kterým se tyto rutiny často zabývají. Může pomoct instrumentovat kód, který volá nativní metodu vydané verze za účelem zachycení identity volajícího, případně úplného trasování zásobníku a hodnoty vydaného popisovače. Hodnotu popisovače je možné porovnat s hodnotou hlášenou tímto mda.

  • Všimněte si, že některé nativní typy popisovačů, například všechny popisovače Win32, které lze vydat prostřednictvím CloseHandle funkce, sdílejí stejný obor názvů popisovačů. Chybné uvolnění jednoho typu popisovače může způsobit problémy s jiným. Například náhodné zavření popisovače události Win32 dvakrát může vést k předčasnému zavření zdánlivě nesouvisejícího popisovače souboru. K tomu dochází, když se popisovač uvolní a hodnota popisovače se zpřístupní ke sledování jiného prostředku, potenciálně jiného typu. Pokud k tomu dojde a následuje chybné druhé vydání, popisovač nesouvisejícího vlákna může být zneplatněn.

Vliv na modul runtime

Tento mda nemá žádný vliv na CLR.

Výstup

Zpráva s oznámením, že nebo SafeHandleCriticalHandle se nepodařilo správně uvolnit popisovač. Příklad:

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

Konfigurace

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

Příklad

Následuje příklad kódu, který může aktivovat 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);  
}  

Viz také