SafeHandle против IntPtr в WinAPI и CloseHandle в C # - PullRequest
0 голосов
/ 08 ноября 2018

Я прочитал много статей о SafeHandle и IDiposable, но до сих пор не понимаю, следует ли мне использовать SafeHandle и CloseHandle или нет в C #.

Пример MSH SafeHandle

IntPtr, SafeHandle и HandleRef - Объяснено

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

Первый источник похож на мой вопрос, но не отвечает на мой вопрос.

Допустим, у меня есть следующие 3 различных примера, которые я получил из Интернета:

// 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;

Примеры - это API-интерфейсы Windows, поэтому они неуправляемые. Что я не получаю, это:

1) Если все 3 примера используют SafeHandle поверх IntPtr, потому что они неуправляемые, а если один из них не должен, то почему?

2) Я нашел CloseHandle для хорошей практики в C / C ++, но в C # я не знаю, закрывает ли сборщик мусора автоматически в конце? Следует ли использовать CloseHandle в приведенных выше примерах и почему?

3) В первом примере используется неуправляемый byte* для выделения памяти в куче. Можно ли выполнить эту процедуру, не используя неуправляемый указатель? Код ниже - мое предположение, что вы об этом думаете?

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

Edit: Размещение кода, который я отредактировал:

[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());

Как вы думаете, я все сделал хорошо? Следует ли использовать SafeHandle в каждом API Windows, например GetModuleHandle, GetProcAddress, MapViewOfFile и еще некоторые, которые я сейчас не помню. Некоторые из них возвращаются LPVOID.

...