releaseHandleFailed MDAreleaseHandleFailed MDA

Помощник по отладке управляемого кода (MDA) releaseHandleFailed активируется для уведомления разработчиков, когда метод ReleaseHandle класса, производного от SafeHandle или CriticalHandle, возвращает значение false.The releaseHandleFailed managed debugging assistant (MDA) is activated is to notify developers when the ReleaseHandle method of a class derived from SafeHandle or CriticalHandle returns false.

СимптомыSymptoms

Утечки памяти или ресурсовResource or memory leaks. Если метод ReleaseHandle класса, производного от SafeHandle или CriticalHandle завершается с ошибкой, то ресурс, инкапсулированный этим классом, может быть не освобожден или не очищен.If the ReleaseHandle method of the class deriving from SafeHandle or CriticalHandle fails, then the resource encapsulated by the class might not have been released or cleaned up.

ПричинаCause

Пользователи должны предоставить реализацию метода ReleaseHandle, если они создают классы, производные от SafeHandle или CriticalHandle; таким образом, эти обстоятельства характерны для отдельных ресурсов.Users must provide the implementation of the ReleaseHandle method if they create classes that derive from SafeHandle or CriticalHandle; thus, the circumstances are specific to the individual resource. Однако действуют следующие требования.However, the requirements are as follows:

  • Типы SafeHandle и CriticalHandle представляют оболочки важных ресурсов процессов.SafeHandle and CriticalHandle types represent wrappers around vital process resources. Утечка памяти может со временем сделать процесс непригодным для использования.A memory leak would make the process unusable over time.

  • Метод ReleaseHandle должен работать без сбоев для выполнения своих функций.The ReleaseHandle method must not fail to perform its function. Когда процесс запрашивает такой ресурс, метод ReleaseHandle является единственным способом для его освобождения.Once the process acquires such a resource, ReleaseHandle is the only way to release it. Таким образом, ошибка подразумевает утечку ресурсов.Therefore, failure implies resource leaks.

  • Все ошибки, возникающие при выполнении метода ReleaseHandle и препятствующие освобождению ресурса, представляют ошибку в реализации самого метода ReleaseHandle.Any failure that does occur during the execution of ReleaseHandle, impeding the release of the resource, is a bug in the implementation of the ReleaseHandle method itself. Программист должен убедиться, что контракт будет выполнен, даже если этот код вызывает код, созданный другим разработчиком для выполнения своих функций.It is the responsibility of the programmer to ensure that the contract is fulfilled, even if that code calls code authored by someone else to perform its function.

РешениеResolution

Код, использующий конкретный тип SafeHandle (или CriticalHandle), вызвавший уведомление MDA, следует пересмотреть, найти места, где необработанное значение дескриптора извлекается из SafeHandle и копируется в другом месте.The code that uses the specific SafeHandle (or CriticalHandle) type that raised the MDA notification should be reviewed, looking for places where the raw handle value is extracted from the SafeHandle and copied elsewhere. Это обычная причина сбоев в реализации SafeHandle или CriticalHandle, так как использование необработанного значения дескриптора далее не отслеживается средой выполнения.This is the usual cause of failures within SafeHandle or CriticalHandle implementations, because the usage of the raw handle value is then no longer tracked by the runtime. Если копия необработанного дескриптора впоследствии закрывается, это может привести к сбою последующего вызова метода ReleaseHandle, поскольку попытка закрытия выполняется в том же дескрипторе, который теперь является недопустимым.If the raw handle copy is subsequently closed, it can cause a later ReleaseHandle call to fail because the close is attempted on the same handle, which is now invalid.

Существует несколько путей, в которых может возникнуть дублирование неверного дескриптора.There are a number of ways in which incorrect handle duplication can occur:

  • Поиск вызовов метода DangerousGetHandle.Look for calls to the DangerousGetHandle method. Вызовы этого метода должны быть крайне редки, и любой такой найденный вызов должны быть окружен вызовами методов DangerousAddRef и DangerousRelease.Calls to this method should be exceedingly rare, and any that you find should be surrounded by calls to the DangerousAddRef and DangerousRelease methods. Эти последующие методы определяют участок кода, в котором можно безопасно использовать необработанное значение дескриптора.These latter methods specify the region of code in which the raw handle value may be safely used. Вне этой области или в случае если число ссылок вообще никогда не увеличивается, значение дескриптора может стать недействительным в любое время путем вызова метода Dispose или Close в другом потоке.Outside this region, or if the reference count is never incremented in the first place, the handle value can be invalidated at any time by a call to Dispose or Close on another thread. Если все случаи использования метода DangerousGetHandle отслеживаются, необходимо следовать пути, к которому прибегает необработанный дескриптор, чтобы гарантировать, что он не передан в компонент, который в конечном итоге вызывает CloseHandle или другой низкоуровневый собственный метод, который будет освобождать этот дескриптор.Once all uses of DangerousGetHandle have been tracked down, you should follow the path the raw handle takes to ensure it is not handed off to some component that will eventually call CloseHandle or another low-level native method that will release the handle.

  • Убедитесь, что код, который используется для инициализации SafeHandle с использованием допустимого необработанного значения дескриптора, владеет этим дескриптором.Ensure that the code that is used to initialize the SafeHandle with a valid raw handle value owns the handle. При создании SafeHandle вокруг дескриптора, который не принадлежит коду без установки параметра ownsHandle в значение false в конструкторе базового класса, и SafeHandle, и реальный владелец дескриптора могут пытаться закрыть этот дескриптор, что приводит к ошибке в методе ReleaseHandle, если SafeHandle теряет состояние гонки.If you form a SafeHandle around a handle your code does not own without setting the ownsHandle parameter to false in the base constructor, then both the SafeHandle and the real handle owner can try to close the handle, leading to an error in ReleaseHandle if the SafeHandle loses the race.

  • Когда SafeHandle маршалируется между доменами приложений, подтвердите, что используемый вывод SafeHandle был помечен как сериализуемый.When a SafeHandle is marshaled between application domains, confirm the SafeHandle derivation being used has been marked as serializable. В редких случаях, когда класс, производный от SafeHandle, был сделан сериализуемым, он должен реализовывать интерфейс ISerializable или использовать один из других методов для управления процессом сериализации и десериализации вручную.In the rare cases where a class derived from SafeHandle has been made serializable, it should implement the ISerializable interface or use one of the other techniques for controlling the serialization and deserialization process manually. Это необходимо, поскольку действие сериализации по умолчанию заключается в создании побитового клона включенного необработанного значения дескриптора, в результате чего два экземпляра SafeHandle думают, что они владеют одним и тем же дескриптором.This is required because the default serialization action is to create a bitwise clone of the enclosed raw handle value, resulting in two SafeHandle instances thinking they own the same handle. Оба будут пытаться вызвать метод ReleaseHandle в том же дескрипторе в определенный момент.Both will try to call ReleaseHandle on the same handle at some point. Второму SafeHandle это сделать не удастся.The second SafeHandle to do this will fail. Правильный способ действий при сериализации SafeHandle заключается в вызове функции DuplicateHandle или аналогичной функции для вашего собственного типа дескриптора, чтобы создать другую копию действительного дескриптора.The correct course of action when serializing a SafeHandle is to call the DuplicateHandle function or a similar function for your native handle type to make a distinct legal handle copy. Если ваш тип дескриптора не поддерживает такое действие, то обертывающий его тип SafeHandle нельзя сделать сериализуемым.If your handle type does not support this then the SafeHandle type wrapping it cannot be made serializable.

  • Можно отслеживать, где дескриптор закрывается раньше, что приводит к возникновению ошибки при последнем вызове метода ReleaseHandle, установив точку останова отладчика в исходной программе, используемой для освобождения дескриптора, например в функции CloseHandle.It may be possible to track where a handle is being closed early, leading to a failure when the ReleaseHandle method is finally called, by placing a debugger breakpoint on the native routine used to release the handle, for example the CloseHandle function. Это может оказаться невозможным в сценариях нагрузочных тестов или даже в функциональных тестах среднего размера из-за большого объема трафика, с которым часто сталкиваются подобные программы.This may not be possible for stress scenarios or even medium-sized functional tests due to the heavy traffic such routines often deal with. Может помочь инструментирование кода, который вызывает собственный метод освобождения, чтобы записывать удостоверение вызывающего объекта или возможно полную трассировку стека и значение дескриптора.It may help to instrument the code that calls the native release method, in order to capture the identity of the caller, or possibly a full stack trace, and the value of the handle being released. Значение дескриптора можно сравнивать со значением, предоставленным MDA.The handle value can be compared with the value reported by this MDA.

  • Обратите внимание, что некоторые типы собственных дескрипторов, например все дескрипторы Win32, которые могут быть освобождены с помощью функции CloseHandle, совместно используют одно пространство имен дескрипторов.Note that some native handle types, such as all the Win32 handles that can be released via the CloseHandle function, share the same handle namespace. Ошибочное освобождение одного типа дескриптора может вызвать проблемы с другим.An erroneous release of one handle type can cause problems with another. Например, случайное закрытие дескриптора события Win32 дважды может привести к вероятному преждевременному закрытию несвязанного дескриптора файла.For instance, accidentally closing a Win32 event handle twice might lead to an apparently unrelated file handle being closed prematurely. Это происходит, когда дескриптор освобождается и значение дескриптора становится доступным для использования в целях отслеживания другого ресурса, возможно другого типа.This happens when the handle is released and the handle value becomes available for use to track another resource, potentially of another type. Если это происходит и приводит к ошибочному повторному освобождению, дескриптор несвязанного потока может стать недействительным.If this happens and is followed by an erroneous second release, the handle of an unrelated thread might be invalidated.

Влияние на среду выполненияEffect on the Runtime

Этот помощник отладки управляемого кода не оказывает никакого влияния на среду CLR.This MDA has no effect on the CLR.

ВыводOutput

Сообщение, указывающее, что SafeHandle или CriticalHandle не удалось должным образом освободить дескриптор.A message indicating that a SafeHandle or a CriticalHandle failed to properly release the handle. Например:For example:

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

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

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

ПримерExample

Ниже приведен пример кода, который может активировать MDA releaseHandleFailed.The following is a code example that can activate the 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);  
}  

См. такжеSee also