diff --git a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj index 81e8b72b8..8792846dd 100644 --- a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj +++ b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj @@ -223,6 +223,7 @@ + diff --git a/AlphaFS.UnitTest/Directory Class/Directory.Move/Directory.Move_UsingFiles.cs b/AlphaFS.UnitTest/Directory Class/Directory.Move/Directory.Move_UsingFiles.cs new file mode 100644 index 000000000..929d9b951 --- /dev/null +++ b/AlphaFS.UnitTest/Directory Class/Directory.Move/Directory.Move_UsingFiles.cs @@ -0,0 +1,112 @@ +/* Copyright (C) 2008-2018 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Reflection; + +namespace AlphaFS.UnitTest +{ + public partial class Directory_MoveTest + { + // Pattern: ___ + + + [TestMethod] + public void Directory_Move_UsingFiles_LocalAndNetwork_Success() + { + Directory_Move_UsingFiles(false); + Directory_Move_UsingFiles(true); + } + + + private void Directory_Move_UsingFiles(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + Console.WriteLine(); + + + // Change the drive letter to suit your environment. + var destinationDriveLetter = "D"; + var destinationDrivePath = destinationDriveLetter + Alphaleonis.Win32.Filesystem.Path.VolumeSeparator +Alphaleonis.Win32.Filesystem.Path.DirectorySeparator; + + + if (!Alphaleonis.Win32.Filesystem.Directory.ExistsDrive(destinationDrivePath, false)) + Assert.Inconclusive("This unit tests needs an additional drive: [" + destinationDrivePath + "]"); + + + + + var tempPath = UnitTestConstants.TempFolder; + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, MethodBase.GetCurrentMethod().Name)) + { + const string srcFileName = "Src file.log"; + const string dstFileName = "Dst file.log"; + + var srcFile = System.IO.Path.Combine(rootDir.Directory.FullName, srcFileName); + + var dstFile = System.IO.Path.Combine(destinationDrivePath, dstFileName); + if (isNetwork) + dstFile = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(dstFile); + + + System.IO.File.AppendAllText(srcFile, new string('*', new Random().Next(1, 1024))); + + const Alphaleonis.Win32.Filesystem.MoveOptions options = Alphaleonis.Win32.Filesystem.MoveOptions.CopyAllowed | Alphaleonis.Win32.Filesystem.MoveOptions.WriteThrough | Alphaleonis.Win32.Filesystem.MoveOptions.ReplaceExisting; + + Console.WriteLine("Src File Path: [{0}]", srcFile); + Console.WriteLine("Dst File Path: [{0}]", dstFile); + + + + + var cmr = Alphaleonis.Win32.Filesystem.Directory.Move(srcFile, dstFile, options); + + UnitTestConstants.Dump(cmr, -18); + + + Assert.IsFalse(System.IO.Directory.Exists(dstFile)); + + Assert.IsTrue(System.IO.File.Exists(dstFile)); + + Assert.AreEqual(new System.IO.FileInfo(srcFile).Length, new System.IO.FileInfo(dstFile).Length); + + + Assert.IsTrue(cmr.IsCopy); + Assert.IsTrue(cmr.IsEmulatedMove); + + Assert.IsTrue(cmr.IsFile); + Assert.IsFalse(cmr.IsDirectory); + + Assert.IsFalse(cmr.IsCanceled); + Assert.AreEqual(0, cmr.TotalFolders); + Assert.AreEqual(1, cmr.TotalFiles); + } + + + Console.WriteLine(); + } + } +} diff --git a/AlphaFS/Filesystem/CopyMoveResult.cs b/AlphaFS/Filesystem/CopyMoveResult.cs index 6a2e6a0d2..3ee30dff0 100644 --- a/AlphaFS/Filesystem/CopyMoveResult.cs +++ b/AlphaFS/Filesystem/CopyMoveResult.cs @@ -61,15 +61,15 @@ private CopyMoveResult(string source, string destination) /// Indicates the full path to the source file or directory. /// Indicates the full path to the destination file or directory. /// >When the action is a Copy, Move otherwise. - /// When indicates the sources is a directory; file otherwise. + /// When indicates the sources is a directory; file otherwise. /// if original Timestamps must be preserved, otherwise. This parameter is ignored for move operations. /// When indicates the Move action used a fallback of Copy + Delete actions. - public CopyMoveResult(string source, string destination, bool isCopy, bool isDirectory, bool preserveDates, bool emulatedMove) : this(source, destination) + public CopyMoveResult(string source, string destination, bool isCopy, bool isFolder, bool preserveDates, bool emulatedMove) : this(source, destination) { IsEmulatedMove = emulatedMove; IsCopy = isCopy; - IsDirectory = isDirectory; + IsDirectory = isFolder; TimestampsCopied = preserveDates; } diff --git a/AlphaFS/Filesystem/Directory Class/Directory.Copy.cs b/AlphaFS/Filesystem/Directory Class/Directory.Copy.cs index 9686d2cd2..937c95366 100644 --- a/AlphaFS/Filesystem/Directory Class/Directory.Copy.cs +++ b/AlphaFS/Filesystem/Directory Class/Directory.Copy.cs @@ -696,7 +696,7 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin string sourcePathLp; string destinationPathLp; bool isCopy; - + // A Move action fallback using Copy + Delete. bool emulateMove; @@ -706,25 +706,29 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin File.ValidateAndUpdatePathsAndOptions(transaction, sourcePath, destinationPath, copyOptions, moveOptions, pathFormat, out sourcePathLp, out destinationPathLp, out isCopy, out emulateMove, out delayUntilReboot, out deleteOnStartup); - + + + // Directory.Move is applicable to both folders and files. + var isFile = File.ExistsCore(transaction, false, sourcePath, PathFormat.LongFullPath); + // Check for local or network drives, such as: "C:" or "\\server\c$". - ExistsDriveOrFolderOrFile(transaction, sourcePathLp, true, (int) Win32Errors.NO_ERROR, true, false); + ExistsDriveOrFolderOrFile(transaction, sourcePathLp, !isFile, (int) Win32Errors.NO_ERROR, true, false); // File Move action: destinationPath is allowed to be null when MoveOptions.DelayUntilReboot is specified. if (!delayUntilReboot) - ExistsDriveOrFolderOrFile(transaction, destinationPathLp, true, (int) Win32Errors.NO_ERROR, true, false); + ExistsDriveOrFolderOrFile(transaction, destinationPathLp, !isFile, (int) Win32Errors.NO_ERROR, true, false); // Process Move action options, possible fallback to Copy action. if (!isCopy && !deleteOnStartup) ValidateAndUpdateCopyMoveAction(sourcePathLp, destinationPathLp, copyOptions, moveOptions, out copyOptions, out moveOptions, out isCopy, out emulateMove); - - + + pathFormat = PathFormat.LongFullPath; - - var cmr = copyMoveResult ?? new CopyMoveResult(sourcePath, destinationPath, isCopy, true, preserveDates, emulateMove); + + var cmr = copyMoveResult ?? new CopyMoveResult(sourcePath, destinationPath, isCopy, !isFile, preserveDates, emulateMove); if (isCopy) @@ -745,7 +749,11 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin } else - cmr = CopyDeleteCore(transaction, sourcePathLp, destinationPathLp, preserveDates, emulateMove, copyOptions, progressHandler, userProgressData, cmr); + { + cmr = isFile + ? File.CopyMoveCore(transaction, true, false, sourcePathLp, destinationPathLp, copyOptions, null, preserveDates, progressHandler, userProgressData, cmr, PathFormat.LongFullPath) + : CopyDeleteDirectoryCore(transaction, sourcePathLp, destinationPathLp, preserveDates, emulateMove, copyOptions, progressHandler, userProgressData, cmr); + } } // Move @@ -754,7 +762,7 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin // AlphaFS feature to overcome a MoveFileXxx limitation. // MoveOptions.ReplaceExisting: This value cannot be used if lpNewFileName or lpExistingFileName names a directory. - if (!delayUntilReboot && File.CanOverwrite(moveOptions)) + if (!isFile && !delayUntilReboot && File.CanOverwrite(moveOptions)) DeleteDirectoryCore(transaction, null, destinationPathLp, true, true, true, pathFormat); // 2017-06-07: A large target directory will probably create a progress-less delay in UI. @@ -764,7 +772,7 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin // Moves a file or directory, including its children. // Copies an existing directory, including its children to a new directory. - cmr = File.CopyMoveCore(transaction, true, true, sourcePathLp, destinationPathLp, copyOptions, moveOptions, preserveDates, progressHandler, userProgressData, cmr, pathFormat); + cmr = File.CopyMoveCore(transaction, true, !isFile, sourcePathLp, destinationPathLp, copyOptions, moveOptions, preserveDates, progressHandler, userProgressData, cmr, pathFormat); // If the move happened on the same drive, we have no knowledge of the number of files/folders. @@ -780,7 +788,7 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin } - private static CopyMoveResult CopyDeleteCore(KernelTransaction transaction, string sourcePathLp, string destinationPathLp, bool preserveDates, bool emulateMove, CopyOptions? copyOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, CopyMoveResult copyMoveResult) + private static CopyMoveResult CopyDeleteDirectoryCore(KernelTransaction transaction, string sourcePathLp, string destinationPathLp, bool preserveDates, bool emulateMove, CopyOptions? copyOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, CopyMoveResult copyMoveResult) { var cmr = copyMoveResult ?? new CopyMoveResult(sourcePathLp, destinationPathLp, true, true, preserveDates, emulateMove); @@ -870,7 +878,7 @@ private static CopyMoveResult CopyDeleteCore(KernelTransaction transaction, stri return cmr; } - + private static void ValidateAndUpdateCopyMoveAction(string sourcePathLp, string destinationPathLp, CopyOptions? copyOptions, MoveOptions? moveOptions, out CopyOptions? newCopyOptions, out MoveOptions? newMoveOptions, out bool isCopy, out bool emulateMove) { diff --git a/AlphaFS/Filesystem/File Class/File.Copy.cs b/AlphaFS/Filesystem/File Class/File.Copy.cs index 4d8334817..adac33785 100644 --- a/AlphaFS/Filesystem/File Class/File.Copy.cs +++ b/AlphaFS/Filesystem/File Class/File.Copy.cs @@ -800,7 +800,7 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, bool var cmr = copyMoveResult ?? new CopyMoveResult(sourcePath, destinationPath, isCopy, isFolder, preserveDates, emulateMove); var isMove = !isCopy; - var isSingleFileAction = null == copyMoveResult && !isFolder; + var isSingleFileAction = null == copyMoveResult && !isFolder || cmr.IsFile; preserveDates = preserveDates && isCopy && !isFolder; diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e4ed5474..0786994be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,9 @@ Version 2.2.2 (2018-XX-XX) ### Bugs Fixed -- Issue #436: Directory.GetFiles() with relative path (Thx stellarbear) -- Issue #437: Fixed PathTooLongException for boundary case of directory name length in Path.NormalizePath (Thx okrushelnitsky) - +- Issue #434: `Directory.Move` operation worked in v2.0.1, but now fails in v.2.2.1 +- Issue #436: `Directory.GetFiles()` with relative path (Thx stellarbear) +- Issue #437: Fixed `PathTooLongException` for boundary case of directory name length in `Path.NormalizePath` (Thx okrushelnitsky) Version 2.2.1 (2018-04-05) -------------