Partilhar via


releaseHandleFailed MDA

Nota

Este artigo é específico do .NET Framework. Ele não se aplica a implementações mais recentes do .NET, incluindo o .NET 6 e versões posteriores.

O releaseHandleFailed assistente de depuração gerenciado (MDA) é ativado para notificar os desenvolvedores quando o ReleaseHandle método de uma classe deriva de SafeHandle ou CriticalHandle retorna false.

Sintomas

Fugas de recursos ou memória. Se o ReleaseHandle método da classe derivar de SafeHandle ou CriticalHandle falhar, o recurso encapsulado pela classe pode não ter sido liberado ou limpo.

Motivo

Os usuários devem fornecer a implementação do ReleaseHandle método se criarem classes que derivam de SafeHandle ou CriticalHandle, portanto, as circunstâncias são específicas para o recurso individual. No entanto, os requisitos são os seguintes:

  • SafeHandle e CriticalHandle os tipos representam invólucros em torno de recursos vitais do processo. Uma fuga de memória tornaria o processo inutilizável ao longo do tempo.

  • O ReleaseHandle método não deve deixar de desempenhar a sua função. Uma vez que o processo adquire tal recurso, ReleaseHandle é a única maneira de liberá-lo. Portanto, a falha implica vazamentos de recursos.

  • Qualquer falha que ocorra durante a execução do ReleaseHandle, impedindo a liberação do recurso, é um bug na implementação do ReleaseHandle próprio método. É da responsabilidade do programador garantir que o contrato é cumprido, mesmo que esse código chame código de autoria de outra pessoa para desempenhar a sua função.

Resolução

O código que usa o tipo específico SafeHandle (ou CriticalHandle) que gerou a notificação MDA deve ser revisado, procurando locais onde o SafeHandle valor do identificador bruto é extraído do e copiado em outro lugar. Essa é a causa usual de falhas dentro SafeHandle de implementações ou CriticalHandle implementações, porque o uso do valor de identificador bruto não é mais rastreado pelo tempo de execução. Se a cópia do identificador bruto for fechada posteriormente, isso pode fazer com que uma chamada posterior ReleaseHandle falhe porque o fechamento é tentado no mesmo identificador, o que agora é inválido.

Há várias maneiras pelas quais a duplicação de identificador incorreto pode ocorrer:

  • Procure chamadas para o DangerousGetHandle método. As chamadas para este método devem ser extremamente raras, e qualquer uma que você encontrar deve ser cercada por chamadas para os DangerousAddRef métodos e DangerousRelease . Esses últimos métodos especificam a região de código na qual o valor bruto do identificador pode ser usado com segurança. Fora dessa região, ou se a contagem de referência nunca for incrementada em primeiro lugar, o valor do identificador pode ser invalidado a qualquer momento por uma chamada para Dispose ou Close em outro thread. Uma vez que todos os usos de tenham sido rastreados, você deve seguir o caminho que o identificador bruto toma para garantir que ele não seja entregue a algum componente que eventualmente chamará CloseHandle ou outro método nativo de DangerousGetHandle baixo nível que liberará o identificador.

  • Certifique-se de que o código usado para inicializar o SafeHandle com um valor de identificador bruto válido seja proprietário do identificador. Se você formar um SafeHandle identificador em torno de um que seu código não possui sem definir o ownsHandle parâmetro para false no construtor base, então tanto o proprietário do SafeHandle identificador quanto o real podem tentar fechar o identificador, levando a um erro se ReleaseHandle o SafeHandle perder a corrida.

  • Quando um SafeHandle é organizado entre domínios de aplicativo, confirme se a derivação que está sendo usada foi marcada SafeHandle como serializável. Nos raros casos em que uma classe derivada foi tornada serializável, ela deve implementar a ISerializable interface ou usar uma das outras técnicas para controlar o processo de SafeHandle serialização e desserialização manualmente. Isso é necessário porque a ação de serialização padrão é criar um clone bit a bit do valor do identificador bruto incluído, resultando em duas SafeHandle instâncias pensando que possuem o mesmo identificador. Ambos tentarão chamar ReleaseHandle a mesma alça em algum momento. O segundo SafeHandle a fazê-lo falhará. O curso de ação correto ao serializar um SafeHandle é chamar a DuplicateHandle função ou uma função semelhante para seu tipo de identificador nativo para fazer uma cópia de identificador legal distinta. Se o tipo de identificador não suportar isso, o encapsulamento do SafeHandle tipo não poderá ser serializado.

  • Pode ser possível rastrear onde um identificador está sendo fechado cedo, levando a uma falha quando o ReleaseHandle método é finalmente chamado, colocando um ponto de interrupção do depurador na rotina nativa usada para liberar o identificador, por exemplo, a CloseHandle função. Isso pode não ser possível para cenários de estresse ou mesmo testes funcionais de médio porte devido ao tráfego intenso com o qual essas rotinas costumam lidar. Pode ajudar a instrumentar o código que chama o método de liberação nativo, a fim de capturar a identidade do chamador, ou possivelmente um rastreamento de pilha completa, e o valor do identificador que está sendo liberado. O valor do identificador pode ser comparado com o valor relatado por este MDA.

  • Observe que alguns tipos de identificador nativo, como todos os identificadores do Win32 que podem ser liberados por meio da CloseHandle função, compartilham o mesmo namespace de identificador. Uma liberação errônea de um tipo de alça pode causar problemas com outro. Por exemplo, fechar acidentalmente um identificador de evento Win32 duas vezes pode levar a um identificador de arquivo aparentemente não relacionado ser fechado prematuramente. Isso acontece quando o identificador é liberado e o valor do identificador fica disponível para uso para rastrear outro recurso, potencialmente de outro tipo. Se isso acontecer e for seguido por uma segunda versão incorreta, o identificador de um thread não relacionado pode ser invalidado.

Efeito no tempo de execução

Este MDA não tem efeito sobre o CLR.

Saída

Uma mensagem indicando que um SafeHandle ou um CriticalHandle não conseguiu liberar corretamente o identificador. Por exemplo:

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

Configuração

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

Exemplo

A seguir está um exemplo de código que pode ativar o 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);
}

Consulte também