/// <summary> /// Move <paramref name="sourcePath"/> to <paramref name="destinationPath"/>. If src /// and dest are on different partitions (e.g., temp and documents are on different /// drives) then this method will do a copy followed by a delete. This is in contrast /// to Directory.Move which fails if src and dest are on different partitions. /// </summary> /// <param name="sourcePath">The source directory or file, similar to Directory.Move</param> /// <param name="destinationPath">The destination directory or file. If <paramref name="sourcePath"/> /// is a file then <paramref name="destinationPath"/> also needs to be a file.</param> public static void Move(string sourcePath, string destinationPath) { if (PathHelper.AreOnSameVolume(destinationPath, sourcePath)) { Directory.Move(sourcePath, destinationPath); return; } if (Directory.Exists(sourcePath)) { Copy(sourcePath, destinationPath); Directory.Delete(sourcePath, true); } else if (File.Exists(sourcePath)) { if (File.Exists(destinationPath) || Directory.Exists(destinationPath)) { throw new IOException("Cannot create a file when that file already exists."); } File.Copy(sourcePath, destinationPath); File.Delete(sourcePath); } else { throw new DirectoryNotFoundException( string.Format("Could not find a part of the path '{0}'", sourcePath)); } }
public static bool PathsAreOnSameVolume(string firstPath, string secondPath) { return(PathHelper.AreOnSameVolume(firstPath, secondPath)); }
/// <summary> /// If there is a problem doing the replace, a dialog is shown which tells the user /// what happened, and lets them try to fix it. It also lets them "Give up", in /// which case this returns False. /// /// To help with situations where something may temporarily be holding on to the file, /// this will retry for up to 5 seconds. /// </summary> /// <param name="sourcePath"></param> /// <param name="destinationPath"></param> /// <param name="backupPath">can be null if you don't want a replacement</param> /// <returns>if the user gives up, throws whatever exception the file system gave</returns> public static void ReplaceFileWithUserInteractionIfNeeded(string sourcePath, string destinationPath, string backupPath) { bool succeeded = false; do { try { if (UnsafeForFileReplaceMethod(sourcePath) || UnsafeForFileReplaceMethod(destinationPath) || (!string.IsNullOrEmpty(backupPath) && !PathHelper.AreOnSameVolume(sourcePath, backupPath)) || !PathHelper.AreOnSameVolume(sourcePath, destinationPath) || !File.Exists(destinationPath) ) { //can't use File.Replace or File.Move across volumes (sigh) RobustFile.ReplaceByCopyDelete(sourcePath, destinationPath, backupPath); } else { var giveUpTime = DateTime.Now.AddSeconds(5); Exception theProblem; do { theProblem = null; try { File.Replace(sourcePath, destinationPath, backupPath); } catch (UnauthorizedAccessException uae) { // We were getting this while trying to Replace on a JAARS network drive. // The network drive is U:\ which maps to \\waxhaw\users\{username}, // so it doesn't get caught by the checks above. // Both files were in the same directory and there were no permissions issues, // but the Replace command was failing with "Access to the path is denied." anyway. // I never could figure out why. See http://issues.bloomlibrary.org/youtrack/issue/BL-4179 try { RobustFile.ReplaceByCopyDelete(sourcePath, destinationPath, backupPath); } catch { // Though it probably doesn't matter, report the original exception since we prefer Replace to CopyDelete. theProblem = uae; Thread.Sleep(100); } } catch (Exception e) { theProblem = e; Thread.Sleep(100); } } while (theProblem != null && DateTime.Now < giveUpTime); if (theProblem != null) { throw theProblem; } } succeeded = true; } catch (UnauthorizedAccessException error) { ReportFailedReplacement(destinationPath, error); } catch (IOException error) { ReportFailedReplacement(destinationPath, error); } }while (!succeeded); }