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

Posted

技术标签:

【中文标题】编组 Win32 结构时的安全句柄 (PROCESS_INFORMATION)【英文标题】:Safe Handles when marshaling Win32 structs (PROCESS_INFORMATION) 【发布时间】:2016-01-09 05:27:55 【问题描述】:

我正在将我的 Win32 p/invoke 代码转换为使用 SafeHandle 类而不是典型的 IntPtr 句柄。

虽然在 DllImport 方法签名中一切正常,但在编组 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; 

ProcessSafeHandleThreadSafeHandle 类与 ReadProcessMemoryWriteProcessMemory 等方法一起工作得很好,但我不能在上面的 Win32 结构中使用它们。

我是否缺少某种注释魔法?

【问题讨论】:

我对.net一无所知,但只是看看SafeHandle类,它的构造函数有两个参数(一个IntPtr和一个bool表示所有权)。我不确定编组器如何知道 bool 是真还是假。 好点。我已经尝试让我的SafeHandle 子类中的构造函数只采用IntPtr(假设true 总是用于布尔)。不幸的是,仍然没有骰子。 只是不要试图把它全部敲进一个洞里。使用两个声明,一个将 just 用于互操作调用,另一个(通常是一个类)用于存储安全句柄。对比framework does it的方式,注意最后对SetProcessHandle的调用。 @JonathanPotter,编组器使用默认构造函数,并且具有 SafeHandle 的特殊知识,允许它设置句柄。 SafeHandle 的特定子类应提供一个默认构造函数,该构造函数传入一个合适的布尔值。 @Erik_at_Digit,您是否使用代表无效句柄的实例预先填充了句柄字段?我发现我需要预先填充用于 ref 参数的 SafeHandle 变量,结构可能有相同的要求。 【参考方案1】:

据我所知*,互操作封送拆收器不支持在类/结构中使用 SafeHandles。

因此,在 P/Invoke 函数声明中将 IntPtr 替换为 SafeHandle 可以正常工作,但在结构中替换它不起作用。 PROCESS_INFORMATION 结构中的句柄必须由对托管 SafeHandle 类一无所知的非托管代码初始化,因此 CLR 需要具备如何执行所需 [out] 封送处理的专业知识。

但不用担心。您按原样声明结构没有任何问题:

[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 的公共专业化提供。 (说到做额外的工作,我假设您已经检查以确保 Process 类还没有包含您 P/Invoking 进程 API 的原因?)

* 这可能在一些最新版本的 CLR 上有所改变;我的知识有点过时了。

【讨论】:

谢谢,完全忘记了SafeWaitHandle。使我不必编写子类。不幸的是,内置的Process 类没有公开任何访问进程内存的方法(ReadProcessMemoryWriteProcessMemory)。我创建了一个包装这些方法的流子类。此外,内置的Process 类不允许您创建带有标志的进程(如挂起)。所以我必须为此使用CreateProcess Win32 API。

以上是关于编组 Win32 结构时的安全句柄 (PROCESS_INFORMATION)的主要内容,如果未能解决你的问题,请参考以下文章

C#调用Win32 api时的内存操作

将具有 int* 成员的 C++ 结构编组到 C#

win32api win32gui win32con 窗口句柄 发送消息 常用方法

Win32获取屏幕句柄的函数是啥?

如何从 win32 进程获取线程句柄列表?

Win32界面程序