哪些“IntPtr”风格是“SafeHandle”的候选者

Posted

技术标签:

【中文标题】哪些“IntPtr”风格是“SafeHandle”的候选者【英文标题】:Which `IntPtr` flavors are candidates for a `SafeHandle` 【发布时间】:2017-06-12 16:49:10 【问题描述】:

我得到一个 CA2006 ("Review usage of ... (a 'IntPtr' instance) to determine whether it should be replaced with a SafeHandle or CriticalHandle") 用于从 ConvertStringSecurityDescriptorToSecurityDescriptor 返回的 IntPtr,我知道这是一个可以用 FreeLocal 释放的安全描述符。我只是简单地使用它,可能不会泄漏所涉及的内存。据我所知,没有关联内核句柄。

有很多 SafeHandle 子类,并且在包装对象的生命周期结束时,它们似乎都没有调用 FreeLocal。但是,我找不到任何关于哪些IntPtr 实例(例如,由 Win32 API 返回)由SafeHandle 有效管理而哪些不是的确切信息。

我应该只取消 CA 警告吗?

更重要的是,如何确定我将遇到的下一个IntPtr 用法相同?

(将SafeHandle 子类映射到最终将释放句柄的函数的表会很棒。)

【问题讨论】:

没有错,只是严重的过度杀戮。当你编写一个终结器来释放资源时,你会考虑它。 @HansPassant - 这有帮助,但终结者本身就是灰色地带的野兽。它们是将资源的生命周期与另一个随机资源(托管堆的一小块)联系起来。他们充其量只是第二层防御,从来没有靠自己可靠地完成任何事情。 【参考方案1】:

如果您的问题是“是否已经有一个 SafeHandle 子类调用 LocalFree 来释放它的句柄”,那么是的,有 - 它是 SafeLocalAllocHandle。该类是internal,因此它不向公众提供,但如果您想重新创建它,代码是显而易见的。如果您的问题是“我应该使用这样的课程吗”,那么这有点像判断题。 CA2006SafeHandle 文档解释了设计原理:SafeHandle 避免了多线程场景中的句柄回收问题,并特别支持 P/Invokes 以确保在使用非托管代码之前不会意外释放句柄。

IntPtr 何时是可以被SafeHandle 包裹的句柄?您无法从IntPtr 中分辨出来,您必须知道返回IntPtr 的函数是什么。它将记录您是否正在处理句柄,如果是,则在完成处理后应如何关闭句柄(经常,但绝不总是,CloseHandle)。如果你只有一个 P/Invoke 签名,那么从技术上讲,你什么都没有。特别是,如果它是句柄,则无法确定应该调用什么函数来释放句柄。 IntPtr 也可用于编组PVOIDSIZE_TP<some_struct>(尽管outref 参数更自然)或任何其他应该是指针大小的类型。

任何超出单个函数(尤其是存储在字段中的函数)的句柄作为IntPtr 都非常适合SafeHandle 包装器,就像同时使用多个IntPtrs 一样时间(以防止意外混淆不兼容的句柄)。对于不涉及多线程并且不会让句柄超出其范围的简单一次性,try .. finally 块可能就足够了。如果您愿意,为了一致性起见,所有内容都可以包装在SafeHandle 中(或明确记录您正在处理的对象类型),但这并不一定会带来更好的结果。句柄回收是一个严重的问题,但这里不是一个问题,因为句柄是本地内存,可靠性不是问题,因为任何严重到足以绕过finally 块的问题也严重到足以导致小内存泄漏没有问题。

您的特定场景(将 SDDL 解析为安全描述符)已经以 RawSecurityDescriptor 的形式在框架中实现,您应该考虑使用它来支持重新发明***(和/或实现您自己的 SecurityDescriptor 子类如果 System.Security.AccessControl 中的现有类尚未涵盖它。对于它的价值,RawSecurityDescriptor 也 P/Invokes 到 ConvertStringSecurityDescriptorToSecurityDescriptorW 并且不会打扰 SafeHandle。框架代码不一定被视为做什么的好例子(大量代码违反了官方指南),但它是一些东西。

【讨论】:

以上是关于哪些“IntPtr”风格是“SafeHandle”的候选者的主要内容,如果未能解决你的问题,请参考以下文章

SafeHandle 中 invalidHandleValue 的作用是啥?

safehandle 和析构函数

使用 SafeHandle 的约束与抽象类

无法将 ReleaseHandle 中的 SafeHandle 实例传递给本机方法

错误:ZLibStreamHandle 类型的 SafeHandle 或 CriticalHandle 未能正确释放

编组 Win32 结构时的安全句柄 (PROCESS_INFORMATION)