Поделиться через


releaseHandleFailed MDA

Обновлен: Ноябрь 2007

Управляемый помощник по отладке (MDA) releaseHandleFailed активируется, чтобы уведомить разработчиков, когда метод ReleaseHandle класса, производного от SafeHandle или CriticalHandle, возвращает значение false.

Признаки

Утечка памяти или ресурсов. Если в методе ReleaseHandle класса, производного от SafeHandle или CriticalHandle, появляется ошибка, это значит, что ресурс, инкапсулированный классом, возможно, не был высвобожден или очищен.

Причина

Пользователи должны предоставить реализацию метода ReleaseHandle при создании классов, производных от SafeHandle или CriticalHandle. Таким образом, ситуация зависит от конкретного ресурса. Однако требования формулируются следующим образом:

  • Типы SafeHandle и CriticalHandle представляют собой оболочки для обязательных ресурсов процесса. Утечка памяти со временем сделает процесс непригодным для использования.

  • Метод ReleaseHandle должен успешно выполнять свои функции. Как только у процесса появляется такой ресурс, ReleaseHandle является единственным способом его высвобождения. Таким образом, ошибка подразумевает утечку ресурсов.

  • Любая ошибка, которая возникает во время выполнения ReleaseHandle, мешая высвобождению ресурса, является ошибкой в реализации самого метода ReleaseHandle. Программист отвечает за исполнение контракта даже в том случае, если для выполнения функций кода требуется вызов кода, созданного кем-то другим.

Решение

Код, использующий особый тип SafeHandle (или CriticalHandle), вызвавший уведомление MDA, должен быть пересмотрен на предмет наличия участков, где исходное значение дескриптора извлекается из SafeHandle и копируется в другое место. Это не самая распространенная причина ошибок в реализациях SafeHandle или CriticalHandle, поскольку использование исходных значений дескрипторов в этом случае больше не отслеживается средой выполнения. Если копия исходного значения дескриптора впоследствии закрывается, это может вызвать ошибку дальнейшего вызова ReleaseHandle, поскольку выполняется попытка закрытия того же дескриптора, который в данный момент является недействительным.

Существует несколько ситуаций, в которых может возникнуть неверное дублирование дескриптора:

  • Следует проверить вызовы метода DangerousGetHandle. Вызовы данного метода должны выполняться как можно реже, и любые обнаруженные вызовы должны сопровождаться вызовами методов DangerousAddRef и DangerousRelease. Эти методы определяют участок кода, в котором можно безопасно использовать исходное значение дескриптора. Вне данного участка, или если не происходит приращения счетчика ссылок, значение дескриптора можно превратить в недействительное в любое время посредством вызова Dispose или Close в другом потоке. Когда отслежены все вызовы DangerousGetHandle, необходимо отследить путь исходного значения дескриптора, чтобы убедиться, что оно не передано какому-либо компоненту, который впоследствии вызовет метод CloseHandle или иной низкоуровневый исходный метод, высвобождающий дескриптор.

  • Необходимо убедиться в том, что код, используемый для инициализации SafeHandle с допустимым исходным значением дескриптора, владеет дескриптором. Если сформировать SafeHandle для дескриптора, которым не владеет код, не присваивая параметру ownsHandle значения false в основном конструкторе, тогда и SafeHandle, и реальный владелец дескриптора попытаются закрыть дескриптор, что приведет к ошибке в ReleaseHandle, если SafeHandle не успеет закрыть его первым.

  • Когда SafeHandle маршалируется между доменами приложения, следует подтвердить, что используемое наследование SafeHandle помечено как сериализуемое. В отдельных редких случаях, когда класс, производный от SafeHandle, делается сериализуемым, он должен реализовать интерфейс ISerializable или использовать иную технику управления вручную сериализацией и десериализацией. Это необходимо, поскольку сериализация по умолчанию должна создавать побитовый клон включенного исходного значения дескриптора, в результате чего два экземпляра SafeHandle считают, что владеют одним и тем же дескриптором. В какой-то момент оба экземпляра попытаются вызвать ReleaseHandle в одном и том же дескрипторе. Для экземпляра SafeHandle, который сделает это вторым, операция окажется неуспешной. При корректной сериализации SafeHandle функция DuplicateHandle или похожая функция исходного типа дескриптора вызывается для создания другой допустимой копии дескриптора. Если тип дескриптора не поддерживает данную функцию, тогда оболочка типа SafeHandle не может быть сериализуемой.

  • Можно отследить участок, где дескриптор закрывается раньше, что ведет к возникновению ошибки при конечном вызове метода ReleaseHandle, посредством размещения точки прерывания отладчика в исходной программе, используемой для высвобождения дескриптора; например, с помощью функции CloseHandle. Это может быть невозможным для нагрузочных сценариев или даже для средних функциональных тестов, вследствие большого объема трафика, с которым часто сталкиваются подобные программы. Можно попытаться инструментировать код, вызывающий исходный метод высвобождения, чтобы определить вызывающий объект или, возможно, выполнить полную трассировку стека, а также значение высвобождаемого дескриптора. Значение дескриптора можно сравнить со значением, о котором сообщает данный MDA.

  • Обратите внимание, что некоторые типы исходных дескрипторов, например, все дескрипторы Win32, которые можно высвободить посредством функции CloseHandle, используют общее пространство имен дескрипторов. Ошибочное высвобождение одного типа дескриптора может привести к возникновению проблем с другим. Например, если случайно дважды закрыть дескриптор события Win32, это может привести к преждевременному закрытию несвязанного дескриптора файла. Это происходит, когда дескриптор высвобождается, и значение дескриптора доступно для использования в целях отслеживания другого ресурса, потенциально относящегося к другому типу. В таком случае, если в дальнейшем происходит повторное ошибочное высвобождение, дескриптор несвязанного потока может стать недействительным.

Влияние на среду выполнения

Данный помощник по отладке управляемого кода не оказывает влияния на среду CLR.

Результат

Сообщение с информацией о том, что SafeHandle или CriticalHandle не удалось корректно высвободить дескриптор. Пример:

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

Конфигурация

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

Пример

Следующий пример кода может активировать 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);
}

См. также

Основные понятия

Диагностика ошибок посредством управляемых помощников по отладке

Общие сведения о маршалинге взаимодействия

Ссылки

MarshalAsAttribute

Другие ресурсы

Взаимодействие