Skip to content

Instantly share code, notes, and snippets.

@jonpryor
Last active August 29, 2015 14:22
Show Gist options
  • Save jonpryor/9ba52fd50468477608db to your computer and use it in GitHub Desktop.
Save jonpryor/9ba52fd50468477608db to your computer and use it in GitHub Desktop.
SafeFileHandle
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
class App {
public static void Main ()
{
var ph = WinApi.GetCurrentProcess();
var _o = WinApi.GetStdHandle(WinApi.STD_OUTPUT_HANDLE);
Console.WriteLine("GetStdHandle(STD_OUTPUT_HANDLE)={0}", _o.ToString("x"));
var so = new SafeFileHandle(_o, false);
SafeFileHandle _n = null;
int r = WinApi.DuplicateHandle(ph, so, ph, out _n, 0, 0, WinApi.DUPLICATE_SAME_ACCESS);
Console.WriteLine("DuplicateHandle? {0}", r);
if (r != 0)
{
Console.WriteLine("_n={0}", _n);
Console.WriteLine("_n.handle={0}", _n.DangerousGetHandle().ToString("x"));
Console.WriteLine("_n.IsClosed={0}", _n.IsClosed);
Console.WriteLine("_n.IsInvalid={0}", _n.IsInvalid);
_n.Close();
Console.WriteLine("_n.IsClosed={0}", _n.IsClosed);
Console.WriteLine("_n.IsInvalid={0}", _n.IsInvalid);
}
}
}
static class WinApi
{
[DllImport("kernel32")]
public static extern int DuplicateHandle(
IntPtr hSourceProcessHandle,
SafeFileHandle hSourceHandle,
IntPtr hTargetProcessHandle,
out SafeFileHandle lpTargetHandle,
uint dwDesiredAccess,
int bInheritHandle,
uint dwOptions
);
[DllImport("kernel32")]
public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32")]
public static extern IntPtr GetStdHandle(uint nStdHandle);
public const uint DUPLICATE_SAME_ACCESS = 0x00000002;
public const uint STD_INPUT_HANDLE = unchecked((uint)-10);
public const uint STD_OUTPUT_HANDLE = unchecked((uint)-11);
public const uint STD_ERROR_HANDLE = unchecked((uint)-12);
}
Z:\tmp\csharp\sh>.\sfh.exe
GetStdHandle(STD_OUTPUT_HANDLE)=1c
DuplicateHandle? 1
_n=Microsoft.Win32.SafeHandles.SafeFileHandle
_n.handle=218
_n.IsClosed=False
_n.IsInvalid=False
_n.IsClosed=True
_n.IsInvalid=False

app.cs P/Invokes GetStdHandle() and DuplicateHandle() (among others), to do the following:

  1. Get the handle for standard output.
  2. Wrap (1) within a SafeFileHandle.
  3. Duplicate (1) into a new SafeFileHandle instance.
  4. Check the SafeHandle.IsClosed and SafeHandle.IsInvalid properties.
  5. Close() the SafeFileHandle from (3)
  6. Check the SafeHandle.IsClosed and SafeHandle.IsInvalid properties.

The result is in app.txt.

What it shows is that Close()ing a SafeHandle does not invalidate the handle. The handle continues to be valid, as far as SafeHandle.IsInvalid is concerned.

Which means that to sanely use a SafeHandle instance, both if the IsClosed and IsInvalid properties must be used, otherwise a closed handle may be used:

// BAD
if (handle.IsInvalid)
    return;

// GOOD
if (handle.IsClosed || handle.IsInvalid)
    return;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment