Renaming a file is quite simple job in C#. You can just call File.Move(oldPath, newPath)
and it works. That is until it doesn’t. Problem with this function is that it cannot rename file that differs only in case. E.g. test
cannot be renamed to Test
. And believe it or not, people sometime want to change case of their file.
Solution that first comes to mind is renaming file to something temporary and then back. E.g. test
will be renamed to test-temp
and then back to Test
. While this solution works it makes exception handling annoying. Just what are you supposed to do if there is exception after you already created test-temp
?
Fortunatelly Windows API is not so restrictive. Things can be as simple as calling MoveFile
function. Or in our case MoveFileEx
with parameters that ensure that we can move between volumes (MOVEFILE_COPY_ALLOWED) and that function returns only after work is done (MOVEFILE_WRITE_THROUGH). Quite straightforward code indeed:
public static void MovePath(string currentPath, string newPath) {
if (currentPath.StartsWith(@"\\?\", StringComparison.Ordinal) == false) { currentPath = @"\\?\" + currentPath; }
if (newPath.StartsWith(@"\\?\", StringComparison.Ordinal) == false) { newPath = @"\\?\" + newPath; }
if (NativeMethods.MoveFileExW(currentPath, newPath, NativeMethods.MOVEFILE_COPY_ALLOWED | NativeMethods.MOVEFILE_WRITE_THROUGH) == false) {
var ex = new Win32Exception();
throw new IOException(ex.Message, ex);
}
}
internal static class NativeMethods {
public const uint MOVEFILE_COPY_ALLOWED = 0x02;
public const uint MOVEFILE_WRITE_THROUGH = 0x08;
[DllImportAttribute("kernel32.dll", EntryPoint = "MoveFileExW", SetLastError=true)]
[return: MarshalAsAttribute(UnmanagedType.Bool)]
public static extern bool MoveFileExW([InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpExistingFileName, [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpNewFileName, uint dwFlags);
}