Compartir a través de


MDA de releaseHandleFailed

El asistente para la depuración administrada (MDA) releaseHandleFailed se activa para notificar a los desarrolladores cuando el método ReleaseHandle de una clase derivada de SafeHandle o CriticalHandle devuelve false.

Síntomas

Pérdidas de recursos o memoria. Si falla el método ReleaseHandle de la clase que deriva de SafeHandle o de CriticalHandle, el recurso encapsulado por la clase podría no haberse liberado o limpiado.

Motivo

Los usuarios deben proporcionar la implementación del método ReleaseHandle si crean clases que derivan de SafeHandle o de CriticalHandle; por tanto, las circunstancias son específicas del recurso individual. Sin embargo, los requisitos son los siguientes:

  • Los tipos SafeHandle y CriticalHandle representan contenedores en torno a recursos vitales de proceso. Con el paso del tiempo, una pérdida de memoria dejaría inutilizable el proceso.

  • El método ReleaseHandle no debe realizar esta función con errores. Una vez que el proceso adquiere este tipo de recurso, ReleaseHandle es la única manera de liberarlo. Por consiguiente, el error implica pérdidas de recursos.

  • Cualquier error que se produzca durante la ejecución de ReleaseHandle, impidiendo la liberación del recurso, es un error en la implementación del propio método ReleaseHandle. Corresponde al programador la responsabilidad de asegurarse de que el contrato se cumple, incluso si ese código, para realizar su cometido, llama a otro código creado por otra persona.

Resolución

El código que utiliza el tipo específico SafeHandle (o CriticalHandle) que inició la notificación MDA debe revisarse para buscar lugares en que se extrae el valor del identificador sin formato de SafeHandle y se copia en otro lugar. Éste es el motivo usual de los errores producidos en las implementaciones de SafeHandle o de CriticalHandle, porque el motor en tiempo de ejecución ya no realiza el seguimiento de la utilización del valor de identificador sin formato. Si la copia del identificador sin formato se cierra más tarde, puede provocar errores en una posterior llamada a ReleaseHandle porque se intenta el cierre en el mismo identificador, que ya ha dejado de ser válido.

Hay varios maneras en las que se puede producir una duplicación incorrecta del identificador:

  • Busque llamadas al método DangerousGetHandle. Las llamadas a este método deberían ser sumamente raras y cualquiera de ellas que encuentre deben ir rodeada por llamadas a los métodos DangerousAddRef y DangerousRelease. Estos últimos métodos especifican el área de código en el que se puede utilizar sin ningún riesgo el valor de identificador sin formato. Fuera de esta área, o si el recuento de referencias nunca se incrementa en primer lugar, el valor del identificador se puede invalidar en cualquier momento por una llamada a Dispose o a Close en otro subproceso. Una vez realizado el seguimiento de todos los usos de DangerousGetHandle, siga la ruta que toma el identificador sin formato para asegurarse de que no se entrega a otro componente que finalmente llame a CloseHandle o a otro método nativo de bajo nivel que libere el identificador.

  • Asegúrese de que el código utilizado para inicializar SafeHandle con un valor de identificador sin formato válido es el propietario del identificador. Si forma un SafeHandle en torno a un controlador que no es propiedad de su código sin establecer el parámetro ownsHandle en false en el constructor base, tanto SafeHandle como el propietario del identificador real puede intentar cerrar el identificador, provocando un error en ReleaseHandle si SafeHandle no consigue anticiparse.

  • Cuando se calculan las referencias a SafeHandle entre los dominios de aplicación, confirme que la derivación de SafeHandle está marcada como serializable. En los raros casos en los que una clase derivada de SafeHandle se ha hecho serializable, debería implementar la interfaz ISerializable o utilizar una de las otras técnicas de control manual del proceso de serialización y deserialización. Esto es necesario porque la acción de serialización predeterminada es crear un clon bit a bit del valor del identificador sin formato incluido, provocando la existencia de dos instancias de SafeHandle que creen que poseen el mismo identificador. Ambos intentarán llamar a ReleaseHandle en el mismo identificador en un momento dado. Se producirá un error en el segundo SafeHandle que intente hacerlo. El curso de acción correcto al serializar un SafeHandle es llamar a la función DuplicateHandle o una función parecida para su tipo de identificador nativo de manera que se cree una copia diferenciada del identificador legal. Si su tipo de identificador no admite esta acción, el tipo de SafeHandle que lo contiene no se puede hacer serializable.

  • Puede ser posible realizar un seguimiento en el que se cierra por adelantado un identificador, lo que provoca un error cuando se llama finalmente al método ReleaseHandle, colocando un punto de interrupción del depurador en la rutina nativa usada para liberar el identificador, por ejemplo, la función CloseHandle. Esto no puede ser posible para los escenarios de tensión o incluso las comprobaciones funcionales de mediano tamaño debido a la intensidad del tráfico que deben afrontar frecuentemente tales rutinas. Puede resultar de ayuda instrumentalizar el código que llama al método de liberación nativo con el fin de captar la identidad del llamador, o posiblemente un seguimiento completo de la pila, y el valor del controlador que se está liberando. El valor de identificador se puede comparar con el valor indicado por este MDA.

  • Observe que algunos tipos de identificador nativo, como todos los identificadores de Win32 que se pueden liberar mediante la función CloseHandle, comparten el mismo espacio de nombres del identificador. Una liberación errónea de un tipo de identificador puede producir problemas con otro. Por ejemplo, cerrar por accidente dos veces un identificador de evento de Win32 podría conducir a que un identificador de archivos aparentemente no relacionado se cerrara prematuramente. Esto ocurre cuando se libera el identificador y el valor de identificador queda disponible para su uso para realizar el seguimiento de otro recurso, potencialmente de otro tipo. Si esto pasa y va seguido por una segunda liberación errónea, se podría invalidar el identificador de un subproceso no relacionado.

Efecto en el tiempo de ejecución

Este MDA no tiene ningún efecto en CLR.

Output

Un mensaje que indica que un SafeHandle o un CriticalHandle no liberó correctamente el identificador. Por ejemplo:

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

Configuración

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

Ejemplo

Lo siguiente es un ejemplo de código que puede activar el MDA releaseHandleFailed.

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

Vea también

Referencia

MarshalAsAttribute

Conceptos

Diagnóstico de errores con ayudantes de depuraciones administradas

Cálculo de referencias de interoperabilidad

Otros recursos

Interoperabilidad