确定文件移动的目标是否在C#

时间:2016-06-11 03:52:30

标签: c# .net io async-await

我有一个Windows窗体应用程序,它使用File.Move来移动文件以响应用户请求。这不是一个异步方法,但是当目标位于同一个驱动器上时,它几乎立即发生(我假设它可以只重写标头而不移动任何东西)。但是,如果目标在同一驱动器上,它会阻止应用程序。不可取。

很自然地,我想到了切换到异步。看起来这样做的方法是使用文件流并在删除原始文件之前使用Stream.CopyToAsync。没关系,除非 在同一个驱动器上,这是一个很浪费的复制。

在决定执行哪个操作之前,是否有可靠的方法来确定文件的建议目标是否位于与当前存储的驱动器不同的驱动器上?我显然可以查看驱动器号,但这看起来有点像hacky,我认为有些情况下像连接点一样无法正常工作。

另一种可能的选择是使用像Task.Run(() => File.Move(x))这样的东西,虽然我对此有一种模糊不清的感觉。

1 个答案:

答案 0 :(得分:1)

我认为最好的努力是比较卷序列号。 GetFileInformationByHandle()能够获取文件句柄的卷序列号。这绝对是RAID /条带卷的YMMV,所以如果这些适用,我会测试这个解决方案的性能。它 适用于交接点。

它的DllImport是

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileInformationByHandle(
    IntPtr hFile,
    out BY_HANDLE_FILE_INFORMATION lpFileInformation);

我们还需要一种方法来获取合适的文件句柄。由于我们正在使用潜在目标,如果我们假设目标目录已经存在,我们可以使用CreateFile()来获取目录句柄。

对于CreateFile(),DllImport是

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CreateFile(
    [MarshalAs(UnmanagedType.LPTStr)] string filename,
    [MarshalAs(UnmanagedType.U4)] FileAccess access,
    [MarshalAs(UnmanagedType.U4)] FileShare share,
    IntPtr securityAttributes,
    [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
    [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
    IntPtr templateFile);

我们还需要CloseHandle()

[DllImport("kernel32.dll", SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);

使用所有这些部分,创建一个包装器方法,如果提供的路径的卷序列号匹配,则返回true。显然有意义抛出更具体的例外,但为了简洁起见:

static bool SameVolume(string src, string dest)
{
    IntPtr srcHandle = IntPtr.Zero, destHandle = IntPtr.Zero;
    try
    {
        srcHandle = CreateFile(src, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero,
            FileMode.Open, (FileAttributes) 0x02000000, IntPtr.Zero);
        destHandle = CreateFile(dest, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero,
            FileMode.Open, (FileAttributes) 0x02000000, IntPtr.Zero);
        var srcInfo = new BY_HANDLE_FILE_INFORMATION();
        var destInfo = new BY_HANDLE_FILE_INFORMATION();

        if (!GetFileInformationByHandle(srcHandle, out srcInfo))
        {
            throw new Exception(Marshal.GetLastWin32Error().ToString());
        }

        if (!GetFileInformationByHandle(destHandle, out destInfo))
        {
            throw new Exception(Marshal.GetLastWin32Error().ToString());
        }

        return srcInfo.VolumeSerialNumber == destInfo.VolumeSerialNumber;
    }
    finally
    {
        if (srcHandle != IntPtr.Zero)
        {
            CloseHandle(srcHandle);
        }

        if (destHandle != IntPtr.Zero)
        {
            CloseHandle(destHandle);
        }
    }
}

或者,您可以使用

Task.Run(() => File.Move(x))

或其他异步构造,无需锁定应用程序的界面即可完成此操作。我看不出有什么不妥。

相关问题