2

I read many articles about SafeHandle and IDiposable but I still don't understand whether I should use SafeHandle and CloseHandle or not in C#.

MSDN SafeHandle example

IntPtr, SafeHandle and HandleRef - Explained

https://blog.benoitblanchon.fr/safehandle/.

The first source is similar to my question but however it doesn't answer my question.

Let's say I have the following 3 different examples that I got from Internet:

// Example 1
byte[] temp = new byte[IntPtr.Size];
fixed (byte* p = temp)
{
    IntPtr SectionHandle = new IntPtr(p);
    LARGE_INTEGER MaximumSize = new LARGE_INTEGER();
    MaximumSize.LowPart = pNTHeader->OptionalHeader.SizeOfImage;
    status = NtCreateSection(
        SectionHandle,
        SectionAccess.SECTION_MAP_EXECUTE | SectionAccess.SECTION_MAP_READ | SectionAccess.SECTION_MAP_WRITE,
        IntPtr.Zero,
        ref MaximumSize,
        MemoryProtectionConstants.PAGE_EXECUTE_READWRITE,
        AllocationTypes.SEC_COMMIT,
        IntPtr.Zero);
}

// Example 2
IntPtr hFile = CreateFile(
    path, 
    GenericRights.GENERIC_READ, 
    FileFlags.FILE_SHARE_DELETE | FileFlags.FILE_SHARE_READ | FileFlags.FILE_SHARE_WRITE, 
    IntPtr.Zero, 
    FileCreationFlags.OPEN_EXISTING, 
    FileFlags.FILE_ATTRIBUTE_NORMAL, 
    IntPtr.Zero);

if (hFile == INVALID_HANDLE_VALUE)
    return false;

// Example 3
IntPtr hMap = CreateFileMapping(
    hFile, 
    IntPtr.Zero, 
    MemoryProtectionConstants.PAGE_READONLY, 
    0, 
    0, 
    IntPtr.Zero);

if (hMap == IntPtr.Zero)
    return false;

The examples are Windows APIs, so they are unmanaged. What I don't get is:

1) Should the all 3 examples use SafeHandle over IntPtr because they are unmanaged and if one of them shouldn't, why?

2) I find CloseHandle for a good practice in C/C++ but in C# I don't know if the Garbage Collector closes the handle automatically in the end? Should CloseHandle be used in the above examples and why?

3) The first example uses an unmanaged byte* to allocate memory on the heap. Can this procedure be done by not using unmanaged pointer? The code below is my guess, what do you think about it?

IntPtr SectionHandle = Marshal.AllocateHGlobal(sizeof(int));
... code ...
Marshal.FreeHGlobal(SectionHandle);

Edit: Posting the code I edited:

[SecurityCritical]
public sealed class SafeHandleBuffer : SafeBuffer
{
    public SafeHandleBuffer()
        : base(true)
    {
        handle = Marshal.AllocHGlobal(IntPtr.Size);
    }

    public int GetValue() =>
        Marshal.ReadInt32(handle);

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    protected override bool ReleaseHandle()
    {
        Marshal.FreeHGlobal(handle);
        return true;
    }
}

public sealed class SafeHandleToken : SafeHandleZeroOrMinusOneIsInvalid
{
    private SafeHandleToken()
        : base(true)
    {
    }

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    protected override bool ReleaseHandle() => 
        CloseHandle(handle);
}

// Including the IDisposable implementation in the class where the next example calls are found
private SafeHandleToken _fileHandle, _mappingHandle;
private SafeHandleBuffer _sectionHandle;

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
protected virtual void Dispose(bool disposing)
{
    if (_fileHandle != null && !_fileHandle.IsInvalid)
    {
        _fileHandle.Dispose();
    }

    if (_mappingHandle != null && !_mappingHandle.IsInvalid)
    {
        _mappingHandle.Dispose();
    }

    if (_sectionHandle != null && !_sectionHandle.IsInvalid)
    {
        _sectionHandle.Dispose();
    }
}

// Example 1
SafeHandleBuffer tempHandle = new SafeHandleBuffer();
LARGE_INTEGER MaximumSize = new LARGE_INTEGER();
MaximumSize.LowPart = pNTHeader->OptionalHeader.SizeOfImage;
status = NtCreateSection(
    tempHandle,
    SectionAccess.SECTION_MAP_EXECUTE | SectionAccess.SECTION_MAP_READ | SectionAccess.SECTION_MAP_WRITE,
    IntPtr.Zero,
    ref MaximumSize,
    MemoryProtectionConstants.PAGE_EXECUTE_READWRITE,
    AllocationTypes.SEC_COMMIT,
    IntPtr.Zero);

// Example 2
SafeHandleToken tempHandle = CreateFile(
    path,
    GenericRights.GENERIC_READ,
    FileShare.ReadWrite | FileShare.Delete,
    IntPtr.Zero,
    FileMode.Open,
    FileAttributes.Normal,
    IntPtr.Zero);

Thread.Sleep(500);
_fileHandle = tempHandle;

if (_fileHandle.IsInvalid)
    throw new Win32Exception(Marshal.GetLastWin32Error());

// Example 3
tempHandle = CreateFileMapping(
    _fileHandle,
    IntPtr.Zero,
    (uint)MemoryProtectionConstants.PAGE_READONLY | (uint)AllocationTypes.SEC_IMAGE,
    0,
    0,
    IntPtr.Zero);

Thread.Sleep(500);
_mappingHandle = tempHandle;

if (_mappingHandle.IsInvalid)
    throw new Win32Exception(Marshal.GetLastWin32Error());

Do you think I did everything alright? Should SafeHandle be used on every Windows API like GetModuleHandle, GetProcAddress, MapViewOfFile and some more I don't remember right now. Some of them are returning LPVOID.

nop
  • 4,711
  • 6
  • 32
  • 93
  • Please **always** use `SafeHandle` in C#. This is the most safe way. Only fall back to unmanaged if it's impossible to use managed API for some reasons. The GC will not close any handles, they will leak if you don't close them. – dymanoid Nov 08 '18 at 10:07
  • Nobody should ever write code like this, already covered by FileStream and MemoryMappedFile. The first snippet is particularly nonsensical, the author did not know how to declare the function correctly. Delete them from your disk/browser cache so they can do no further harm. – Hans Passant Nov 08 '18 at 10:08
  • Does that mean even APIs like ``GetModuleHandle``, ``GetProcAddress``, ``MapViewOfFile``, etc. that can't be ``CloseHandle``'d, should be returning ``SafeHandle`` as well? Edit: They basically return also an ``IntPtr`` but not a handle that can be closed. And is ``_handle.DangerousGetHandle()`` safe because let's say I want to have operations like addition, subtraction? – nop Nov 08 '18 at 13:54
  • Cannot edit previous comment. Actually, ``MapViewOfFile`` returns ``LPVOID``, does that mean that ``SafeHandle`` is not useful for it? Does SafeHandle only include handles that can be closed with ``CloseHandle`` such as CreateFile, CreateFileMapping and some more that I don't remember right now. – nop Nov 08 '18 at 14:07
  • Edited my initial post with the code I could manage to edit. The previous questions still remain. – nop Nov 08 '18 at 16:22
  • Only use CloseHandle with actual handles to kernel objects (integer values). There's an UnmapViewOfFile function, which takes the pointer as argument that MapViewOfFile returned. – 500 - Internal Server Error Nov 08 '18 at 16:33
  • Thanks, what do you think about the ``SafeHandle``, ``SaveBuffer`` implementations I did? – nop Nov 08 '18 at 16:36

0 Answers0