使用FileStream.SafeFileHandle之后,我可以安全地删除对GC.KeepAlive()的调用吗?

时间:2013-04-30 09:49:43

标签: c# filestream safehandle

请考虑以下我最近更改为使用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
);

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

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

  

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

我已经查阅了FileStream.SafeFileHandle上的文档,但我不清楚是否可以安全地删除对GC.KeepAlive()的调用。

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

另外,有人能指出一些关于使用SafeHandle的体面文件吗?

1 个答案:

答案 0 :(得分:4)

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

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

你也会看到它使用SafeFileHandle和而不是使用GC.KeepAlive(),就像FxCop要求一样。