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();
?