我一直在网上阅读一些材料,例如:
所以 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 的范围?在这种情况下,有什么机制可以避免这个样板代码吗?
所以确实
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();
}
}
这也记录在在线文档的示例之一中: