I am in the process of converting my Win32 p/invoke code to use SafeHandle
classes instead of the typical IntPtr
handles.
While everything works pretty well in DllImport
method signatures, I cannot for the life of me get them to work when marshaling Win32 structs (i.e. 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; }
}
The ProcessSafeHandle
and ThreadSafeHandle
classes work just fine with methods like ReadProcessMemory
or WriteProcessMemory
, but I cannot use them in Win32 structs like above.
Am I missing some kind of annotation magic?
As far as I know*, the interop marshaler does not support the use of SafeHandles in classes/structures.
So it works fine to replace IntPtr
with SafeHandle
in a P/Invoke function declaration, but it doesn't work to replace it in a struct. The handles in the PROCESS_INFORMATION
structure have to be initialized by unmanaged code that doesn't know anything about the managed SafeHandle
class, so the CLR would need to have specialized knowledge of how to do the required [out]
marshaling.
But no worries. There's nothing wrong with your declaration of the struct as-is:
[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; }
}
If you want, once you've called the function that fills a Win32ProcessInformation
struct, you can create a SafeHandle
object for each of the handle values stored in the IntPtrs. This will ensure the handles get closed when the object gets garbage collected (if you forget to call Dispose()
earlier).
For process handles (as in this example), SafeWaitHandle
would be a good choice, since all process handles are waitable. This saves you from having to do any extra work because SafeWaitHandle
is provided already as a public specialization of SafeHandle. (Speaking of doing extra work, I assume you've already checked to be sure that the Process
class doesn't already wrap the reason you're P/Invoking process APIs?)
* This may have changed on some very recent version of the CLR; my knowledge is slightly out-of-date.