releaseHandleFailed MDA

Note

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 gerido (MDA) é ativado para notificar os programadores 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 derivado de SafeHandle ou CriticalHandle falhar, então o recurso encapsulado pela classe pode não ter sido libertado ou limpo.

Motivo

Os utilizadores devem fornecer a implementação do ReleaseHandle método se criarem classes que derivem de SafeHandle ou CriticalHandle; assim, 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 wrappers em torno de recursos vitais de processo. Uma fuga de memória tornaria o processo inutilizável com o tempo.

  • O ReleaseHandle método não deve falhar na sua função. Uma vez que o processo adquire tal recurso, ReleaseHandle é a única forma de o libertar. Portanto, falha implica fugas de recursos.

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

Resolução

O código que utiliza o tipo específico SafeHandle (ou CriticalHandle) que gerou a notificação MDA deve ser revisto, procurando locais onde o valor bruto do handle é extraído do SafeHandle e copiado para outro local. Esta é a causa habitual de falhas dentro SafeHandle de ou CriticalHandle implementações, porque o uso do valor bruto do handle deixa de ser rastreado pelo tempo de execução. Se a cópia bruta do handle for posteriormente fechada, pode causar a falha de uma chamada posterior ReleaseHandle porque o fecho é tentado no mesmo handle, o que agora é inválido.

Existem várias formas pelas quais pode ocorrer duplicação incorreta de alças:

  • Procure chamadas para o DangerousGetHandle método. As chamadas a este método devem ser extremamente raras, e qualquer uma que encontrar deve estar rodeada de chamadas para os DangerousAddRef métodos e.DangerousRelease Estes últimos métodos especificam a região de código em que o valor bruto do handle pode ser usado em segurança. Fora desta região, ou se a contagem de referências nunca for incrementada, o valor do handle pode ser invalidado a qualquer momento por uma chamada para Dispose ou Close noutro thread. Depois de todos os usos DangerousGetHandle de terem sido identificados, deves seguir o caminho que o handle bruto segue para garantir que não é passado para algum componente que eventualmente irá chamar CloseHandle ou para outro método nativo de baixo nível que liberte o handle.

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

  • Quando a SafeHandle é marshalled entre domínios de aplicação, confirme que a SafeHandle derivação utilizada foi marcada como serializável. Nos raros casos em que uma classe derivada de SafeHandle foi tornada serializável, deve implementar a ISerializable interface ou usar uma das outras técnicas para controlar manualmente o processo de serialização e desserialização. Isto é necessário porque a ação padrão de serialização é criar um clone bit a bit do valor bruto do handle incluído, resultando em duas SafeHandle instâncias a pensar que possuem o mesmo handle. Ambos vão tentar usar ReleaseHandle o mesmo nome em algum momento. O segundo SafeHandle que o fizer falhará. O caminho correto ao serializar um SafeHandle é chamar a DuplicateHandle função ou uma função semelhante para o seu tipo de handle nativo, para criar uma cópia de handle legal distinta. Se o seu tipo de handle não suportar isto, então o SafeHandle type wrapping não pode ser tornado serializável.

  • Pode ser possível rastrear onde um handle está a ser 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 libertar o handle, por exemplo, a CloseHandle função. Isto pode não ser possível em cenários de stress ou mesmo em testes funcionais de média dimensão devido ao tráfego intenso com que tais rotinas frequentemente lidam. Pode ajudar a instrumentar o código que chama o método nativo de liberação, para capturar a identidade do chamador, ou possivelmente um rastreio full stack, e o valor do handle a ser libertado. O valor do handle pode ser comparado com o valor reportado por esta MDA.

  • Note que alguns tipos nativos de handles, como todos os handles do Win32 que podem ser libertados através da CloseHandle função, partilham o mesmo namespace de handles. Uma libertação errada de um tipo de pega pode causar problemas com outro. Por exemplo, fechar acidentalmente um handle de evento Win32 duas vezes pode levar ao encerramento prematuro de um handle de ficheiro aparentemente não relacionado. Isto acontece quando o handle é libertado e o valor do handle fica disponível para seguir outro recurso, potencialmente de outro tipo. Se isto acontecer e for seguido por uma segunda versão errada, o handle de um thread não relacionado pode ser invalidado.

Efeito no tempo de execução

Este MDA não tem efeito no CLR.

Output

Uma mensagem a indicar que a SafeHandle ou a CriticalHandle falhou em libertar corretamente a alavanca. 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>

Example

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

Ver também