IntPtr, SafeHandle и HandleRef - Объяснено

Не указывая мне на MSDN, кто-то может дать краткое, четкое объяснение цели каждого из них и когда их использовать. (IntPtr, SafeHandle и HandleRef)

3 ответа

Решение

IntPtr это простая целочисленная структура, которая может содержать указатель (т.е. 32-битный размер в 32-битных системах, 64-битный размер в 64-битных системах).

SafeHandle это класс, предназначенный для хранения дескрипторов объекта Win32 - он имеет финализатор, который обеспечивает закрытие дескриптора, когда объект GC-объект. SafeHandle является абстрактным классом, потому что разные дескрипторы Win32 имеют разные способы их закрытия. До введения SafeHandle, IntPtr был использован для хранения дескрипторов Win32, но ответственность за то, чтобы они были правильно закрыты и защищены от GC, была обязанностью программиста.

HandleRef это способ убедиться, что неуправляемый дескриптор не является GC, когда вы находитесь в середине вызова P/Invoke. Без чего-то вроде HandleRef, если ваш управляемый код ничего не делает с дескриптором после вызова P/Invoke, если GC был запущен во время вызова P/Invoke, он не осознает, что дескриптор все еще используется, и может его GC. Я представляю (но я не уверен и не смотрел), что SafeHandle может использовать HandleRef как часть управления инкапсулированной ручкой.

HWnd a = new HWnd();
B.SendMessage(a.Handle, ...);

Предполагая, что это единственная ссылка на "а" в программе, это эквивалентно:

HWnd a = new HWnd();
IntPtr h = a.Handle;
// a is no longer needed and thus can be GC'ed
B.SendMessage(h, ...);

Проблема в том, что когда "а" удаляется, он закрывает ручку. Если это произойдет до или во время вызова SendMessage, дескриптор будет недействительным.

HandleRef предотвращает сбор мусора до того, как программа будет выполнена с помощью h.

Похоже, что SafeHandle включает в себя поведение KeepAlive HandleRef: Project Roslyn SafeHandle.cs http://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/safehandle.cs,743afbddafaea263

/*
  Problems addressed by the SafeHandle class:
  1) Critical finalization - ensure we never leak OS resources in SQL.  Done
     without running truly arbitrary & unbounded amounts of managed code.
  2) Reduced graph promotion - during finalization, keep object graph small
  3) GC.KeepAlive behavior - P/Invoke vs. finalizer thread ---- (HandleRef)
<...>
*/

Но я не уверен, похоже, что поведение keepalive может быть достигнуто только путем предоставления ложного значения конструктору, который просто помечает объект как не финализируемый, поэтому вы должны вызвать SafeHandle's Dispose() вручную, чтобы предотвратить утечку ресурсов в этом случае. Я прав? Может кто-нибудь объяснить исходный код, что такое

private extern void InternalDispose();
private extern void InternalFinalize();

?

Другие вопросы по тегам