3

考虑以下我最近更改为使用的代码FileStream.SafeFileHandle

public static void FastWrite<T>(FileStream fs, T[] array, int offset, int count) where T: struct
{
    int sizeOfT = Marshal.SizeOf(typeof(T));
    GCHandle gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);

    try
    {
        uint bytesWritten;
        uint bytesToWrite = (uint)(count * sizeOfT);
        var overlapped = new NativeOverlapped();

        if
        (
            !WriteFile
            (
                fs.SafeFileHandle,
                new IntPtr(gcHandle.AddrOfPinnedObject().ToInt64() + (offset*sizeOfT)),
                bytesToWrite,
                out bytesWritten,
                ref overlapped
            )
        )
        {
            throw new IOException("Unable to write file.", new Win32Exception(Marshal.GetLastWin32Error()));
        }

        Debug.Assert(bytesWritten == bytesToWrite);

        GC.KeepAlive(fs); // <--- Is this really not necessary?
    }

    finally
    {
        gcHandle.Free();
    }
}

[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]

private static extern bool WriteFile
(
    SafeFileHandle       hFile,
    IntPtr               lpBuffer,
    uint                 nNumberOfBytesToWrite,
    out uint             lpNumberOfBytesWritten,
    ref NativeOverlapped lpOverlapped
);

我之前添加了以确保在 Windows API调用返回GC.KeepAlive(fs)之前不会对 FileStream 进行垃圾收集。WriteFile()

但是,在更改为使用SafeFileHandle代码分析后,现在告诉我没有必要使用warning CA2004: Remove calls to GC.KeepAlive

如果要转换为 SafeHandle 用法,请删除对 GC.KeepAlive(对象)的所有调用。

我已经查阅了文档,FileStream.SafeFileHandle但我不清楚删除对GC.KeepAlive().

删除它绝对安全吗?我是否正确使用它?

另外,任何人都可以指出一些关于使用 SafeHandle 的体面文档吗?

4

1 回答 1

4

使用 SafeHandle 的要点是在 WriteFile() 函数执行时不会关闭句柄。这确实是您想要在这里实现的目标。但是请注意,FileStream 对象可能仍然是最终确定的。发布的代码中没有明显的后果。所以 FxCop 警告是合适的。

请注意使用这样的代码附加的字符串。它不太可能比 FileStream.Write() 快。但是你确实会因为没有正确处理边缘条件而增加风险。包括使用 Overlapped 但未适当处理重叠 I/O,不要那样做。如果实际上打算使用重叠 I/O,则检查此答案以了解在 CLR 中优化的方式超出了 GCHandle 的能力。仔细查看 FileStream 的Reference Source源代码,特别关注 _isAsync 字段以及 ERROR_NO_DATA 和 ERROR_INVALID_HANDLE 的错误处理。

您还会看到它使用 SafeFileHandle 而不是像 FxCop 要求的那样使用 GC.KeepAlive()。

于 2013-04-30T10:40:39.700 回答