我正在将 Win32 p/invoke 代码转换为使用 SafeHandle
类,而不是典型的 IntPtr
句柄。
虽然在 DllImport
方法签名中一切都工作得很好,但在编码(marshal) Win32 结构(即 PROCESS_INFORMATION
)时,我一辈子都无法让它们工作。
// This works without issue.
[StructLayout(LayoutKind.Sequential)]
internal struct Win32ProcessInformation
{
public IntPtr ProcessHandle { get; set; }
public IntPtr ThreadHandle { get; set; }
public int ProcessId { get; set; }
public int ThreadId { get; set; }
}
// This does not work!
[StructLayout(LayoutKind.Sequential)]
internal struct Win32ProcessInformation
{
public ProcessSafeHandle ProcessHandle { get; set; }
public ThreadSafeHandle ThreadHandle { get; set; }
public int ProcessId { get; set; }
public int ThreadId { get; set; }
}
ProcessSafeHandle
和 ThreadSafeHandle
类与 ReadProcessMemory
或 WriteProcessMemory
等方法配合得很好,但我无法使用它们位于像上面这样的 Win32 结构中。
我是否缺少某种注释魔法?
最佳答案
据我所知*,互操作编码(marshal)拆收器不支持在类/结构中使用 SafeHandles。
因此,在 P/Invoke 函数声明中将 IntPtr
替换为 SafeHandle
效果很好,但在结构中替换它则不起作用。 PROCESS_INFORMATION
结构中的句柄必须由非托管代码初始化,这些代码对托管 SafeHandle
类一无所知,因此 CLR 需要具备如何初始化的专业知识执行所需的[out]
编码(marshal)处理。
不过不用担心。按原样声明结构没有任何问题:
[StructLayout(LayoutKind.Sequential)]
internal struct Win32ProcessInformation
{
public IntPtr ProcessHandle { get; set; }
public IntPtr ThreadHandle { get; set; }
public int ProcessId { get; set; }
public int ThreadId { get; set; }
}
如果您愿意,一旦调用了填充 Win32ProcessInformation
结构的函数,您就可以为存储在 IntPtrs 中的每个句柄值创建一个 SafeHandle
对象。这将确保在对象被垃圾回收时关闭句柄(如果您忘记之前调用 Dispose()
)。
对于进程句柄(如本例所示),SafeWaitHandle
将是一个不错的选择,因为所有进程句柄都是可等待的。这使您不必做任何额外的工作,因为 SafeWaitHandle
已经作为 SafeHandle 的公共(public)特化提供。 (说到做额外的工作,我假设您已经检查过以确保 Process
类尚未包含您 P/Invoking 流程 API 的原因?)
* 这可能在某些最新版本的 CLR 上发生了变化;我的知识有点过时了。
关于c# - 编码(marshal) Win32 结构时的安全句柄 (PROCESS_INFORMATION),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34690037/