了解 SafeHandle 模式和用法

问题描述 投票:0回答:1

我一直在网上阅读一些材料,例如:

所以 P/Invoke 中使用的模式如下:

// inherits from SafeHandleZeroOrMinusOneIsInvalid, so IsInvalid is already implemented.
internal sealed class MySafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    // A default constructor is required for P/Invoke to instantiate the class
    public MySafeHandle()
        : base(ownsHandle: true)
    {
    }

    protected override bool ReleaseHandle()
    {
        return NativeMethods.CloseHandle(handle);
    }
}

internal static class NativeMethods
{
    // Returns the SafeHandle instead of IntPtr
    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
    internal extern static MySafeHandle CreateFile(String fileName, int dwDesiredAccess, System.IO.FileShare dwShareMode, IntPtr securityAttrs_MustBeZero, System.IO.FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr TemplateFile_MustBeZero);

    // Take a SafeHandle in parameter instead of IntPtr
    [DllImport("kernel32", SetLastError = true)]
    internal extern static int ReadFile(MySafeHandle handle, byte[] bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

    [DllImport("kernel32", SetLastError = true)]
    internal extern static bool CloseHandle(IntPtr handle);
}

嗯,不完全是……如果我理解正确,正确的模式会更像这样:

internal static class NativeMethods
{
    [...] CreateFile / CloseHandle unchnaged [...]

    // Take a SafeHandle in parameter instead of IntPtr
    [DllImport("kernel32", SetLastError = true)]
    private extern static int ReadFile(MySafeHandle handle, byte[] bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

    internal static int ReadFileSafe(MySafeHandle handle, byte[] bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero) {
      if(handle.IsInvalid) throw new Exception("Invalid handle");
      return ReadFile(handle, bytes, numBytesToRead, numBytesRead, overlapped_MustBeZero);
    }

}

我是否正确理解了 SafeHandle 的范围?在这种情况下,有什么机制可以避免这个样板代码吗?

c# pinvoke
1个回答
0
投票

所以确实

SafeHandle
只是暴露内部句柄的状态。因此,可以使用以下方式保持 API 简单:

internal static class NativeMethods
{
    // Returns the SafeHandle instead of IntPtr
    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
    internal extern static MySafeHandle CreateFile(String fileName, int dwDesiredAccess, System.IO.FileShare dwShareMode, IntPtr securityAttrs_MustBeZero, System.IO.FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr TemplateFile_MustBeZero);

    // Take a SafeHandle in parameter instead of IntPtr
    [DllImport("kernel32", SetLastError = true)]
    internal extern static int ReadFile(MySafeHandle handle, byte[] bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

    [DllImport("kernel32", SetLastError = true)]
    internal extern static bool CloseHandle(IntPtr handle);
}

但是将状态检查实现为:

public sealed class MyFileWrapper : IDisposable
{
    private readonly MySafeHandle _handle;

    public MyFileWrapper(string fullPath)
    {
        _handle = NativeMethods.CreateFile(fullPath, ...);
        if(_handle.IsInvalid) throw new IOException("invalid handle");
    }

    // - There is no need to implement a finalizer, MySafeHandle already has one
    // - You do not need to protect against multiple disposing, MySafeHandle already does
    public void Dispose()
    {
        _handle.Dispose();
    }
}

这也记录在在线文档的示例之一中:

© www.soinside.com 2019 - 2024. All rights reserved.