From 70fbdf501f9035fe2ba97dc45a7f7c6552e66043 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Tue, 23 May 2017 12:24:43 +0200 Subject: [PATCH 01/15] Fixes #288: `Directory.Exists` on root drive problem has come back with recent updates --- .../Directory Class/Directory_Exists.cs | 52 +++++++++++++++++++ AlphaFS/Filesystem/File Class/File.Exists.cs | 15 +++--- CHANGELOG.md | 8 +++ 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/AlphaFS.UnitTest/Directory Class/Directory_Exists.cs b/AlphaFS.UnitTest/Directory Class/Directory_Exists.cs index d6987768e..f4738d950 100644 --- a/AlphaFS.UnitTest/Directory Class/Directory_Exists.cs +++ b/AlphaFS.UnitTest/Directory Class/Directory_Exists.cs @@ -50,6 +50,57 @@ private void Directory_Exists(bool isNetwork) { UnitTestConstants.PrintUnitTestHeader(isNetwork); + + // Test Driveletter according to System.IO + + // SystemIO: "C:" + // AlphaFS : "\\?\C:" + var shouldBe = true; + var driveSysIO = UnitTestConstants.SysDrive; + var drive = @"\\?\" + UnitTestConstants.SysDrive; + if (isNetwork) { driveSysIO = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(driveSysIO); drive = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(drive); } + Console.Write("\nDrive path (System.IO, should be " + shouldBe + "):\t\t" + driveSysIO + "\t\t\t"); + var existSystemIO = System.IO.Directory.Exists(driveSysIO); + Console.WriteLine(existSystemIO); + var existAlphaFS = Alphaleonis.Win32.Filesystem.Directory.Exists(drive); + Console.Write("Drive path (AlphaFS, should be " + shouldBe + "):\t\t" + drive + "\t\t\t"); + Console.WriteLine(existAlphaFS); + Assert.AreEqual(shouldBe, existSystemIO, "The result should be: " + shouldBe); + Assert.AreEqual(existSystemIO, existAlphaFS, "The results are not equal, but were expected to be."); + Console.WriteLine(); + + + + + // Both: "\\?\C:\" + shouldBe = isNetwork; // True when network (UNC path), false when local path. + drive = @"\\?\" + UnitTestConstants.SysDrive + @"\"; + if (isNetwork) drive = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(drive); + Console.Write("Drive path (should be " + shouldBe + "):\t\t" + drive + "\t\t\t"); + existSystemIO = System.IO.Directory.Exists(drive); + existAlphaFS = Alphaleonis.Win32.Filesystem.Directory.Exists(drive); + Console.WriteLine(existSystemIO); + Assert.AreEqual(shouldBe, existSystemIO, "The result should be: " + shouldBe); + Assert.AreEqual(existSystemIO, existAlphaFS, "The results are not equal, but were expected to be."); + + + + // Both: "C:\" + shouldBe = true; + drive = UnitTestConstants.SysDrive + @"\"; + if (isNetwork) drive = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(drive); + Console.Write("Drive path (should be " + shouldBe + "):\t\t" + drive + "\t\t\t"); + existSystemIO = System.IO.Directory.Exists(drive); + existAlphaFS = Alphaleonis.Win32.Filesystem.Directory.Exists(drive); + Console.WriteLine(existSystemIO); + Assert.AreEqual(shouldBe, existSystemIO, "The result should be: " + shouldBe); + Assert.AreEqual(existSystemIO, existAlphaFS, "The results are not equal, but were expected to be."); + + + + + // Test Folder. + var tempPath = System.IO.Path.GetTempPath(); if (isNetwork) tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); @@ -67,6 +118,7 @@ private void Directory_Exists(bool isNetwork) Assert.IsTrue(Alphaleonis.Win32.Filesystem.Directory.Exists(folder), "The directory does not exists, but is expected to be."); } + Console.WriteLine(); } diff --git a/AlphaFS/Filesystem/File Class/File.Exists.cs b/AlphaFS/Filesystem/File Class/File.Exists.cs index fa808cd5f..aef64a5fb 100644 --- a/AlphaFS/Filesystem/File Class/File.Exists.cs +++ b/AlphaFS/Filesystem/File Class/File.Exists.cs @@ -194,22 +194,19 @@ internal static bool ExistsCore(bool isFolder, KernelTransaction transaction, st return false; - // DriveInfo.IsReady() will fail. - // - //// After normalizing, check whether path ends in directory separator. - //// Otherwise, FillAttributeInfoCore removes it and we may return a false positive. - //string pathRp = Path.GetRegularPathCore(path, true, false, false, false); + // Check for driveletter, such as: "C:" + var pathRp = Path.GetRegularPathCore(path, GetFullPathOptions.None, false); - //if (pathRp.Length > 0 && Path.IsDVsc(pathRp[pathRp.Length - 1], false)) - // return false; + if (pathRp.Length == 2 && Path.IsPathRooted(pathRp, false)) + path = pathRp; try { - string pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.TrimEnd | GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.CheckInvalidPathChars | GetFullPathOptions.ContinueOnNonExist); + var pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.TrimEnd | GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.CheckInvalidPathChars | GetFullPathOptions.ContinueOnNonExist); var data = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA(); - int dataInitialised = FillAttributeInfoCore(transaction, pathLp, ref data, false, true); + var dataInitialised = FillAttributeInfoCore(transaction, pathLp, ref data, false, true); return (dataInitialised == Win32Errors.ERROR_SUCCESS && data.dwFileAttributes != (FileAttributes) (-1) && diff --git a/CHANGELOG.md b/CHANGELOG.md index ad5106ca8..66260f006 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Changelog ========= +Version 2.1.3 (2017-XX-XX) +------------- + +### Bugs Fixed + +- Issue #288: `Directory.Exists` on root drive problem has come back with recent updates (Thx warrenlbrown) + + Version 2.1.2 (2016-10-30) ------------- From 8b7b2cb8bfd9711b0c69fe33f9ef0b2a28848bd4 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Tue, 23 May 2017 13:54:30 +0200 Subject: [PATCH 02/15] -Fixes #297: Incorrect domain returned from `Host.EnumerateDomainDfsRoot` when specifying domain; -Fixes #313: `GetHostShareFromPath()` fails with spaces in share name; -Split Host class unit test; --- AlphaFS.UnitTest/AlphaFS.UnitTest.csproj | 9 +- AlphaFS.UnitTest/Classes/ClassesTest.cs | 2 +- .../Directory Class/Directory_Exists.cs | 4 +- .../Host Class/AlphaFS_Host_ConnectDrive.cs | 155 +++++ .../Host Class/AlphaFS_Host_ConnectTo.cs | 148 +++++ .../AlphaFS_Host_DriveConnection.cs | 3 +- .../AlphaFS_Host_EnumerateDfsLinks.cs | 85 +++ .../AlphaFS_Host_EnumerateDfsRoot.cs | 97 +++ .../AlphaFS_Host_EnumerateDomainDfsRoot.cs | 67 +++ .../AlphaFS_Host_EnumerateDrives.cs | 51 ++ .../AlphaFS_Host_GetHostShareFromPath.cs | 126 ++++ .../Host Class/AlphaFS_Host_GetUncName.cs | 44 ++ AlphaFS.UnitTest/Host Class/HostTest.cs | 568 ------------------ .../Host Class/DistributedFileSystem.cs | 15 +- AlphaFS/Network/Host Class/Host.cs | 4 +- .../Network/Host Class/ServerMessageBlock.cs | 2 +- 16 files changed, 796 insertions(+), 584 deletions(-) create mode 100644 AlphaFS.UnitTest/Host Class/AlphaFS_Host_ConnectDrive.cs create mode 100644 AlphaFS.UnitTest/Host Class/AlphaFS_Host_ConnectTo.cs create mode 100644 AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDfsLinks.cs create mode 100644 AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDfsRoot.cs create mode 100644 AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDomainDfsRoot.cs create mode 100644 AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDrives.cs create mode 100644 AlphaFS.UnitTest/Host Class/AlphaFS_Host_GetHostShareFromPath.cs create mode 100644 AlphaFS.UnitTest/Host Class/AlphaFS_Host_GetUncName.cs delete mode 100644 AlphaFS.UnitTest/Host Class/HostTest.cs diff --git a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj index 5ef9735a5..c8f2bee59 100644 --- a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj +++ b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj @@ -192,8 +192,15 @@ + + + + + + + - + diff --git a/AlphaFS.UnitTest/Classes/ClassesTest.cs b/AlphaFS.UnitTest/Classes/ClassesTest.cs index f95995a53..09fb72218 100644 --- a/AlphaFS.UnitTest/Classes/ClassesTest.cs +++ b/AlphaFS.UnitTest/Classes/ClassesTest.cs @@ -293,7 +293,7 @@ private void DumpClassDfsInfo() Console.WriteLine("\n\n\t{0}", UnitTestConstants.Reporter(true)); if (noDomainConnection) - Assert.Inconclusive("Test ignored because the computer is probably not connected to a domain."); + Assert.Inconclusive("Test ignored because the computer is either not connected to a domain or no DFS root exists."); if (cnt == 0) Assert.Inconclusive("Nothing was enumerated, but it was expected."); diff --git a/AlphaFS.UnitTest/Directory Class/Directory_Exists.cs b/AlphaFS.UnitTest/Directory Class/Directory_Exists.cs index f4738d950..16ce57b66 100644 --- a/AlphaFS.UnitTest/Directory Class/Directory_Exists.cs +++ b/AlphaFS.UnitTest/Directory Class/Directory_Exists.cs @@ -76,7 +76,7 @@ private void Directory_Exists(bool isNetwork) shouldBe = isNetwork; // True when network (UNC path), false when local path. drive = @"\\?\" + UnitTestConstants.SysDrive + @"\"; if (isNetwork) drive = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(drive); - Console.Write("Drive path (should be " + shouldBe + "):\t\t" + drive + "\t\t\t"); + Console.Write("Drive path (System.IO, should be " + shouldBe + "):\t\t" + drive + "\t\t\t"); existSystemIO = System.IO.Directory.Exists(drive); existAlphaFS = Alphaleonis.Win32.Filesystem.Directory.Exists(drive); Console.WriteLine(existSystemIO); @@ -89,7 +89,7 @@ private void Directory_Exists(bool isNetwork) shouldBe = true; drive = UnitTestConstants.SysDrive + @"\"; if (isNetwork) drive = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(drive); - Console.Write("Drive path (should be " + shouldBe + "):\t\t" + drive + "\t\t\t"); + Console.Write("Drive path (System.IO, should be " + shouldBe + "):\t\t" + drive + "\t\t\t"); existSystemIO = System.IO.Directory.Exists(drive); existAlphaFS = Alphaleonis.Win32.Filesystem.Directory.Exists(drive); Console.WriteLine(existSystemIO); diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_ConnectDrive.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_ConnectDrive.cs new file mode 100644 index 000000000..e48eedf27 --- /dev/null +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_ConnectDrive.cs @@ -0,0 +1,155 @@ +/* Copyright (C) 2008-2016 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.Globalization; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for Host and is intended to contain all Host Unit Tests. + public partial class HostTest + { + [TestMethod] + public void AlphaFS_Host_ConnectDrive() + { + Console.WriteLine("Network.Host.ConnectDrive()"); + + #region Connect drive to share + + var drive = string.Format(CultureInfo.CurrentCulture, "{0}{1}{2}", Alphaleonis.Win32.Filesystem.DriveInfo.GetFreeDriveLetter(), Alphaleonis.Win32.Filesystem.Path.VolumeSeparatorChar, Alphaleonis.Win32.Filesystem.Path.DirectorySeparatorChar); + var share = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.LocalHostShare); + bool connectOk; + Console.WriteLine("\nConnect using a designated drive: [{0}]", drive); + try + { + UnitTestConstants.StopWatcher(true); + Alphaleonis.Win32.Network.Host.ConnectDrive(drive, share); + Console.WriteLine("\nConnectDrive(): [{0}] to: [{1}]\n\n{2}\n", drive, share, UnitTestConstants.Reporter(true)); + + connectOk = true; + + } + catch (Exception ex) + { + connectOk = false; + + Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + + Console.WriteLine("\nFailed ConnectDrive(): [{0}] to: [{1}]", drive, share); + } + + Assert.IsTrue(connectOk); + + #endregion // Connect drive to share + + + #region Disconnect drive from share + + var disconnectOk = false; + + // We only need this for the unit test. + while (!disconnectOk) + { + try + { + UnitTestConstants.StopWatcher(true); + Alphaleonis.Win32.Network.Host.DisconnectDrive(drive); + Console.WriteLine("\nDisconnectDrive(): [{0}] from: [{1}]\n\n{2}\n", drive, share, UnitTestConstants.Reporter(true)); + + disconnectOk = true; + + } + catch (Exception ex) + { + disconnectOk = false; + Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + + Console.WriteLine("\nFailed DisconnectDrive(): [{0}] from: [{1}]", drive, share); + } + } + + Assert.IsTrue(disconnectOk); + + #endregion // Disconnect drive from share + + + + + #region Connect last available drive to share + + Console.WriteLine("\nConnect using the last available drive."); + drive = null; + try + { + UnitTestConstants.StopWatcher(true); + drive = Alphaleonis.Win32.Network.Host.ConnectDrive(null, share); + Console.WriteLine("\nConnectDrive(): [{0}] to: [{1}]\n\n{2}\n", drive, share, UnitTestConstants.Reporter(true)); + + connectOk = true; + + } + catch (Exception ex) + { + connectOk = false; + + Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + + Console.WriteLine("\nFailed ConnectDrive(): [{0}] to: [{1}]", drive, share); + } + + Assert.IsTrue(connectOk); + + #endregion // Connect last available drive to share + + + #region Disconnect last available drive from share + + disconnectOk = false; + + // We only need this for the unit test. + while (!disconnectOk) + { + try + { + UnitTestConstants.StopWatcher(true); + Alphaleonis.Win32.Network.Host.DisconnectDrive(drive); + Console.WriteLine("\nDisconnectDrive(): [{0}] from: [{1}]\n\n{2}\n", drive, share, UnitTestConstants.Reporter(true)); + + disconnectOk = true; + + } + catch (Exception ex) + { + disconnectOk = false; + + Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + + Console.WriteLine("\nFailed DisconnectDrive(): [{0}] from: [{1}]", drive, share); + } + } + + Assert.IsTrue(disconnectOk); + + #endregion // Disconnect last available drive from share + } + } +} diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_ConnectTo.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_ConnectTo.cs new file mode 100644 index 000000000..8faaa29b6 --- /dev/null +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_ConnectTo.cs @@ -0,0 +1,148 @@ +/* Copyright (C) 2008-2016 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; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for Host and is intended to contain all Host Unit Tests. + public partial class HostTest + { + [TestMethod] + public void AlphaFS_Host_ConnectTo() + { + Console.WriteLine("Network.Host.ConnectTo()"); + + #region Connect to computer + + bool connectOk; + var share = Alphaleonis.Win32.Network.Host.GetUncName(); + try + { + UnitTestConstants.StopWatcher(true); + Alphaleonis.Win32.Network.Host.ConnectTo(share); + Console.WriteLine("\nConnectTo(): [{0}]\n\n{1}\n", share, UnitTestConstants.Reporter(true)); + + connectOk = true; + + } + catch (Exception ex) + { + connectOk = false; + + Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + + Console.WriteLine("\nFailed ConnectTo(): [{0}]", share); + } + + Assert.IsTrue(connectOk); + + #endregion // Connect to computer + + #region Disconnect from share + + var disconnectOk = false; + + // We only need this for the unit test. + while (!disconnectOk) + { + try + { + UnitTestConstants.StopWatcher(true); + Alphaleonis.Win32.Network.Host.DisconnectFrom(share); + Console.WriteLine("\nDisconnectFrom(): [{0}]\n\n{1}\n", share, UnitTestConstants.Reporter(true)); + + disconnectOk = true; + + } + catch (Exception ex) + { + disconnectOk = false; + + Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + + Console.WriteLine("\nFailed DisconnectFrom(): [{0}]", share); + } + } + + Assert.IsTrue(disconnectOk); + + #endregion // Disconnect from share + + + #region Connect to share + + share = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.LocalHostShare); + try + { + UnitTestConstants.StopWatcher(true); + Alphaleonis.Win32.Network.Host.ConnectTo(share); + Console.WriteLine("\nConnectTo(): [{0}]\n\n{1}\n", share, UnitTestConstants.Reporter(true)); + + connectOk = true; + + } + catch (Exception ex) + { + connectOk = false; + + Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + + Console.WriteLine("\nFailed ConnectTo(): [{0}]", share); + } + + Assert.IsTrue(connectOk); + + #endregion // Connect to share + + #region Disconnect from share + + disconnectOk = false; + + // We only need this for the unit test. + while (!disconnectOk) + { + try + { + UnitTestConstants.StopWatcher(true); + Alphaleonis.Win32.Network.Host.DisconnectFrom(share); + Console.WriteLine("\nDisconnectFrom(): [{0}]\n\n{1}\n", share, UnitTestConstants.Reporter(true)); + + disconnectOk = true; + + } + catch (Exception ex) + { + disconnectOk = false; + + Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + + Console.WriteLine("\nFailed DisconnectFrom(): [{0}]", share); + } + } + + Assert.IsTrue(disconnectOk); + + #endregion // Disconnect from share + } + } +} diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_DriveConnection.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_DriveConnection.cs index 244938ab8..927da3cb2 100644 --- a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_DriveConnection.cs +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_DriveConnection.cs @@ -19,7 +19,6 @@ * THE SOFTWARE. */ -using Alphaleonis.Win32.Network; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; @@ -33,7 +32,7 @@ public void AlphaFS_Host_DriveConnection() { var share = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.LocalHostShare); - using (var connection = new DriveConnection(share)) + using (var connection = new Alphaleonis.Win32.Network.DriveConnection(share)) { Console.WriteLine("\nUsing DriveConnection(): [{0}] to: [{1}]", connection.LocalName, share); diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDfsLinks.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDfsLinks.cs new file mode 100644 index 000000000..b193b1162 --- /dev/null +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDfsLinks.cs @@ -0,0 +1,85 @@ +/* Copyright (C) 2008-2016 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.Linq; +using System.Net.NetworkInformation; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for Host and is intended to contain all Host Unit Tests. + public partial class HostTest + { + [TestMethod] + public void AlphaFS_Host_EnumerateDfsLinks() + { + Console.WriteLine("Network.Host.EnumerateDfsLinks()"); + + var cnt = 0; + var noDomainConnection = true; + UnitTestConstants.StopWatcher(true); + try + { + foreach (var dfsNamespace in Alphaleonis.Win32.Network.Host.EnumerateDomainDfsRoot()) + { + noDomainConnection = false; + + Console.Write("\n#{0:000}\tDFS Root: [{1}]\n", ++cnt, dfsNamespace); + var cnt2 = 0; + + try + { + foreach (var dfsInfo in Alphaleonis.Win32.Network.Host.EnumerateDfsLinks(dfsNamespace).OrderBy(d => d.EntryPath)) + Console.Write("\n\t#{0:000}\tDFS Link: [{1}]", ++cnt2, dfsInfo.EntryPath); + } + catch (NetworkInformationException ex) + { + Console.WriteLine("\n\tNetworkInformationException #1: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); + } + catch (Exception ex) + { + Console.WriteLine("\n\tCaught (unexpected #1) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + } + + Console.WriteLine(); + } + } + catch (NetworkInformationException ex) + { + Console.WriteLine("\n\tNetworkInformationException #2: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); + } + catch (Exception ex) + { + Console.WriteLine("\n\tCaught (unexpected #2) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + } + + Console.WriteLine("\n\n{0}", UnitTestConstants.Reporter(true)); + + if (noDomainConnection) + Assert.Inconclusive("Test ignored because the computer is either not connected to a domain or no DFS root exists."); + else if (cnt == 0) + Assert.Inconclusive("Nothing was enumerated, but it was expected."); + + Console.WriteLine(); + } + } +} diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDfsRoot.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDfsRoot.cs new file mode 100644 index 000000000..79f3fc3b2 --- /dev/null +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDfsRoot.cs @@ -0,0 +1,97 @@ +/* Copyright (C) 2008-2016 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.Net.NetworkInformation; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for Host and is intended to contain all Host Unit Tests. + public partial class HostTest + { + [TestMethod] + public void AlphaFS_Host_EnumerateDfsRoot() + { + Console.WriteLine("Network.Host.EnumerateDfsRoot()"); + + var cnt = 0; + var noDomainConnection = true; + UnitTestConstants.StopWatcher(true); + + // Drill down to get servers from the first namespace retrieved. + + try + { + foreach (var dfsName in Alphaleonis.Win32.Network.Host.EnumerateDomainDfsRoot()) + { + noDomainConnection = false; + + Console.Write("\n#{0:000}\tDFS Root: [{1}]\n", ++cnt, dfsName); + + try + { + var dfsInfo = Alphaleonis.Win32.Network.Host.GetDfsInfo(dfsName); + + foreach (var storage in dfsInfo.StorageInfoCollection) + { + var cnt2 = 0; + Console.Write("\n\tEnumerating DFS Namespaces from host: [{0}]\n", storage.ServerName); + + foreach (var dfsNamespace in Alphaleonis.Win32.Network.Host.EnumerateDfsRoot(storage.ServerName, true)) + Console.Write("\t#{0:000}\tDFS Root: [{1}]\n", ++cnt2, dfsNamespace); + } + } + catch (NetworkInformationException ex) + { + Console.WriteLine("\n\tNetworkInformationException #1: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); + } + catch (Exception ex) + { + Console.WriteLine("\n\tCaught (unexpected #1) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + } + } + + Console.Write("\n{0}", UnitTestConstants.Reporter(true)); + + if (cnt == 0) + Assert.Inconclusive("Nothing was enumerated, but it was expected."); + } + catch (NetworkInformationException ex) + { + Console.WriteLine("\n\tNetworkInformationException #2: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); + } + catch (Exception ex) + { + Console.WriteLine("\n\tCaught (unexpected #2) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + } + + + Console.WriteLine("\n\n{0}", UnitTestConstants.Reporter(true)); + + + if (noDomainConnection) + Assert.Inconclusive("Test ignored because the computer is either not connected to a domain or no DFS root exists."); + else if (cnt == 0) + Assert.Inconclusive("Nothing was enumerated, but it was expected."); + } + } +} diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDomainDfsRoot.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDomainDfsRoot.cs new file mode 100644 index 000000000..6a3ab727b --- /dev/null +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDomainDfsRoot.cs @@ -0,0 +1,67 @@ +/* Copyright (C) 2008-2016 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.Net.NetworkInformation; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for Host and is intended to contain all Host Unit Tests. + public partial class HostTest + { + [TestMethod] + public void AlphaFS_Host_EnumerateDomainDfsRoot() + { + Console.WriteLine("Network.Host.EnumerateDomainDfsRoot()"); + + Console.Write("\nEnumerating DFS Root from user domain: [{0}]\n", Alphaleonis.Win32.Network.NativeMethods.ComputerDomain); + var cnt = 0; + var noDomainConnection = true; + UnitTestConstants.StopWatcher(true); + try + { + foreach (var dfsNamespace in Alphaleonis.Win32.Network.Host.EnumerateDomainDfsRoot()) + { + noDomainConnection = false; + Console.Write("\n\t#{0:000}\tDFS Root: [{1}]", ++cnt, dfsNamespace); + } + } + catch (NetworkInformationException ex) + { + Console.WriteLine("\n\tNetworkInformationException: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); + } + catch (Exception ex) + { + Console.WriteLine("\n\tCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); + } + + Console.WriteLine("\n\n{0}", UnitTestConstants.Reporter(true)); + + if (noDomainConnection) + Assert.Inconclusive("Test ignored because the computer is either not connected to a domain or no DFS root exists."); + else if (cnt == 0) + Assert.Inconclusive("Nothing was enumerated, but it was expected."); + + Console.WriteLine(); + } + } +} diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDrives.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDrives.cs new file mode 100644 index 000000000..3deebe28e --- /dev/null +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_EnumerateDrives.cs @@ -0,0 +1,51 @@ +/* Copyright (C) 2008-2016 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; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for Host and is intended to contain all Host Unit Tests. + public partial class HostTest + { + [TestMethod] + public void AlphaFS_Host_EnumerateDrives() + { + Console.WriteLine("Network.Host.EnumerateDrives()"); + + var host = UnitTestConstants.LocalHost; + + + Console.WriteLine("\nEnumerating drives from host: [{0}]\n", host); + var cnt = 0; + UnitTestConstants.StopWatcher(true); + + foreach (var drive in Alphaleonis.Win32.Network.Host.EnumerateDrives(host, true)) + Console.WriteLine("\t#{0:000}\tDrive: [{1}]", ++cnt, drive); + + Console.WriteLine("\n{0}", UnitTestConstants.Reporter(true)); + + if (cnt == 0) + Assert.Inconclusive("Nothing was enumerated, but it was expected."); + } + } +} diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_GetHostShareFromPath.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_GetHostShareFromPath.cs new file mode 100644 index 000000000..748fd514c --- /dev/null +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_GetHostShareFromPath.cs @@ -0,0 +1,126 @@ +/* Copyright (C) 2008-2016 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 Alphaleonis.Win32.Network; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for Host and is intended to contain all Host Unit Tests. + public partial class HostTest + { + [TestMethod] + public void AlphaFS_Host_GetHostShareFromPath() + { + Console.WriteLine("Network.Host.GetHostShareFromPath\n"); + + var folderWithspaces = @"\Folder with spaces"; + + + // Local Path. + var uncPath = UnitTestConstants.SysDrive; + var hostAndShare = Host.GetHostShareFromPath(uncPath); + Console.WriteLine("Input local path: [{0}]", uncPath); + Console.WriteLine("\tResult == null: {0}", null == hostAndShare); + + Assert.AreEqual(null, hostAndShare); + + + Console.WriteLine(); + + + // Local Path. + uncPath = UnitTestConstants.SysDrive + folderWithspaces; + hostAndShare = Host.GetHostShareFromPath(uncPath); + Console.WriteLine("Input local path: [{0}]", uncPath); + Console.WriteLine("\tResult == null: {0}", null == hostAndShare); + + Assert.AreEqual(null, hostAndShare); + + + Console.WriteLine(); + + + // Local Path. + uncPath = Alphaleonis.Win32.Filesystem.Path.GetLongPath(UnitTestConstants.SysRoot + folderWithspaces); + hostAndShare = Host.GetHostShareFromPath(uncPath); + Console.WriteLine("Input local path: [{0}]", uncPath); + Console.WriteLine("\tResult == null: {0}", null == hostAndShare); + Assert.AreEqual(null, hostAndShare); + + + Console.WriteLine(); + + + // Network Path as regular path. + uncPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.SysDrive); + hostAndShare = Host.GetHostShareFromPath(uncPath); + Console.WriteLine("Input UNC path: [{0}]", uncPath); + Console.WriteLine("\tHost : [{0}]", hostAndShare[0]); + Console.WriteLine("\tShare: [{0}]", hostAndShare[1]); + + Assert.IsTrue(hostAndShare[1].EndsWith("$")); + Assert.AreEqual(Environment.MachineName, hostAndShare[0].ToUpperInvariant()); + + + Console.WriteLine(); + + + // Network Path as regular path. + uncPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.SysDrive + @"\"); + hostAndShare = Host.GetHostShareFromPath(uncPath); + Console.WriteLine("Input UNC path: [{0}]", uncPath); + Console.WriteLine("\tHost : [{0}]", hostAndShare[0]); + Console.WriteLine("\tShare: [{0}]", hostAndShare[1]); + + Assert.IsTrue(hostAndShare[1].EndsWith(@"\")); + Assert.AreEqual(Environment.MachineName, hostAndShare[0].ToUpperInvariant()); + + + Console.WriteLine(); + + + // Network Path as regular path. + uncPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.SysDrive + folderWithspaces); + hostAndShare = Host.GetHostShareFromPath(uncPath); + Console.WriteLine("Input UNC path: [{0}]", uncPath); + Console.WriteLine("\tHost : [{0}]", hostAndShare[0]); + Console.WriteLine("\tShare: [{0}]", hostAndShare[1]); + + Assert.AreEqual(Environment.MachineName, hostAndShare[0].ToUpperInvariant()); + + + Console.WriteLine(); + + + // Network Path as long path. + uncPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.SysRoot + folderWithspaces, true); + hostAndShare = Host.GetHostShareFromPath(uncPath); + Console.WriteLine("Input UNC path: [{0}]", uncPath); + Console.WriteLine("\tHost : [{0}]", hostAndShare[0]); + Console.WriteLine("\tShare: [{0}]", hostAndShare[1]); + + Assert.IsFalse(hostAndShare[0].Contains("/")); + Assert.AreEqual(Environment.MachineName, hostAndShare[0].ToUpperInvariant()); + } + } +} diff --git a/AlphaFS.UnitTest/Host Class/AlphaFS_Host_GetUncName.cs b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_GetUncName.cs new file mode 100644 index 000000000..8119e0e4e --- /dev/null +++ b/AlphaFS.UnitTest/Host Class/AlphaFS_Host_GetUncName.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2008-2016 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; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for Host and is intended to contain all Host Unit Tests. + [TestClass] + public partial class HostTest + { + [TestMethod] + public void AlphaFS_Host_GetUncName() + { + Console.WriteLine("Network.Host.GetUncName()"); + + var hostUncName = Alphaleonis.Win32.Network.Host.GetUncName(); + Console.WriteLine("\nHost.GetUncName(): [{0}]", hostUncName); + + Assert.IsTrue(hostUncName.Contains(@"\")); + + Assert.AreEqual(Alphaleonis.Win32.Filesystem.Path.UncPrefix + Environment.MachineName, hostUncName); + } + } +} diff --git a/AlphaFS.UnitTest/Host Class/HostTest.cs b/AlphaFS.UnitTest/Host Class/HostTest.cs deleted file mode 100644 index 6951b8f82..000000000 --- a/AlphaFS.UnitTest/Host Class/HostTest.cs +++ /dev/null @@ -1,568 +0,0 @@ -/* Copyright (C) 2008-2016 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 System.Net.NetworkInformation; -using Alphaleonis.Win32.Filesystem; -using Alphaleonis.Win32.Network; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Globalization; -using System.Linq; -using NativeMethods = Alphaleonis.Win32.Network.NativeMethods; -using Path = Alphaleonis.Win32.Filesystem.Path; - -namespace AlphaFS.UnitTest -{ - /// This is a test class for Host and is intended to contain all Host Unit Tests. - [TestClass] - public partial class HostTest - { - #region DumpEnumerateDrives - - private void DumpEnumerateDrives(bool isLocal) - { - Console.WriteLine("\n=== TEST {0} ===", isLocal ? UnitTestConstants.Local : UnitTestConstants.Network); - var host = UnitTestConstants.LocalHost; - - - if (isLocal) - { - var nonX = Path.GetRandomFileName(); - var caughtException = false; - Console.WriteLine("\nEnumerating drives from (non-existing) host: [{0}]\n", nonX); - UnitTestConstants.StopWatcher(true); - try - { - Host.EnumerateDrives(nonX, false).Any(); - } - catch (Exception ex) - { - caughtException = true; - Console.Write("Caught (expected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - } - Console.WriteLine("\n{0}\n", UnitTestConstants.Reporter(true)); - Assert.IsTrue(caughtException, "No Exception was caught."); - - Console.Write("\n"); - } - - - Console.WriteLine("Enumerating drives from host: [{0}]\n", host); - var cnt = 0; - UnitTestConstants.StopWatcher(true); - - foreach (var drive in Host.EnumerateDrives(host, true)) - { - Console.WriteLine("\t#{0:000}\tDrive: [{1}]", ++cnt, drive); - } - - Console.WriteLine("\n{0}", UnitTestConstants.Reporter(true)); - - if (cnt == 0) - Assert.Inconclusive("Nothing was enumerated, but it was expected."); - - Console.WriteLine(); - } - - #endregion // DumpEnumerateDrives - - - #region ConnectDrive - - [TestMethod] - public void AlphaFS_Host_ConnectDrive() - { - Console.WriteLine("Network.Host.ConnectDrive()"); - - #region Connect drive to share - - var drive = string.Format(CultureInfo.CurrentCulture, "{0}{1}{2}", DriveInfo.GetFreeDriveLetter(), Path.VolumeSeparatorChar, Path.DirectorySeparatorChar); - var share = Path.LocalToUnc(UnitTestConstants.LocalHostShare); - bool connectOk; - Console.WriteLine("\nConnect using a designated drive: [{0}]", drive); - try - { - UnitTestConstants.StopWatcher(true); - Host.ConnectDrive(drive, share); - Console.WriteLine("\nConnectDrive(): [{0}] to: [{1}]\n\n{2}\n", drive, share, UnitTestConstants.Reporter(true)); - - connectOk = true; - - } - catch (Exception ex) - { - connectOk = false; - - Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - - Console.WriteLine("\nFailed ConnectDrive(): [{0}] to: [{1}]", drive, share); - } - - Assert.IsTrue(connectOk); - - #endregion // Connect drive to share - - #region Disconnect drive from share - - var disconnectOk = false; - - // We only need this for the unit test. - while (!disconnectOk) - { - try - { - UnitTestConstants.StopWatcher(true); - Host.DisconnectDrive(drive); - Console.WriteLine("\nDisconnectDrive(): [{0}] from: [{1}]\n\n{2}\n", drive, share, UnitTestConstants.Reporter(true)); - - disconnectOk = true; - - } - catch (Exception ex) - { - disconnectOk = false; - Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - - Console.WriteLine("\nFailed DisconnectDrive(): [{0}] from: [{1}]", drive, share); - } - } - - Assert.IsTrue(disconnectOk); - - #endregion // Disconnect drive from share - - - #region Connect last available drive to share - - Console.WriteLine("\nConnect using the last available drive."); - drive = null; - try - { - UnitTestConstants.StopWatcher(true); - drive = Host.ConnectDrive(null, share); - Console.WriteLine("\nConnectDrive(): [{0}] to: [{1}]\n\n{2}\n", drive, share, UnitTestConstants.Reporter(true)); - - connectOk = true; - - } - catch (Exception ex) - { - connectOk = false; - - Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - - Console.WriteLine("\nFailed ConnectDrive(): [{0}] to: [{1}]", drive, share); - } - - Assert.IsTrue(connectOk); - - #endregion // Connect last available drive to share - - #region Disconnect last available drive from share - - disconnectOk = false; - - // We only need this for the unit test. - while (!disconnectOk) - { - try - { - UnitTestConstants.StopWatcher(true); - Host.DisconnectDrive(drive); - Console.WriteLine("\nDisconnectDrive(): [{0}] from: [{1}]\n\n{2}\n", drive, share, UnitTestConstants.Reporter(true)); - - disconnectOk = true; - - } - catch (Exception ex) - { - disconnectOk = false; - - Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - - Console.WriteLine("\nFailed DisconnectDrive(): [{0}] from: [{1}]", drive, share); - } - } - - Assert.IsTrue(disconnectOk); - - #endregion // Disconnect last available drive from share - } - - #endregion // ConnectDrive - - #region ConnectTo - - [TestMethod] - public void AlphaFS_Host_ConnectTo() - { - Console.WriteLine("Network.Host.ConnectTo()"); - - #region Connect to computer - - bool connectOk; - var share = Host.GetUncName(); - try - { - UnitTestConstants.StopWatcher(true); - Host.ConnectTo(share); - Console.WriteLine("\nConnectTo(): [{0}]\n\n{1}\n", share, UnitTestConstants.Reporter(true)); - - connectOk = true; - - } - catch (Exception ex) - { - connectOk = false; - - Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - - Console.WriteLine("\nFailed ConnectTo(): [{0}]", share); - } - - Assert.IsTrue(connectOk); - - #endregion // Connect to computer - - #region Disconnect from share - - var disconnectOk = false; - - // We only need this for the unit test. - while (!disconnectOk) - { - try - { - UnitTestConstants.StopWatcher(true); - Host.DisconnectFrom(share); - Console.WriteLine("\nDisconnectFrom(): [{0}]\n\n{1}\n", share, UnitTestConstants.Reporter(true)); - - disconnectOk = true; - - } - catch (Exception ex) - { - disconnectOk = false; - - Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - - Console.WriteLine("\nFailed DisconnectFrom(): [{0}]", share); - } - } - - Assert.IsTrue(disconnectOk); - - #endregion // Disconnect from share - - - #region Connect to share - - share = Path.LocalToUnc(UnitTestConstants.LocalHostShare); - try - { - UnitTestConstants.StopWatcher(true); - Host.ConnectTo(share); - Console.WriteLine("\nConnectTo(): [{0}]\n\n{1}\n", share, UnitTestConstants.Reporter(true)); - - connectOk = true; - - } - catch (Exception ex) - { - connectOk = false; - - Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - - Console.WriteLine("\nFailed ConnectTo(): [{0}]", share); - } - - Assert.IsTrue(connectOk); - - #endregion // Connect to share - - #region Disconnect from share - - disconnectOk = false; - - // We only need this for the unit test. - while (!disconnectOk) - { - try - { - UnitTestConstants.StopWatcher(true); - Host.DisconnectFrom(share); - Console.WriteLine("\nDisconnectFrom(): [{0}]\n\n{1}\n", share, UnitTestConstants.Reporter(true)); - - disconnectOk = true; - - } - catch (Exception ex) - { - disconnectOk = false; - - Console.WriteLine("\nCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - - Console.WriteLine("\nFailed DisconnectFrom(): [{0}]", share); - } - } - - Assert.IsTrue(disconnectOk); - - #endregion // Disconnect from share - } - - #endregion // ConnectTo - - #region EnumerateDfsLinks - - [TestMethod] - public void AlphaFS_Host_EnumerateDfsLinks() - { - Console.WriteLine("Network.Host.EnumerateDfsLinks()"); - - var cnt = 0; - var noDomainConnection = true; - UnitTestConstants.StopWatcher(true); - try - { - foreach (var dfsNamespace in Host.EnumerateDomainDfsRoot()) - { - noDomainConnection = false; - - Console.Write("\n#{0:000}\tDFS Root: [{1}]\n", ++cnt, dfsNamespace); - var cnt2 = 0; - - try - { - foreach (var dfsInfo in Host.EnumerateDfsLinks(dfsNamespace).OrderBy(d => d.EntryPath)) - Console.Write("\n\t#{0:000}\tDFS Link: [{1}]", ++cnt2, dfsInfo.EntryPath); - } - catch (NetworkInformationException ex) - { - Console.WriteLine("\n\tNetworkInformationException #1: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); - } - catch (Exception ex) - { - Console.WriteLine("\n\tCaught (unexpected #1) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - } - - Console.WriteLine(); - } - } - catch (NetworkInformationException ex) - { - Console.WriteLine("\n\tNetworkInformationException #2: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); - } - catch (Exception ex) - { - Console.WriteLine("\n\tCaught (unexpected #2) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - } - - Console.WriteLine("\n\n{0}", UnitTestConstants.Reporter(true)); - - if (noDomainConnection) - Assert.Inconclusive("Test ignored because the computer is probably not connected to a domain."); - else if (cnt == 0) - Assert.Inconclusive("Nothing was enumerated, but it was expected."); - - Console.WriteLine(); - } - - #endregion // EnumerateDfsLinks - - #region EnumerateDfsRoot - - [TestMethod] - public void AlphaFS_Host_EnumerateDfsRoot() - { - Console.WriteLine("Network.Host.EnumerateDfsRoot()"); - - var cnt = 0; - var noDomainConnection = true; - UnitTestConstants.StopWatcher(true); - - // Drill down to get servers from the first namespace retrieved. - - try - { - foreach (var dfsName in Host.EnumerateDomainDfsRoot()) - { - noDomainConnection = false; - - Console.Write("\n#{0:000}\tDFS Root: [{1}]\n", ++cnt, dfsName); - - try - { - var dfsInfo = Host.GetDfsInfo(dfsName); - - foreach (var storage in dfsInfo.StorageInfoCollection) - { - var cnt2 = 0; - Console.Write("\n\tEnumerating DFS Namespaces from host: [{0}]\n", storage.ServerName); - - foreach (var dfsNamespace in Host.EnumerateDfsRoot(storage.ServerName, true)) - Console.Write("\t#{0:000}\tDFS Root: [{1}]\n", ++cnt2, dfsNamespace); - } - } - catch (NetworkInformationException ex) - { - Console.WriteLine("\n\tNetworkInformationException #1: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); - } - catch (Exception ex) - { - Console.WriteLine("\n\tCaught (unexpected #1) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - } - } - - Console.Write("\n{0}", UnitTestConstants.Reporter(true)); - - if (cnt == 0) - Assert.Inconclusive("Nothing was enumerated, but it was expected."); - } - catch (NetworkInformationException ex) - { - Console.WriteLine("\n\tNetworkInformationException #2: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); - } - catch (Exception ex) - { - Console.WriteLine("\n\tCaught (unexpected #2) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - } - - Console.WriteLine("\n\n{0}", UnitTestConstants.Reporter(true)); - - if (noDomainConnection) - Assert.Inconclusive("Test ignored because the computer is probably not connected to a domain."); - else if (cnt == 0) - Assert.Inconclusive("Nothing was enumerated, but it was expected."); - - Console.WriteLine(); - } - - #endregion // EnumerateDfsRoot - - #region EnumerateDomainDfsRoot - - [TestMethod] - public void AlphaFS_Host_EnumerateDomainDfsRoot() - { - Console.WriteLine("Network.Host.EnumerateDomainDfsRoot()"); - - Console.Write("\nEnumerating DFS Root from user domain: [{0}]\n", NativeMethods.ComputerDomain); - var cnt = 0; - var noDomainConnection = true; - UnitTestConstants.StopWatcher(true); - try - { - foreach (var dfsNamespace in Host.EnumerateDomainDfsRoot()) - { - noDomainConnection = false; - Console.Write("\n\t#{0:000}\tDFS Root: [{1}]", ++cnt, dfsNamespace); - } - } - catch (NetworkInformationException ex) - { - Console.WriteLine("\n\tNetworkInformationException: [{0}]", ex.Message.Replace(Environment.NewLine, " ")); - } - catch (Exception ex) - { - Console.WriteLine("\n\tCaught (unexpected) {0}: [{1}]", ex.GetType().FullName, ex.Message.Replace(Environment.NewLine, " ")); - } - - Console.WriteLine("\n\n{0}", UnitTestConstants.Reporter(true)); - - if (noDomainConnection) - Assert.Inconclusive("Test ignored because the computer is probably not connected to a domain."); - else if (cnt == 0) - Assert.Inconclusive("Nothing was enumerated, but it was expected."); - - Console.WriteLine(); - } - - #endregion // EnumerateDomainDfsRoot - - #region EnumerateDrives - - [TestMethod] - public void AlphaFS_Host_EnumerateDrives() - { - Console.WriteLine("Network.Host.EnumerateDrives()"); - - DumpEnumerateDrives(true); - } - - #endregion // EnumerateDrives - - #region GetHostShareFromPath - - [TestMethod] - public void AlphaFS_Host_GetHostShareFromPath() - { - Console.WriteLine("Network.Host.GetHostShareFromPath\n"); - - var uncPath = UnitTestConstants.SysRoot32; - var hostAndShare = Host.GetHostShareFromPath(uncPath); - Console.WriteLine("Input local path: [{0}]", uncPath); - Assert.AreEqual(null, hostAndShare); - - uncPath = Path.GetLongPath(UnitTestConstants.SysRoot32); - hostAndShare = Host.GetHostShareFromPath(uncPath); - Console.WriteLine("Input local path: [{0}]", uncPath); - Assert.AreEqual(null, hostAndShare); - - Console.WriteLine(); - - uncPath = Path.LocalToUnc(UnitTestConstants.SysRoot32); - hostAndShare = Host.GetHostShareFromPath(uncPath); - Console.WriteLine("Input UNC path: [{0}]", uncPath); - Console.WriteLine("\tHost : [{0}]", hostAndShare[0]); - Console.WriteLine("\tShare: [{0}]", hostAndShare[1]); - - Assert.AreEqual(Environment.MachineName, hostAndShare[0].ToUpperInvariant()); - - Console.WriteLine(); - - uncPath = Path.LocalToUnc(UnitTestConstants.SysRoot32, true); - hostAndShare = Host.GetHostShareFromPath(uncPath); - Console.WriteLine("Input UNC path: [{0}]", uncPath); - Console.WriteLine("\tHost : [{0}]", hostAndShare[0]); - Console.WriteLine("\tShare: [{0}]", hostAndShare[1]); - - Assert.AreEqual(Environment.MachineName, hostAndShare[0].ToUpperInvariant()); - } - - #endregion // GetHostShareFromPath - - #region GetUncName - - [TestMethod] - public void AlphaFS_Host_GetUncName() - { - Console.WriteLine("Network.Host.GetUncName()"); - - var hostUncName = Host.GetUncName(); - Console.WriteLine("\nHost.GetUncName(): [{0}]", hostUncName); - - Assert.AreEqual(Path.UncPrefix + Environment.MachineName, hostUncName); - } - - #endregion // GetUncName - } -} diff --git a/AlphaFS/Network/Host Class/DistributedFileSystem.cs b/AlphaFS/Network/Host Class/DistributedFileSystem.cs index c921811d6..5cd3921bb 100644 --- a/AlphaFS/Network/Host Class/DistributedFileSystem.cs +++ b/AlphaFS/Network/Host Class/DistributedFileSystem.cs @@ -202,7 +202,7 @@ private static IEnumerable EnumerateDfsRootCore(string host, bool contin // However, the resulting OpenResourceInfo.Host property will be empty. // So, explicitly state Environment.MachineName to prevent this. // Furthermore, the UNC prefix: \\ is not required and always removed. - string stripUnc = Utils.IsNullOrWhiteSpace(host) ? Environment.MachineName : Path.GetRegularPathCore(host, GetFullPathOptions.CheckInvalidPathChars, false).Replace(Path.UncPrefix, string.Empty); + var stripUnc = !Utils.IsNullOrWhiteSpace(host) ? Path.GetRegularPathCore(host, GetFullPathOptions.CheckInvalidPathChars, false).Replace(Path.UncPrefix, string.Empty) : Environment.MachineName; return NativeMethods.NetDfsEnum(stripUnc, 300, prefMaxLen, out buffer, out entriesRead, out resumeHandle); @@ -223,20 +223,21 @@ private static IEnumerable EnumerateDomainDfsRootCore(string domain, boo { if (!Filesystem.NativeMethods.IsAtLeastWindowsVista) throw new PlatformNotSupportedException(Resources.Requires_Windows_Vista_Or_Higher); + + return EnumerateNetworkObjectCore(new FunctionData(), + (NativeMethods.DFS_INFO_200 structure, SafeGlobalMemoryBufferHandle buffer) => - return EnumerateNetworkObjectCore(new FunctionData(), (NativeMethods.DFS_INFO_200 structure, SafeGlobalMemoryBufferHandle buffer) => - - new DfsInfo { EntryPath = string.Format(CultureInfo.CurrentCulture, "{0}{1}{2}{3}", Path.UncPrefix, NativeMethods.ComputerDomain, Path.DirectorySeparatorChar, structure.FtDfsName) }, + new DfsInfo { EntryPath = string.Format(CultureInfo.CurrentCulture, "{0}{1}{2}{3}", Path.UncPrefix, !Utils.IsNullOrWhiteSpace(domain) ? domain : NativeMethods.ComputerDomain, Path.DirectorySeparatorChar, structure.FtDfsName) }, (FunctionData functionData, out SafeGlobalMemoryBufferHandle buffer, int prefMaxLen, out uint entriesRead, out uint totalEntries, out uint resumeHandle) => { totalEntries = 0; // When host == null, the local computer is used. - // However, the resulting OpenResourceInfo.Host property will be empty. + // However, the resulting Host property will be empty. // So, explicitly state Environment.MachineName to prevent this. // Furthermore, the UNC prefix: \\ is not required and always removed. - string stripUnc = Utils.IsNullOrWhiteSpace(domain) ? NativeMethods.ComputerDomain : Path.GetRegularPathCore(domain, GetFullPathOptions.CheckInvalidPathChars, false).Replace(Path.UncPrefix, string.Empty); + var stripUnc = !Utils.IsNullOrWhiteSpace(domain) ? Path.GetRegularPathCore(domain, GetFullPathOptions.CheckInvalidPathChars, false).Replace(Path.UncPrefix, string.Empty) : NativeMethods.ComputerDomain; return NativeMethods.NetDfsEnum(stripUnc, 200, prefMaxLen, out buffer, out entriesRead, out resumeHandle); @@ -279,7 +280,7 @@ private static DfsInfo GetDfsInfoCore(bool getFromClient, string dfsName, string // Level 9 = DFS_INFO_9 - uint lastError = getFromClient + var lastError = getFromClient ? NativeMethods.NetDfsGetClientInfo(dfsName, serverName, shareName, 9, out safeBuffer) : NativeMethods.NetDfsGetInfo(dfsName, null, null, 9, out safeBuffer); diff --git a/AlphaFS/Network/Host Class/Host.cs b/AlphaFS/Network/Host Class/Host.cs index 4133b3ad7..88198a6e2 100644 --- a/AlphaFS/Network/Host Class/Host.cs +++ b/AlphaFS/Network/Host Class/Host.cs @@ -39,7 +39,7 @@ public static partial class Host { #region GetUncName - /// Return the host name in UNC format, for example: \\hostname. + /// Return the host name in UNC format, for example: \\hostname /// The unc name. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] [SecurityCritical] @@ -48,7 +48,7 @@ public static string GetUncName() return string.Format(CultureInfo.InvariantCulture, "{0}{1}", Path.UncPrefix, Environment.MachineName); } - /// Return the host name in UNC format, for example: \\hostname. + /// Return the host name in UNC format, for example: \\hostname /// Name of the computer. /// The unc name. [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Utils.IsNullOrWhiteSpace validates arguments.")] diff --git a/AlphaFS/Network/Host Class/ServerMessageBlock.cs b/AlphaFS/Network/Host Class/ServerMessageBlock.cs index 5ece5ed98..376400609 100644 --- a/AlphaFS/Network/Host Class/ServerMessageBlock.cs +++ b/AlphaFS/Network/Host Class/ServerMessageBlock.cs @@ -150,7 +150,7 @@ public static string[] GetHostShareFromPath(string uncPath) return new[] { uri.Host, - uri.AbsolutePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + uri.GetComponents(UriComponents.Path, UriFormat.Unescaped).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) }; } From de32cd7a7365e2b24531551c11b65415a6af72af Mon Sep 17 00:00:00 2001 From: Yomodo Date: Tue, 23 May 2017 13:57:02 +0200 Subject: [PATCH 03/15] Updated CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66260f006..af95e15cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ Version 2.1.3 (2017-XX-XX) ### Bugs Fixed - Issue #288: `Directory.Exists` on root drive problem has come back with recent updates (Thx warrenlbrown) +- Issue #297: Incorrect domain returned from `Host.EnumerateDomainDfsRoot` when specifying domain (Thx damiarnold) +- Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx damiarnold) Version 2.1.2 (2016-10-30) From 4b839d86f4d0bf5812ca6e94c727d69abcfd69e4 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Tue, 23 May 2017 14:00:35 +0200 Subject: [PATCH 04/15] Updated CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af95e15cc..f1277c2ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,9 @@ Version 2.1.3 (2017-XX-XX) ### Bugs Fixed - Issue #288: `Directory.Exists` on root drive problem has come back with recent updates (Thx warrenlbrown) +- Issue #289: `Alphaleonis.Win32.Network.Host.GetShareInfo` doesn't work since 2.1.0 - Issue #297: Incorrect domain returned from `Host.EnumerateDomainDfsRoot` when specifying domain (Thx damiarnold) -- Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx damiarnold) +- Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx Schoolmonkey/damiarnold) Version 2.1.2 (2016-10-30) From ecc27c0a071fcaf32baaadf297903dfc0a9bdc5f Mon Sep 17 00:00:00 2001 From: Yomodo Date: Tue, 23 May 2017 18:23:40 +0200 Subject: [PATCH 05/15] Added DirectoryInfo unit test for creating folder instance with >= 255 characters. --- .../DirectoryInfo_FolderName255Characters_.cs | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_FolderName255Characters_.cs diff --git a/AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_FolderName255Characters_.cs b/AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_FolderName255Characters_.cs new file mode 100644 index 000000000..81cceca84 --- /dev/null +++ b/AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_FolderName255Characters_.cs @@ -0,0 +1,143 @@ +/* Copyright (C) 2008-2016 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 directorys (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; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for DirectoryInfo and is intended to contain all DirectoryInfo UnitTests. + public partial class DirectoryInfoTest + { + // Pattern: ___ + + [TestMethod] + public void DirectoryInfo_CatchPathTooLongException_FolderNameGreaterThan255Characters_LocalAndNetwork_Success() + { + DirectoryInfo_CatchPathTooLongException_FolderNameGreaterThan255Characters(false); + DirectoryInfo_CatchPathTooLongException_FolderNameGreaterThan255Characters(true); + } + + + [TestMethod] + public void DirectoryInfo_FolderName255Characters_LocalAndNetwork_Success() + { + DirectoryInfo_FolderName255Characters(false); + DirectoryInfo_FolderName255Characters(true); + } + + + + + private void DirectoryInfo_CatchPathTooLongException_FolderNameGreaterThan255Characters(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var tempPath = System.IO.Path.GetTempPath(); + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, "DirectoryInfo_CatchPathTooLongException_FolderNameGreaterThan255Characters")) + { + var folder = rootDir.Directory.FullName; + + + Console.WriteLine("\nInput Directory Path: [{0}]\n", folder); + + + // System.IO: 244, anything higher throws System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters. + // AlphaFS : 255, anything higher throws System.IO.PathTooLongException. + var subFolder = new string('b', 256); + + + var local = Alphaleonis.Win32.Filesystem.Path.Combine(folder, subFolder); + var unc = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(local); + Console.WriteLine("SubFolder length: {0}, total path length: {1}", subFolder.Length, isNetwork ? unc.Length : local.Length); + Console.WriteLine(); + + + var gotException = false; + + try + { + // Fail. + var di1 = new Alphaleonis.Win32.Filesystem.DirectoryInfo(isNetwork ? unc : local); + } + catch (Exception ex) + { + var exName = ex.GetType().Name; + gotException = exName.Equals("PathTooLongException", StringComparison.OrdinalIgnoreCase); + Console.WriteLine("\tCaught Exception (Expected): [{0}] Message: [{1}]", exName, ex.Message); + } + + + Assert.IsTrue(gotException, "The exception is not caught, but is expected to."); + } + + Console.WriteLine(); + } + + + private void DirectoryInfo_FolderName255Characters(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var tempPath = System.IO.Path.GetTempPath(); + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, "DirectoryInfo_FolderName255Characters")) + { + var folder = rootDir.Directory.FullName; + + + Console.WriteLine("\nInput Directory Path: [{0}]\n", folder); + + + // System.IO: 244, anything higher throws System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters. + // AlphaFS : 255, anything higher throws System.IO.PathTooLongException. + var subFolder = new string('b', 255); + + + var local = Alphaleonis.Win32.Filesystem.Path.Combine(folder, subFolder); + var unc = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(local); + Console.WriteLine("SubFolder length: {0}, total path length: {1}", subFolder.Length, isNetwork ? unc.Length : local.Length); + + // Success. + var di1 = new Alphaleonis.Win32.Filesystem.DirectoryInfo(isNetwork ? unc : local); + + // Fail. + //var di1 = new System.IO.DirectoryInfo(local); + + + di1.Create(); + Assert.IsTrue(di1.Exists); + + + UnitTestConstants.Dump(di1, -17); + } + + Console.WriteLine(); + } + } +} From 5752b89fede27c26e99461565429edd55b7e9a5a Mon Sep 17 00:00:00 2001 From: Yomodo Date: Tue, 23 May 2017 18:59:37 +0200 Subject: [PATCH 06/15] Updated unit test project file. --- AlphaFS.UnitTest/AlphaFS.UnitTest.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj index c8f2bee59..121e4798e 100644 --- a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj +++ b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj @@ -165,6 +165,7 @@ + From 506a625ec45b0fb56114ab4fcd234c0a63cc35a3 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Tue, 23 May 2017 21:10:51 +0200 Subject: [PATCH 07/15] Fixes #296: Folder rename (casing) throws IOException with HResult 'ERROR_SAME_DRIVE' --- .../Directory Class/Directory_Move.cs | 191 +++++++++++++++--- .../Directory Class/Directory.CopyMove.cs | 3 +- CHANGELOG.md | 1 + 3 files changed, 164 insertions(+), 31 deletions(-) diff --git a/AlphaFS.UnitTest/Directory Class/Directory_Move.cs b/AlphaFS.UnitTest/Directory Class/Directory_Move.cs index a0fc8b7db..d03b46f24 100644 --- a/AlphaFS.UnitTest/Directory Class/Directory_Move.cs +++ b/AlphaFS.UnitTest/Directory Class/Directory_Move.cs @@ -28,6 +28,22 @@ partial class DirectoryTest { // Pattern: ___ + [TestMethod] + public void Directory_Move_Rename_LocalAndNetwork_Success() + { + Directory_Move_Rename(false); + Directory_Move_Rename(true); + } + + + [TestMethod] + public void Directory_Move_Rename_DifferentCasing_LocalAndNetwork_Success() + { + Directory_Move_Rename_DifferentCasing(false); + Directory_Move_Rename_DifferentCasing(true); + } + + [TestMethod] public void Directory_Move_SameVolume_LocalAndNetwork_Success() { @@ -95,7 +111,15 @@ public void Directory_Move_CatchDirectoryNotFoundException_NonExistingDirectory_ Directory_Move_CatchDirectoryNotFoundException_NonExistingDirectory(false); Directory_Move_CatchDirectoryNotFoundException_NonExistingDirectory(true); } - + + + [TestMethod] + public void Directory_Move_CatchIOException_SameSourceAndDestination_LocalAndNetwork_Success() + { + Directory_Move_CatchIOException_SameSourceAndDestination(false); + Directory_Move_CatchIOException_SameSourceAndDestination(true); + } + [TestMethod] public void Directory_Move_CatchNotSupportedException_PathContainsColon_LocalAndNetwork_Success() @@ -112,9 +136,9 @@ public void Directory_Move_CatchUnauthorizedAccessException_UserExplicitDeny_Loc Directory_Move_CatchUnauthorizedAccessException_UserExplicitDeny(true); } + - - + private void Directory_Move(bool isNetwork) { UnitTestConstants.PrintUnitTestHeader(isNetwork); @@ -352,34 +376,7 @@ private void Directory_Move_CatchArgumentException_PathStartsWithColon(bool isNe Console.WriteLine(); } - - private void Directory_Move_CatchNotSupportedException_PathContainsColon(bool isNetwork) - { - UnitTestConstants.PrintUnitTestHeader(isNetwork); - - var colonText = @"\My:FilePath"; - var folderSrc = (isNetwork ? Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.LocalHostShare) : UnitTestConstants.SysDrive + @"\dev\test") + colonText; - - Console.WriteLine("\nSrc Directory Path: [{0}]", folderSrc); - - - var gotException = false; - try - { - Alphaleonis.Win32.Filesystem.Directory.Move(folderSrc, string.Empty); - } - catch (Exception ex) - { - var exName = ex.GetType().Name; - gotException = exName.Equals("NotSupportedException", StringComparison.OrdinalIgnoreCase); - Console.WriteLine("\n\tCaught Exception: [{0}] Message: [{1}]", exName, ex.Message); - } - Assert.IsTrue(gotException, "The exception is not caught, but is expected to."); - - Console.WriteLine(); - } - private void Directory_Move_CatchDirectoryNotFoundException_NonExistingDriveLetter(bool isNetwork) { @@ -457,6 +454,70 @@ private void Directory_Move_CatchDirectoryNotFoundException_NonExistingDirectory } + private void Directory_Move_CatchIOException_SameSourceAndDestination(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var tempPath = System.IO.Path.GetTempPath(); + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, "Directory_Move_CatchIOException_SameSourceAndDestination")) + { + var folderSrc = System.IO.Directory.CreateDirectory(System.IO.Path.Combine(rootDir.Directory.FullName, "Source-") + System.IO.Path.GetRandomFileName()); + var folderDst = folderSrc; + + + Console.WriteLine("\nSrc Directory Path: [{0}]", folderSrc.FullName); + Console.WriteLine("Dst Directory Path: [{0}]", folderDst.FullName); + + + var gotException = false; + try + { + System.IO.Directory.Move(folderSrc.FullName, folderDst.FullName); + } + catch (Exception ex) + { + var exName = ex.GetType().Name; + gotException = exName.Equals("IOException", StringComparison.OrdinalIgnoreCase); + Console.WriteLine("\n\tCaught Exception: [{0}] Message: [{1}]", exName, ex.Message); + } + Assert.IsTrue(gotException, "The exception is not caught, but is expected to."); + } + + Console.WriteLine(); + } + + + private void Directory_Move_CatchNotSupportedException_PathContainsColon(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var colonText = @"\My:FilePath"; + var folderSrc = (isNetwork ? Alphaleonis.Win32.Filesystem.Path.LocalToUnc(UnitTestConstants.LocalHostShare) : UnitTestConstants.SysDrive + @"\dev\test") + colonText; + + Console.WriteLine("\nSrc Directory Path: [{0}]", folderSrc); + + + var gotException = false; + try + { + Alphaleonis.Win32.Filesystem.Directory.Move(folderSrc, string.Empty); + } + catch (Exception ex) + { + var exName = ex.GetType().Name; + gotException = exName.Equals("NotSupportedException", StringComparison.OrdinalIgnoreCase); + Console.WriteLine("\n\tCaught Exception: [{0}] Message: [{1}]", exName, ex.Message); + } + Assert.IsTrue(gotException, "The exception is not caught, but is expected to."); + + Console.WriteLine(); + } + + private void Directory_Move_CatchUnauthorizedAccessException_UserExplicitDeny(bool isNetwork) { UnitTestConstants.PrintUnitTestHeader(isNetwork); @@ -516,5 +577,75 @@ private void Directory_Move_CatchUnauthorizedAccessException_UserExplicitDeny(bo Console.WriteLine(); } + + + private void Directory_Move_Rename(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var tempPath = System.IO.Path.GetTempPath(); + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, "Directory.Move (Rename)")) + { + var folderSrc = System.IO.Directory.CreateDirectory(System.IO.Path.Combine(rootDir.Directory.FullName, "Source-") + System.IO.Path.GetRandomFileName()); + var folderDst = new System.IO.DirectoryInfo(System.IO.Path.Combine(rootDir.Directory.FullName, "Destination-") + System.IO.Path.GetRandomFileName()); + + Console.WriteLine("\nInput Directory Path: [{0}]", folderSrc.FullName); + Console.WriteLine("\nRename folder: [{0}] to: [{1}]", folderSrc.Name, folderDst.Name); + + + Assert.IsTrue(folderSrc.Exists, "The source folder does not exist which was not expected."); + Assert.IsFalse(folderDst.Exists, "The destination folder exists which was not expected."); + + Alphaleonis.Win32.Filesystem.Directory.Move(folderSrc.FullName, folderDst.FullName); + + folderSrc.Refresh(); + folderDst.Refresh(); + + Assert.IsFalse(folderSrc.Exists, "The source folder exists which was not expected."); + Assert.IsTrue(folderDst.Exists, "The destination folder does not exists which was not expected."); + } + + Console.WriteLine(); + } + + + private void Directory_Move_Rename_DifferentCasing(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var tempPath = System.IO.Path.GetTempPath(); + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, "Directory.Move (Rename_DifferentCasing)")) + { + var folderSrc = System.IO.Directory.CreateDirectory((System.IO.Path.Combine(rootDir.Directory.FullName, "Source-") + System.IO.Path.GetRandomFileName()).ToLowerInvariant()); + var folderDst = folderSrc; + var newFolderName = System.IO.Path.GetFileName(folderDst.FullName.ToUpperInvariant()); + + + Console.WriteLine("\nInput Directory Path: [{0}]", folderSrc.FullName); + Console.WriteLine("\nRename folder: [{0}] to: [{1}]", folderSrc.Name, newFolderName); + + + Assert.IsTrue(folderSrc.Exists, "The source folder does not exist which was not expected."); + Assert.IsTrue(folderDst.Exists, "The destination folder exists which was not expected."); + + Alphaleonis.Win32.Filesystem.Directory.Move(folderSrc.FullName, folderDst.FullName.ToUpperInvariant()); + + folderSrc.Refresh(); + folderDst.Refresh(); + + Assert.IsTrue(folderSrc.Exists, "The source folder exists which was not expected."); + Assert.IsTrue(folderDst.Exists, "The destination folder does not exists which was not expected."); + } + + Console.WriteLine(); + } } } diff --git a/AlphaFS/Filesystem/Directory Class/Directory.CopyMove.cs b/AlphaFS/Filesystem/Directory Class/Directory.CopyMove.cs index 19d6cbcfe..357af4ec5 100644 --- a/AlphaFS/Filesystem/Directory Class/Directory.CopyMove.cs +++ b/AlphaFS/Filesystem/Directory Class/Directory.CopyMove.cs @@ -767,7 +767,8 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin var destinationPathLp = Path.GetExtendedLengthPathCore(transaction, destinationPath, pathFormat, fullPathOptions); // MSDN: .NET3.5+: IOException: The sourceDirName and destDirName parameters refer to the same file or directory. - if (sourcePathLp.Equals(destinationPathLp, StringComparison.OrdinalIgnoreCase)) + // Do not use StringComparison.OrdinalIgnoreCase to allow renaming a folder with different casing. + if (sourcePathLp.Equals(destinationPathLp)) NativeError.ThrowException(Win32Errors.ERROR_SAME_DRIVE, destinationPathLp); diff --git a/CHANGELOG.md b/CHANGELOG.md index f1277c2ca..df93393c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Version 2.1.3 (2017-XX-XX) - Issue #288: `Directory.Exists` on root drive problem has come back with recent updates (Thx warrenlbrown) - Issue #289: `Alphaleonis.Win32.Network.Host.GetShareInfo` doesn't work since 2.1.0 +- Issue #296: Folder rename (casing) throws IOException with HResult `ERROR_SAME_DRIVE` - Issue #297: Incorrect domain returned from `Host.EnumerateDomainDfsRoot` when specifying domain (Thx damiarnold) - Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx Schoolmonkey/damiarnold) From a6e004cb5611f10f62ee1da23aef115c6756a34b Mon Sep 17 00:00:00 2001 From: Yomodo Date: Wed, 24 May 2017 00:13:36 +0200 Subject: [PATCH 08/15] Fixes #312: Volume.EnumerateVolumes skips first volume --- AlphaFS.UnitTest/Volume Class/VolumeTest.cs | 7 ++-- AlphaFS/Filesystem/Volume.cs | 45 ++++++++++++++++----- CHANGELOG.md | 5 ++- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/AlphaFS.UnitTest/Volume Class/VolumeTest.cs b/AlphaFS.UnitTest/Volume Class/VolumeTest.cs index 7ab41bf72..a8b473a54 100644 --- a/AlphaFS.UnitTest/Volume Class/VolumeTest.cs +++ b/AlphaFS.UnitTest/Volume Class/VolumeTest.cs @@ -545,7 +545,7 @@ public void AlphaFS_Volume_EnumerateVolumes() { Console.WriteLine("Volume.EnumerateVolumes()"); - Console.WriteLine("\nShould give the same enumeration as \"mountvol.exe\"\n"); + Console.WriteLine("\nShould give the same (or more) enumeration as \"mountvol.exe\"\n"); var cnt = 0; UnitTestConstants.StopWatcher(true); @@ -565,12 +565,13 @@ public void AlphaFS_Volume_EnumerateVolumes() Console.WriteLine("\t\tGetVolumeLabel() : [{0}]", Volume.GetVolumeLabel(volume)); Console.WriteLine("\t\tGetVolumeDisplayName() : [{0}]", Volume.GetVolumeDisplayName(volume)); - foreach (var displayName in Volume.EnumerateVolumePathNames(volume)) { - Console.WriteLine("\t\tEnumerateVolumePathNames(): [{0}]\n", displayName); + Console.WriteLine("\t\tEnumerateVolumePathNames(): [{0}]", displayName); Assert.IsTrue(!string.IsNullOrWhiteSpace(displayName)); } + + Console.WriteLine(); } Console.WriteLine("\t{0}\n", UnitTestConstants.Reporter(true)); diff --git a/AlphaFS/Filesystem/Volume.cs b/AlphaFS/Filesystem/Volume.cs index 5b23d515c..cf66d483e 100644 --- a/AlphaFS/Filesystem/Volume.cs +++ b/AlphaFS/Filesystem/Volume.cs @@ -570,24 +570,51 @@ public static IEnumerable EnumerateVolumes() var buffer = new StringBuilder(NativeMethods.MaxPathUnicode); using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors)) - using (var handle = NativeMethods.FindFirstVolume(buffer, (uint)buffer.Capacity)) + using (var handle = NativeMethods.FindFirstVolume(buffer, (uint) buffer.Capacity)) { - while (handle != null && !handle.IsInvalid) + var lastError = Marshal.GetLastWin32Error(); + + if (handle.IsInvalid) { - if (NativeMethods.FindNextVolume(handle, buffer, (uint)buffer.Capacity)) - yield return buffer.ToString(); + handle.Close(); - else + switch ((uint) lastError) { - var lastError = Marshal.GetLastWin32Error(); + case Win32Errors.ERROR_NO_MORE_FILES: + case Win32Errors.ERROR_PATH_NOT_FOUND: // Observed with USB stick, FAT32 formatted. + yield break; + + default: + NativeError.ThrowException(lastError); + break; + } + } + + yield return buffer.ToString(); + + while (NativeMethods.FindNextVolume(handle, buffer, (uint) buffer.Capacity)) + { + lastError = Marshal.GetLastWin32Error(); + + if (handle.IsInvalid) + { handle.Close(); - if (lastError == Win32Errors.ERROR_NO_MORE_FILES) - yield break; + switch ((uint) lastError) + { + case Win32Errors.ERROR_NO_MORE_FILES: + case Win32Errors.ERROR_PATH_NOT_FOUND: // Observed with USB stick, FAT32 formatted. + case Win32Errors.ERROR_MORE_DATA: + yield break; - NativeError.ThrowException(lastError); + default: + NativeError.ThrowException(lastError); + break; + } } + + yield return buffer.ToString(); } } } diff --git a/CHANGELOG.md b/CHANGELOG.md index df93393c2..cae19e28a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,10 @@ Version 2.1.3 (2017-XX-XX) ### Bugs Fixed - Issue #288: `Directory.Exists` on root drive problem has come back with recent updates (Thx warrenlbrown) -- Issue #289: `Alphaleonis.Win32.Network.Host.GetShareInfo` doesn't work since 2.1.0 -- Issue #296: Folder rename (casing) throws IOException with HResult `ERROR_SAME_DRIVE` +- Issue #289: `Alphaleonis.Win32.Network.Host.GetShareInfo` doesn't work since 2.1.0 (Thx Schoolmonkey) +- Issue #296: Folder rename (casing) throws IOException with HResult `ERROR_SAME_DRIVE` (Thx doormalena) - Issue #297: Incorrect domain returned from `Host.EnumerateDomainDfsRoot` when specifying domain (Thx damiarnold) +- Issue #312: `Volume.EnumerateVolumes` skips first volume (Thx springy76) - Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx Schoolmonkey/damiarnold) From 85b02deb253fe1fe1970a9fe5057115e49c7eb99 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Wed, 24 May 2017 00:16:49 +0200 Subject: [PATCH 09/15] Removed traling underscore from file name. --- AlphaFS.UnitTest/AlphaFS.UnitTest.csproj | 2 +- ...5Characters_.cs => DirectoryInfo_FolderName255Characters.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename AlphaFS.UnitTest/DirectoryInfo Class/{DirectoryInfo_FolderName255Characters_.cs => DirectoryInfo_FolderName255Characters.cs} (100%) diff --git a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj index 121e4798e..2da4ac0dc 100644 --- a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj +++ b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj @@ -165,7 +165,7 @@ - + diff --git a/AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_FolderName255Characters_.cs b/AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_FolderName255Characters.cs similarity index 100% rename from AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_FolderName255Characters_.cs rename to AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_FolderName255Characters.cs From 5868a286acca1a7a48e4ce89f670dfbdfb84f41a Mon Sep 17 00:00:00 2001 From: Yomodo Date: Wed, 24 May 2017 02:14:38 +0200 Subject: [PATCH 10/15] Fixes #320: Minor changes in comments in `Win32Errors.cs` to eliminate compiler warnings. --- .../NativeMethods.VolumeManagement.cs | 24 +++++++-------- AlphaFS/Win32Errors.cs | 30 +++++++++---------- CHANGELOG.md | 5 ++-- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/AlphaFS/Filesystem/Native Methods/NativeMethods.VolumeManagement.cs b/AlphaFS/Filesystem/Native Methods/NativeMethods.VolumeManagement.cs index c2576df10..71c170d1b 100644 --- a/AlphaFS/Filesystem/Native Methods/NativeMethods.VolumeManagement.cs +++ b/AlphaFS/Filesystem/Native Methods/NativeMethods.VolumeManagement.cs @@ -53,7 +53,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "DeleteVolumeMountPointW"), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool DeleteVolumeMountPoint([MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeMountPoint); + internal static extern bool DeleteVolumeMountPoint([MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeMountPoint); /// Retrieves the name of a volume on a computer. FindFirstVolume is used to begin scanning the volumes of a computer. /// @@ -64,7 +64,7 @@ internal static partial class NativeMethods /// Minimum supported server: Windows Server 2003 [desktop apps only] [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "FindFirstVolumeW"), SuppressUnmanagedCodeSecurity] - internal extern static SafeFindVolumeHandle FindFirstVolume(StringBuilder lpszVolumeName, [MarshalAs(UnmanagedType.U4)] uint cchBufferLength); + internal static extern SafeFindVolumeHandle FindFirstVolume(StringBuilder lpszVolumeName, [MarshalAs(UnmanagedType.U4)] uint cchBufferLength); /// Retrieves the name of a mounted folder on the specified volume. FindFirstVolumeMountPoint is used to begin scanning the mounted folders on a volume. /// @@ -75,7 +75,7 @@ internal static partial class NativeMethods /// Minimum supported server: Windows Server 2003 [desktop apps only] [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "FindFirstVolumeMountPointW"), SuppressUnmanagedCodeSecurity] - internal extern static SafeFindVolumeMountPointHandle FindFirstVolumeMountPoint([MarshalAs(UnmanagedType.LPWStr)] string lpszRootPathName, StringBuilder lpszVolumeMountPoint, [MarshalAs(UnmanagedType.U4)] uint cchBufferLength); + internal static extern SafeFindVolumeMountPointHandle FindFirstVolumeMountPoint([MarshalAs(UnmanagedType.LPWStr)] string lpszRootPathName, StringBuilder lpszVolumeMountPoint, [MarshalAs(UnmanagedType.U4)] uint cchBufferLength); /// Continues a volume search started by a call to the FindFirstVolume function. FindNextVolume finds one volume per call. /// @@ -87,7 +87,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "FindNextVolumeW"), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool FindNextVolume(SafeFindVolumeHandle hFindVolume, StringBuilder lpszVolumeName, [MarshalAs(UnmanagedType.U4)] uint cchBufferLength); + internal static extern bool FindNextVolume(SafeFindVolumeHandle hFindVolume, StringBuilder lpszVolumeName, [MarshalAs(UnmanagedType.U4)] uint cchBufferLength); /// Continues a mounted folder search started by a call to the FindFirstVolumeMountPoint function. FindNextVolumeMountPoint finds one mounted folder per call. /// @@ -100,7 +100,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "FindNextVolumeMountPointW"), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool FindNextVolumeMountPoint(SafeFindVolumeMountPointHandle hFindVolume, StringBuilder lpszVolumeName, [MarshalAs(UnmanagedType.U4)] uint cchBufferLength); + internal static extern bool FindNextVolumeMountPoint(SafeFindVolumeMountPointHandle hFindVolume, StringBuilder lpszVolumeName, [MarshalAs(UnmanagedType.U4)] uint cchBufferLength); /// Closes the specified volume search handle. /// @@ -114,7 +114,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = false, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool FindVolumeClose(IntPtr hFindVolume); + internal static extern bool FindVolumeClose(IntPtr hFindVolume); /// Closes the specified mounted folder search handle. /// @@ -129,7 +129,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = false, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool FindVolumeMountPointClose(IntPtr hFindVolume); + internal static extern bool FindVolumeMountPointClose(IntPtr hFindVolume); /// /// Determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network drive. @@ -149,7 +149,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "GetDriveTypeW"), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.U4)] - internal extern static DriveType GetDriveType([MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName); + internal static extern DriveType GetDriveType([MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName); /// /// Retrieves a bitmask representing the currently available disk drives. @@ -180,7 +180,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "GetVolumeInformationW"), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool GetVolumeInformation([MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, StringBuilder lpVolumeNameBuffer, [MarshalAs(UnmanagedType.U4)] uint nVolumeNameSize, [MarshalAs(UnmanagedType.U4)] out uint lpVolumeSerialNumber, [MarshalAs(UnmanagedType.U4)] out int lpMaximumComponentLength, [MarshalAs(UnmanagedType.U4)] out VolumeInfoAttributes lpFileSystemAttributes, StringBuilder lpFileSystemNameBuffer, [MarshalAs(UnmanagedType.U4)] uint nFileSystemNameSize); + internal static extern bool GetVolumeInformation([MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, StringBuilder lpVolumeNameBuffer, [MarshalAs(UnmanagedType.U4)] uint nVolumeNameSize, [MarshalAs(UnmanagedType.U4)] out uint lpVolumeSerialNumber, [MarshalAs(UnmanagedType.U4)] out int lpMaximumComponentLength, [MarshalAs(UnmanagedType.U4)] out VolumeInfoAttributes lpFileSystemAttributes, StringBuilder lpFileSystemNameBuffer, [MarshalAs(UnmanagedType.U4)] uint nFileSystemNameSize); /// Retrieves information about the file system and volume associated with the specified file. /// @@ -194,7 +194,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "GetVolumeInformationByHandleW"), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool GetVolumeInformationByHandle(SafeFileHandle hFile, StringBuilder lpVolumeNameBuffer, [MarshalAs(UnmanagedType.U4)] uint nVolumeNameSize, [MarshalAs(UnmanagedType.U4)] out uint lpVolumeSerialNumber, [MarshalAs(UnmanagedType.U4)] out int lpMaximumComponentLength, out VolumeInfoAttributes lpFileSystemAttributes, StringBuilder lpFileSystemNameBuffer, [MarshalAs(UnmanagedType.U4)] uint nFileSystemNameSize); + internal static extern bool GetVolumeInformationByHandle(SafeFileHandle hFile, StringBuilder lpVolumeNameBuffer, [MarshalAs(UnmanagedType.U4)] uint nVolumeNameSize, [MarshalAs(UnmanagedType.U4)] out uint lpVolumeSerialNumber, [MarshalAs(UnmanagedType.U4)] out int lpMaximumComponentLength, out VolumeInfoAttributes lpFileSystemAttributes, StringBuilder lpFileSystemNameBuffer, [MarshalAs(UnmanagedType.U4)] uint nFileSystemNameSize); /// Retrieves a volume GUID path for the volume that is associated with the specified volume mount point (drive letter, volume GUID path, or mounted folder). /// @@ -255,7 +255,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetVolumeLabelW"), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool SetVolumeLabel([MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, [MarshalAs(UnmanagedType.LPWStr)] string lpVolumeName); + internal static extern bool SetVolumeLabel([MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, [MarshalAs(UnmanagedType.LPWStr)] string lpVolumeName); /// Associates a volume with a drive letter or a directory on another volume. /// Minimum supported client: Windows XP [desktop apps only]. @@ -267,7 +267,7 @@ internal static partial class NativeMethods [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetVolumeMountPointW"), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] - internal extern static bool SetVolumeMountPoint([MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeMountPoint, [MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeName); + internal static extern bool SetVolumeMountPoint([MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeMountPoint, [MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeName); /// Retrieves information about MS-DOS device names. /// Minimum supported client: Windows XP [desktop apps only]. diff --git a/AlphaFS/Win32Errors.cs b/AlphaFS/Win32Errors.cs index ef0d89e8a..06885f5c1 100644 --- a/AlphaFS/Win32Errors.cs +++ b/AlphaFS/Win32Errors.cs @@ -222,8 +222,8 @@ public static int GetHrFromWin32Error(uint errorCode) //public const uint ERROR_SIGNAL_REFUSED = 156; //public const uint ERROR_DISCARDED = 157; - /// (158) The segment is already unlocked. - public const uint ERROR_NOT_LOCKED = 158; + //// (158) The segment is already unlocked. + //public const uint ERROR_NOT_LOCKED = 158; //public const uint ERROR_BAD_THREADID_ADDR = 159; //public const uint ERROR_BAD_ARGUMENTS = 160; @@ -259,7 +259,7 @@ public static int GetHrFromWin32Error(uint errorCode) //public const uint ERROR_INFLOOP_IN_RELOC_CHAIN = 202; /// (203) The system could not find the environment option that was entered. - public const uint ERROR_ENVVAR_NOT_FOUND = 203; + //public const uint ERROR_ENVVAR_NOT_FOUND = 203; //public const uint ERROR_NO_SIGNAL_SENT = 205; //public const uint ERROR_FILENAME_EXCED_RANGE = 206; @@ -319,8 +319,8 @@ public static int GetHrFromWin32Error(uint errorCode) //public const uint ERROR_IO_INCOMPLETE = 996; - /// (997) Overlapped I/O operation is in progress. - public const uint ERROR_IO_PENDING = 997; + //// (997) Overlapped I/O operation is in progress. + //public const uint ERROR_IO_PENDING = 997; //public const uint ERROR_NOACCESS = 998; //public const uint ERROR_SWAPERROR = 999; @@ -448,8 +448,8 @@ public static int GetHrFromWin32Error(uint errorCode) //public const uint ERROR_POTENTIAL_FILE_FOUND = 1180; //public const uint ERROR_JOURNAL_ENTRY_DELETED = 1181; - /// (1200) The specified device name is invalid. - public const uint ERROR_BAD_DEVICE = 1200; + //// (1200) The specified device name is invalid. + //public const uint ERROR_BAD_DEVICE = 1200; //public const uint ERROR_CONNECTION_UNAVAIL = 1201; //public const uint ERROR_DEVICE_ALREADY_REMEMBERED = 1202; @@ -459,8 +459,8 @@ public static int GetHrFromWin32Error(uint errorCode) //public const uint ERROR_BAD_PROFILE = 1206; //public const uint ERROR_NOT_CONTAINER = 1207; - /// (1208) An extended error has occurred. - public const uint ERROR_EXTENDED_ERROR = 1208; + //// (1208) An extended error has occurred. + //public const uint ERROR_EXTENDED_ERROR = 1208; //public const uint ERROR_INVALID_GROUPNAME = 1209; //public const uint ERROR_INVALID_COMPUTERNAME = 1210; @@ -476,8 +476,8 @@ public static int GetHrFromWin32Error(uint errorCode) //public const uint ERROR_REMOTE_SESSION_LIMIT_EXCEEDED = 1220; //public const uint ERROR_DUP_DOMAINNAME = 1221; - /// (1222) The network is not present or not started. - public const uint ERROR_NO_NETWORK = 1222; + //// (1222) The network is not present or not started. + //public const uint ERROR_NO_NETWORK = 1222; //public const uint ERROR_CANCELLED = 1223; //public const uint ERROR_USER_MAPPED_FILE = 1224; @@ -3558,8 +3558,8 @@ public static int GetHrFromWin32Error(uint errorCode) ///// This replicant database is outdated; synchronization is required. //public const uint NERR_SyncRequired = 2249; - /// (2250) The network connection could not be found. - public const uint NERR_UseNotFound = 2250; + //// (2250) The network connection could not be found. + //public const uint NERR_UseNotFound = 2250; ///// This asg_type is invalid. //public const uint NERR_BadAsgType = 2251; @@ -3645,8 +3645,8 @@ public static int GetHrFromWin32Error(uint errorCode) ///// This operation is not supported on computers with multiple networks. //public const uint NERR_MultipleNets = 2300; - /// (2310) This shared resource does not exist. - public const uint NERR_NetNameNotFound = 2310; + //// (2310) This shared resource does not exist. + //public const uint NERR_NetNameNotFound = 2310; ///// This device is not shared. //public const uint NERR_DeviceNotShared = 2311; diff --git a/CHANGELOG.md b/CHANGELOG.md index cae19e28a..eec50b205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,12 @@ Version 2.1.3 (2017-XX-XX) ### Bugs Fixed - Issue #288: `Directory.Exists` on root drive problem has come back with recent updates (Thx warrenlbrown) -- Issue #289: `Alphaleonis.Win32.Network.Host.GetShareInfo` doesn't work since 2.1.0 (Thx Schoolmonkey) +- Issue #289: `Alphaleonis.Win32.Network.Host.GetShareInfo` doesn't work since 2.1.0 (Thx Schoolmonkey/damiarnold) - Issue #296: Folder rename (casing) throws IOException with HResult `ERROR_SAME_DRIVE` (Thx doormalena) - Issue #297: Incorrect domain returned from `Host.EnumerateDomainDfsRoot` when specifying domain (Thx damiarnold) - Issue #312: `Volume.EnumerateVolumes` skips first volume (Thx springy76) -- Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx Schoolmonkey/damiarnold) +- Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx damiarnold) +- Issue #320: Minor changes in comments in `Win32Errors.cs` to eliminate compiler warnings. (Thx besoft) Version 2.1.2 (2016-10-30) From 6e991db90adc64971978d34dedaff0725ed0a8d3 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Wed, 24 May 2017 10:04:54 +0200 Subject: [PATCH 11/15] Added suppression of CA1809:AvoidExcessiveLocals warning for Path.NormalizePath. --- AlphaFS/Filesystem/Path Class/Path.ValidationAndChecks.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/AlphaFS/Filesystem/Path Class/Path.ValidationAndChecks.cs b/AlphaFS/Filesystem/Path Class/Path.ValidationAndChecks.cs index 2d426e9cb..4e8d226a8 100644 --- a/AlphaFS/Filesystem/Path Class/Path.ValidationAndChecks.cs +++ b/AlphaFS/Filesystem/Path Class/Path.ValidationAndChecks.cs @@ -290,6 +290,7 @@ internal static bool IsDVsc(char c, bool? checkSeparatorChar) [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + [SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals")] private static string NormalizePath(string path, GetFullPathOptions options) { var newBuffer = new StringBuilder(NativeMethods.MaxPathUnicode); From 4c9a163a53180297788d2d530656c2a9e7745aa0 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Wed, 24 May 2017 12:37:57 +0200 Subject: [PATCH 12/15] Small unit test TemporaryDirectory class improvement. --- AlphaFS.UnitTest/Utils/TemporaryDirectory.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/AlphaFS.UnitTest/Utils/TemporaryDirectory.cs b/AlphaFS.UnitTest/Utils/TemporaryDirectory.cs index fa3cabe2e..3c4346b6d 100644 --- a/AlphaFS.UnitTest/Utils/TemporaryDirectory.cs +++ b/AlphaFS.UnitTest/Utils/TemporaryDirectory.cs @@ -69,11 +69,22 @@ private void Dispose(bool isDisposing) try { if (isDisposing) - Alphaleonis.Win32.Filesystem.Directory.Delete(Directory.FullName, true, Alphaleonis.Win32.Filesystem.PathFormat.FullPath); + System.IO.Directory.Delete(Directory.FullName, true); } catch (Exception ex) { - Console.WriteLine("\n\nFailed to delete TemporaryDirectory: [{0}]: [{1}]", Directory.FullName, ex.Message); + Console.WriteLine("\n\nSystem.IO failed to delete TemporaryDirectory: [{0}]\nError: [{1}]", Directory.FullName, ex.Message); + Console.Write("Retry using AlphaFS... "); + + try + { + Alphaleonis.Win32.Filesystem.Directory.Delete(Directory.FullName, true, Alphaleonis.Win32.Filesystem.PathFormat.FullPath); + Console.WriteLine("Success."); + } + catch (Exception ex2) + { + Console.WriteLine("Failed to delete TemporaryDirectory.\nError: [{0}]", ex2.Message); + } } } From 54f070f7a5f6ffb4885067ba0398ae3f37b3d157 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Wed, 24 May 2017 20:06:27 +0200 Subject: [PATCH 13/15] Fixes #299: FileInfo.MoveTo and DirectoryInfo.MoveTo throw ArgumentNullException on empty destination path; Fixes #321: DirectoryInfo.CopyToMoveToCore() calls Path.GetExtendedLengthPathCore() without Transaction parameter; --- AlphaFS.UnitTest/AlphaFS.UnitTest.csproj | 1 + .../DirectoryInfo_MoveTo.cs | 102 +++++++++++++++++ .../Directory Class/Directory.CopyMove.cs | 60 ++++++---- .../DirectoryInfo.CopyToMoveTo.cs | 33 +++--- .../Filesystem/File Class/File.CopyMove.cs | 105 +++++++++++------- .../FileInfo Class/FileInfo.CopyToMoveTo.cs | 49 ++++---- .../Path Class/Path.ShortLongConversions.cs | 1 + CHANGELOG.md | 2 + 8 files changed, 255 insertions(+), 98 deletions(-) create mode 100644 AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_MoveTo.cs diff --git a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj index 2da4ac0dc..b1758155c 100644 --- a/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj +++ b/AlphaFS.UnitTest/AlphaFS.UnitTest.csproj @@ -166,6 +166,7 @@ + diff --git a/AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_MoveTo.cs b/AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_MoveTo.cs new file mode 100644 index 000000000..c5e327e21 --- /dev/null +++ b/AlphaFS.UnitTest/DirectoryInfo Class/DirectoryInfo_MoveTo.cs @@ -0,0 +1,102 @@ +/* Copyright (C) 2008-2016 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 directorys (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 Microsoft.Win32; + +namespace AlphaFS.UnitTest +{ + /// This is a test class for FileInfo and is intended to contain all FileInfo UnitTests. + public partial class DirectoryInfoTest + { + // Pattern: ___ + + [TestMethod] + public void DirectoryInfo_MoveTo_DelayUntilReboot_LocalAndNetwork_Success() + { + DirectoryInfo_MoveTo_DelayUntilReboot(false); + DirectoryInfo_MoveTo_DelayUntilReboot(true); + } + + + + + private void DirectoryInfo_MoveTo_DelayUntilReboot(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var tempPath = System.IO.Path.GetTempPath(); + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, "DirectoryInfo.MoveTo_DelayUntilReboot")) + { + var folder = rootDir.Directory.FullName; + var folderSrc = Alphaleonis.Win32.Filesystem.Directory.CreateDirectory(System.IO.Path.Combine(folder, "Source-" + System.IO.Path.GetRandomFileName())); + var pendingEntry = folderSrc.FullName; + + Console.WriteLine("\nSrc Directory Path: [{0}]", pendingEntry); + + UnitTestConstants.CreateDirectoriesAndFiles(pendingEntry, new Random().Next(5, 15), true); + + + // Trigger DelayUntilReboot. + folderSrc.MoveTo(null, Alphaleonis.Win32.Filesystem.MoveOptions.DelayUntilReboot); + + + // Verify DelayUntilReboot in registry. + var pendingList = (string[]) Registry.GetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager", "PendingFileRenameOperations", null); + + + Assert.IsNotNull(pendingList, "The PendingFileRenameOperations is null, which is not expected."); + + + var found = false; + + foreach (var line in pendingList) + { + var entry = isNetwork ? pendingEntry.TrimStart('\\') : pendingEntry; + + var prefix = @"\??\" + (isNetwork ? @"UNC\" : string.Empty); + + + found = !Alphaleonis.Utils.IsNullOrWhiteSpace(line) && line.Replace(entry, string.Empty).Equals(prefix, StringComparison.OrdinalIgnoreCase); + if (found) + { + Console.WriteLine("\n\tPending entry found in registry: [{0}]", line); + + // TODO: Remove unit test entry from registry. + + + break; + } + } + + + Assert.IsTrue(found, "No pending entry found in registry, which is not expected."); + } + + Console.WriteLine(); + } + } +} diff --git a/AlphaFS/Filesystem/Directory Class/Directory.CopyMove.cs b/AlphaFS/Filesystem/Directory Class/Directory.CopyMove.cs index 357af4ec5..e17ae6ad6 100644 --- a/AlphaFS/Filesystem/Directory Class/Directory.CopyMove.cs +++ b/AlphaFS/Filesystem/Directory Class/Directory.CopyMove.cs @@ -207,6 +207,7 @@ public static void CopyTransacted(KernelTransaction transaction, string sourcePa #endregion // Copy + #region Copy (CopyOptions) /// [AlphaFS] Copies a directory and its contents to a new location, can be specified. @@ -415,6 +416,7 @@ public static CopyMoveResult CopyTransacted(KernelTransaction transaction, strin #endregion // Copy (CopyOptions) + #region Move #region .NET @@ -511,6 +513,7 @@ public static void MoveTransacted(KernelTransaction transaction, string sourcePa #endregion // Move + #region Move (MoveOptions) /// [AlphaFS] Moves a file or a directory and its contents to a new location, can be specified. @@ -719,6 +722,7 @@ public static CopyMoveResult MoveTransacted(KernelTransaction transaction, strin #endregion // Move (MoveOptions) + #region Internal Methods /// Copy/move a Non-/Transacted file or directory including its children to a new location, @@ -729,7 +733,7 @@ public static CopyMoveResult MoveTransacted(KernelTransaction transaction, strin /// /// Option is recommended for very large file transfers. /// You cannot use the Move method to overwrite an existing file, unless contains . - /// This Move method works across disk volumes, and it does not throw an exception if the source and destination are the same. + /// This Move method works across disk volumes. /// Note that if you attempt to replace a file by moving a file of the same name into that directory, you get an IOException. /// /// @@ -752,10 +756,24 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin { #region Setup + // Determine Copy or Move action. + var doCopy = copyOptions != null && null == moveOptions; + var doMove = moveOptions != null && null == copyOptions; + + if (doCopy == doMove) + throw new NotSupportedException(Resources.Cannot_Determine_Copy_Or_Move); + + var fullCheck = pathFormat == PathFormat.RelativePath; + // Allow null value for destinationPath when flag MoveOptions.DelayUntilReboot is specified. + var delayUntilReboot = null == destinationPath && doMove && ((MoveOptions) moveOptions & MoveOptions.DelayUntilReboot) != 0; + + Path.CheckSupportedPathFormat(sourcePath, fullCheck, fullCheck); - Path.CheckSupportedPathFormat(destinationPath, fullCheck, fullCheck); + + if (!delayUntilReboot) + Path.CheckSupportedPathFormat(destinationPath, fullCheck, fullCheck); // MSDN: .NET 4+ Trailing spaces are removed from the end of the path parameters before moving the directory. @@ -764,25 +782,19 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin const GetFullPathOptions fullPathOptions = GetFullPathOptions.TrimEnd | GetFullPathOptions.RemoveTrailingDirectorySeparator; var sourcePathLp = Path.GetExtendedLengthPathCore(transaction, sourcePath, pathFormat, fullPathOptions); - var destinationPathLp = Path.GetExtendedLengthPathCore(transaction, destinationPath, pathFormat, fullPathOptions); + var destinationPathLp = delayUntilReboot ? null : Path.GetExtendedLengthPathCore(transaction, destinationPath, pathFormat, fullPathOptions); + // MSDN: .NET3.5+: IOException: The sourceDirName and destDirName parameters refer to the same file or directory. // Do not use StringComparison.OrdinalIgnoreCase to allow renaming a folder with different casing. - if (sourcePathLp.Equals(destinationPathLp)) + if (!delayUntilReboot && sourcePathLp.Equals(destinationPathLp)) NativeError.ThrowException(Win32Errors.ERROR_SAME_DRIVE, destinationPathLp); var emulateMove = false; - - // Determine Copy or Move action. - var doCopy = copyOptions != null; - var doMove = !doCopy && moveOptions != null; - if ((!doCopy && !doMove) || (doCopy && doMove)) - throw new NotSupportedException(Resources.Cannot_Determine_Copy_Or_Move); - - if (doMove) + if (doMove && !delayUntilReboot) { if (((MoveOptions) moveOptions & MoveOptions.CopyAllowed) != 0 || !Volume.IsSameVolume(sourcePath, destinationPath)) { @@ -801,21 +813,29 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin #endregion //Setup + #region Copy if (doCopy) { CreateDirectoryCore(transaction, destinationPathLp, null, null, false, PathFormat.LongFullPath); - foreach (var fsei in EnumerateFileSystemEntryInfosCore(transaction, sourcePathLp, Path.WildcardStarMatchAll, DirectoryEnumerationOptions.FilesAndFolders, PathFormat.LongFullPath)) + foreach (var fsei in EnumerateFileSystemEntryInfosCore(transaction, sourcePathLp, + Path.WildcardStarMatchAll, DirectoryEnumerationOptions.FilesAndFolders, PathFormat.LongFullPath)) { var newDestinationPathLp = Path.CombineCore(false, destinationPathLp, fsei.FileName); cmr = fsei.IsDirectory - ? CopyMoveCore(transaction, fsei.LongFullPath, newDestinationPathLp, copyOptions, null, progressHandler, userProgressData, PathFormat.LongFullPath) - : File.CopyMoveCore(false, transaction, fsei.LongFullPath, newDestinationPathLp, false, copyOptions, null, progressHandler, userProgressData, PathFormat.LongFullPath); + ? CopyMoveCore(transaction, fsei.LongFullPath, newDestinationPathLp, copyOptions, null, + progressHandler, userProgressData, PathFormat.LongFullPath) + : File.CopyMoveCore(false, transaction, fsei.LongFullPath, newDestinationPathLp, false, copyOptions, + null, progressHandler, userProgressData, PathFormat.LongFullPath); + + + if (cmr.IsCanceled) + return cmr; + - // Remove the folder or file when copying was successful. if (emulateMove && cmr.ErrorCode == Win32Errors.ERROR_SUCCESS) { @@ -824,10 +844,6 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin else File.DeleteFileCore(transaction, fsei.LongFullPath, true, PathFormat.LongFullPath); } - - - if (cmr.IsCanceled) - return cmr; } @@ -838,12 +854,13 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin #endregion // Copy + #region Move else { // MSDN: .NET3.5+: IOException: An attempt was made to move a directory to a different volume. - if (((MoveOptions) moveOptions & MoveOptions.CopyAllowed) == 0) + if (!delayUntilReboot && ((MoveOptions) moveOptions & MoveOptions.CopyAllowed) == 0) if (!Path.GetPathRoot(sourcePathLp, false).Equals(Path.GetPathRoot(destinationPathLp, false), StringComparison.OrdinalIgnoreCase)) NativeError.ThrowException(Win32Errors.ERROR_NOT_SAME_DEVICE, destinationPathLp); @@ -860,6 +877,7 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin #endregion // Move + // The copy/move operation succeeded or was canceled. return cmr; } diff --git a/AlphaFS/Filesystem/DirectoryInfo Class/DirectoryInfo.CopyToMoveTo.cs b/AlphaFS/Filesystem/DirectoryInfo Class/DirectoryInfo.CopyToMoveTo.cs index 9e591951d..0459c708e 100644 --- a/AlphaFS/Filesystem/DirectoryInfo Class/DirectoryInfo.CopyToMoveTo.cs +++ b/AlphaFS/Filesystem/DirectoryInfo Class/DirectoryInfo.CopyToMoveTo.cs @@ -165,7 +165,7 @@ public DirectoryInfo CopyTo(string destinationPath, CopyOptions copyOptions, Pat public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, CopyMoveProgressRoutine progressHandler, object userProgressData) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); + var cmr = CopyToMoveToCore(destinationPath, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -199,7 +199,7 @@ public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, Co public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, PathFormat pathFormat) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, pathFormat); + var cmr = CopyToMoveToCore(destinationPath, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, pathFormat); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -240,7 +240,7 @@ public void MoveTo(string destinationPath) #region AlphaFS - /// Moves a instance and its contents to a new path. + /// [AlphaFS] Moves a instance and its contents to a new path. /// A new instance if the directory was completely moved. /// /// Use this method to prevent overwriting of an existing directory by default. @@ -295,7 +295,7 @@ public DirectoryInfo MoveTo(string destinationPath, MoveOptions moveOptions) { string destinationPathLp; CopyToMoveToCore(destinationPath, null, moveOptions, null, null, out destinationPathLp, PathFormat.RelativePath); - return new DirectoryInfo(Transaction, destinationPathLp, PathFormat.LongFullPath); + return null != destinationPathLp ? new DirectoryInfo(Transaction, destinationPathLp, PathFormat.LongFullPath) : null; } /// [AlphaFS] Moves a instance and its contents to a new path, can be specified. @@ -324,7 +324,7 @@ public DirectoryInfo MoveTo(string destinationPath, MoveOptions moveOptions, Pat { string destinationPathLp; CopyToMoveToCore(destinationPath, null, moveOptions, null, null, out destinationPathLp, pathFormat); - return new DirectoryInfo(Transaction, destinationPathLp, PathFormat.LongFullPath); + return null != destinationPathLp ? new DirectoryInfo(Transaction, destinationPathLp, PathFormat.LongFullPath) : null; } @@ -357,7 +357,7 @@ public DirectoryInfo MoveTo(string destinationPath, MoveOptions moveOptions, Pat public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, CopyMoveProgressRoutine progressHandler, object userProgressData) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, null, moveOptions, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); + var cmr = CopyToMoveToCore(destinationPath, null, moveOptions, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -393,7 +393,7 @@ public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, Co public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, PathFormat pathFormat) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, null, moveOptions, progressHandler, userProgressData, out destinationPathLp, pathFormat); + var cmr = CopyToMoveToCore(destinationPath, null, moveOptions, progressHandler, userProgressData, out destinationPathLp, pathFormat); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -402,6 +402,7 @@ public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, Co #endregion // MoveTo + #region Internal Methods /// Copy/move a Non-/Transacted file or directory including its children to a new location, @@ -411,10 +412,9 @@ public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, Co /// /// Option is recommended for very large file transfers. /// You cannot use the Move method to overwrite an existing file, unless contains . - /// This Move method works across disk volumes, and it does not throw an exception if the source and destination are the same. + /// This Move method works across disk volumes. /// Note that if you attempt to replace a file by moving a file of the same name into that directory, you get an IOException. /// - /// A class with the status of the Copy or Move action. /// /// /// @@ -431,20 +431,25 @@ public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, Co [SecurityCritical] private CopyMoveResult CopyToMoveToCore(string destinationPath, CopyOptions? copyOptions, MoveOptions? moveOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, out string longFullPath, PathFormat pathFormat) { - string destinationPathLp = Path.GetExtendedLengthPathCore(null, destinationPath, pathFormat, GetFullPathOptions.TrimEnd | GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck); - longFullPath = destinationPathLp; + // Allow null value for destinationPath when flag MoveOptions.DelayUntilReboot is specified. + var delayUntilReboot = null == destinationPath && null != moveOptions && ((MoveOptions) moveOptions & MoveOptions.DelayUntilReboot) != 0; + + var destinationPathLp = longFullPath = delayUntilReboot + ? null + : Path.GetExtendedLengthPathCore(Transaction, destinationPath, pathFormat, GetFullPathOptions.TrimEnd | GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck); + - // Returns false when CopyMoveProgressResult is PROGRESS_CANCEL or PROGRESS_STOP. return Directory.CopyMoveCore(Transaction, LongFullName, destinationPathLp, copyOptions, moveOptions, progressHandler, userProgressData, PathFormat.LongFullPath); } + private void CopyToMoveToCoreRefresh(string destinationPath, string destinationPathLp) { LongFullName = destinationPathLp; - FullPath = Path.GetRegularPathCore(destinationPathLp, GetFullPathOptions.None, false); + FullPath = null != destinationPathLp ? Path.GetRegularPathCore(destinationPathLp, GetFullPathOptions.None, false) : null; OriginalPath = destinationPath; - DisplayPath = Path.GetRegularPathCore(OriginalPath, GetFullPathOptions.None, false); + DisplayPath = null != OriginalPath ? Path.GetRegularPathCore(OriginalPath, GetFullPathOptions.None, false) : null; // Flush any cached information about the directory. Reset(); diff --git a/AlphaFS/Filesystem/File Class/File.CopyMove.cs b/AlphaFS/Filesystem/File Class/File.CopyMove.cs index 7f0ddd430..ded069c2b 100644 --- a/AlphaFS/Filesystem/File Class/File.CopyMove.cs +++ b/AlphaFS/Filesystem/File Class/File.CopyMove.cs @@ -232,6 +232,7 @@ public static void CopyTransacted(KernelTransaction transaction, string sourceFi #endregion // Copy + #region Copy (CopyOptions) #region Non-Transactional @@ -688,6 +689,7 @@ public static CopyMoveResult CopyTransacted(KernelTransaction transaction, strin #endregion // Copy (CopyOptions) + #region Move #region .NET @@ -717,6 +719,7 @@ public static void Move(string sourceFileName, string destinationFileName) #endregion // .NET + #region AlphaFS #region Non-Transactional @@ -804,6 +807,7 @@ public static void MoveTransacted(KernelTransaction transaction, string sourceFi #endregion // Move + #region Move (MoveOptions) #region Non-Transactional @@ -1032,6 +1036,7 @@ public static CopyMoveResult MoveTransacted(KernelTransaction transaction, strin #endregion // Move (MoveOptions) + #region Internal Methods /// Copy/move a Non-/Transacted file or directory including its children to a new location, or can be specified, @@ -1056,8 +1061,8 @@ public static CopyMoveResult MoveTransacted(KernelTransaction transaction, strin /// /// Specifies that and are a file or directory. /// The transaction. - /// The source directory path. - /// The destination directory path. + /// The source directory path plus file name. + /// The destination directory path plus file name. /// if original Timestamps must be preserved, otherwise. This parameter is ignored for move operations. /// that specify how the file is to be copied. This parameter can be . /// Flags that specify how the file or directory is to be moved. This parameter can be . @@ -1066,37 +1071,52 @@ public static CopyMoveResult MoveTransacted(KernelTransaction transaction, strin /// Indicates the format of the path parameter(s). [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] [SecurityCritical] - internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction transaction, string sourceFileName, string destinationFileName, bool preserveDates, CopyOptions? copyOptions, MoveOptions? moveOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, PathFormat pathFormat) + internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction transaction, string sourcePath, string destinationPath, bool preserveDates, CopyOptions? copyOptions, MoveOptions? moveOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, PathFormat pathFormat) { #region Setup + // Determine Copy or Move action. + var doCopy = copyOptions != null && null == moveOptions; + var doMove = moveOptions != null && null == copyOptions; + + if (doCopy == doMove) + throw new NotSupportedException(Resources.Cannot_Determine_Copy_Or_Move); + + var fullCheck = pathFormat == PathFormat.RelativePath; - Path.CheckSupportedPathFormat(sourceFileName, fullCheck, fullCheck); - Path.CheckSupportedPathFormat(destinationFileName, fullCheck, fullCheck); + // Allow null value for destinationPath when flag MoveOptions.DelayUntilReboot is specified. + var delayUntilReboot = null == destinationPath && doMove && ((MoveOptions) moveOptions & MoveOptions.DelayUntilReboot) != 0; + - var sourceFileNameLp = Path.GetExtendedLengthPathCore(transaction, sourceFileName, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator); - var destFileNameLp = Path.GetExtendedLengthPathCore(transaction, destinationFileName, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator); + Path.CheckSupportedPathFormat(sourcePath, fullCheck, fullCheck); + + if (!delayUntilReboot) + Path.CheckSupportedPathFormat(destinationPath, fullCheck, fullCheck); + + + var sourcePathLp = Path.GetExtendedLengthPathCore(transaction, sourcePath, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator); + var destinationPathLp = delayUntilReboot ? null : Path.GetExtendedLengthPathCore(transaction, destinationPath, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator); + + + // MSDN: .NET3.5+: IOException: The sourceDirName and destDirName parameters refer to the same file or directory. + // Do not use StringComparison.OrdinalIgnoreCase to allow renaming a folder with different casing. + if (!delayUntilReboot && sourcePathLp.Equals(destinationPathLp)) + NativeError.ThrowException(Win32Errors.ERROR_SAME_DRIVE, destinationPathLp); // MSDN: If this flag is set to TRUE during the copy/move operation, the operation is canceled. // Otherwise, the copy/move operation will continue to completion. var cancel = false; - // Determine Copy or Move action. - var doCopy = copyOptions != null; - var doMove = !doCopy && moveOptions != null; - if ((!doCopy && !doMove) || (doCopy && doMove)) - throw new NotSupportedException(Resources.Cannot_Determine_Copy_Or_Move); - - var raiseException = progressHandler == null; + var raiseException = null == progressHandler; // Setup callback function for progress notifications. - var routine = progressHandler != null - ? (totalFileSize, totalBytesTransferred, streamSize, streamBytesTransferred, dwStreamNumber, dwCallbackReason, hSourceFile, hDestinationFile, lpData) => - progressHandler(totalFileSize, totalBytesTransferred, streamSize, streamBytesTransferred, dwStreamNumber, dwCallbackReason, userProgressData) - : (NativeMethods.NativeCopyMoveProgressRoutine) null; + var routine = raiseException + ? (NativeMethods.NativeCopyMoveProgressRoutine) null + : (totalFileSize, totalBytesTransferred, streamSize, streamBytesTransferred, dwStreamNumber, dwCallbackReason, hSourceFile, hDestinationFile, lpData) => + progressHandler(totalFileSize, totalBytesTransferred, streamSize, streamBytesTransferred, dwStreamNumber, dwCallbackReason, userProgressData); #endregion //Setup @@ -1105,6 +1125,7 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra var lastError = Win32Errors.ERROR_SUCCESS; + #region Win32 Copy/Move if (!(transaction == null || !NativeMethods.IsAtLeastWindowsVista @@ -1119,12 +1140,12 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. // 2013-04-15: MSDN confirms LongPath usage. - ? NativeMethods.MoveFileWithProgress(sourceFileNameLp, destFileNameLp, routine, IntPtr.Zero, (MoveOptions) moveOptions) - : NativeMethods.CopyFileEx(sourceFileNameLp, destFileNameLp, routine, IntPtr.Zero, out cancel, (CopyOptions) copyOptions) + ? NativeMethods.MoveFileWithProgress(sourcePathLp, destinationPathLp, routine, IntPtr.Zero, (MoveOptions) moveOptions) + : NativeMethods.CopyFileEx(sourcePathLp, destinationPathLp, routine, IntPtr.Zero, out cancel, (CopyOptions) copyOptions) : doMove - ? NativeMethods.MoveFileTransacted(sourceFileNameLp, destFileNameLp, routine, IntPtr.Zero, (MoveOptions) moveOptions, transaction.SafeHandle) - : NativeMethods.CopyFileTransacted(sourceFileNameLp, destFileNameLp, routine, IntPtr.Zero, out cancel, (CopyOptions) copyOptions, transaction.SafeHandle))) + ? NativeMethods.MoveFileTransacted(sourcePathLp, destinationPathLp, routine, IntPtr.Zero, (MoveOptions) moveOptions, transaction.SafeHandle) + : NativeMethods.CopyFileTransacted(sourcePathLp, destinationPathLp, routine, IntPtr.Zero, out cancel, (CopyOptions) copyOptions, transaction.SafeHandle))) { lastError = (uint) Marshal.GetLastWin32Error(); @@ -1159,7 +1180,7 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra // File.Copy() // File.Move() // MSDN: .NET 3.5+: FileNotFoundException: sourceFileName was not found. - NativeError.ThrowException(lastError, sourceFileNameLp); + NativeError.ThrowException(lastError, sourcePathLp); break; case Win32Errors.ERROR_PATH_NOT_FOUND: @@ -1167,13 +1188,13 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra // File.Move() // Directory.Move() // MSDN: .NET 3.5+: DirectoryNotFoundException: The path specified in sourceFileName or destinationFileName is invalid (for example, it is on an unmapped drive). - NativeError.ThrowException(lastError, sourceFileNameLp); + NativeError.ThrowException(lastError, sourcePathLp); break; case Win32Errors.ERROR_FILE_EXISTS: // File.Copy() // Directory.Copy() - NativeError.ThrowException(lastError, destFileNameLp); + NativeError.ThrowException(lastError, destinationPathLp); break; default: @@ -1183,27 +1204,27 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra // Check if destination directory already exists. // Directory.Move() // MSDN: .NET 3.5+: IOException: destDirName already exists. - if (ExistsCore(true, transaction, destFileNameLp, PathFormat.LongFullPath)) - NativeError.ThrowException(Win32Errors.ERROR_ALREADY_EXISTS, destFileNameLp); + if (ExistsCore(true, transaction, destinationPathLp, PathFormat.LongFullPath)) + NativeError.ThrowException(Win32Errors.ERROR_ALREADY_EXISTS, destinationPathLp); if (doMove) { // Ensure that the source file or directory exists. // Directory.Move() // MSDN: .NET 3.5+: DirectoryNotFoundException: The path specified by sourceDirName is invalid (for example, it is on an unmapped drive). - if (!ExistsCore(isFolder, transaction, sourceFileNameLp, PathFormat.LongFullPath)) - NativeError.ThrowException(isFolder ? Win32Errors.ERROR_PATH_NOT_FOUND : Win32Errors.ERROR_FILE_NOT_FOUND, sourceFileNameLp); + if (!ExistsCore(isFolder, transaction, sourcePathLp, PathFormat.LongFullPath)) + NativeError.ThrowException(isFolder ? Win32Errors.ERROR_PATH_NOT_FOUND : Win32Errors.ERROR_FILE_NOT_FOUND, sourcePathLp); } // Try reading the source file. - var fileNameLp = destFileNameLp; + var fileNameLp = destinationPathLp; if (!isFolder) { - using (var safeHandle = CreateFileCore(transaction, sourceFileNameLp, ExtendedFileAttributes.Normal, null, FileMode.Open, 0, FileShare.Read, false, PathFormat.LongFullPath)) + using (var safeHandle = CreateFileCore(transaction, sourcePathLp, ExtendedFileAttributes.Normal, null, FileMode.Open, 0, FileShare.Read, false, PathFormat.LongFullPath)) if (safeHandle != null && safeHandle.IsInvalid) - fileNameLp = sourceFileNameLp; + fileNameLp = sourcePathLp; } if (lastError == Win32Errors.ERROR_ACCESS_DENIED) @@ -1212,13 +1233,13 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra // File.Move() // MSDN: .NET 3.5+: IOException: An I/O error has occurred. // Directory exists with the same name as the file. - if (!isFolder && ExistsCore(true, transaction, destFileNameLp, PathFormat.LongFullPath)) - NativeError.ThrowException(lastError, string.Format(CultureInfo.CurrentCulture, Resources.Target_File_Is_A_Directory, destFileNameLp)); + if (!isFolder && ExistsCore(true, transaction, destinationPathLp, PathFormat.LongFullPath)) + NativeError.ThrowException(lastError, string.Format(CultureInfo.CurrentCulture, Resources.Target_File_Is_A_Directory, destinationPathLp)); if (doMove) { var data = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA(); - FillAttributeInfoCore(transaction, destFileNameLp, ref data, false, true); + FillAttributeInfoCore(transaction, destinationPathLp, ref data, false, true); if (data.dwFileAttributes != (FileAttributes) (-1)) { @@ -1228,7 +1249,7 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra if (((MoveOptions) moveOptions & MoveOptions.ReplaceExisting) != 0) { // Reset file system object attributes. - SetAttributesCore(isFolder, transaction, destFileNameLp, FileAttributes.Normal, true, PathFormat.LongFullPath); + SetAttributesCore(isFolder, transaction, destinationPathLp, FileAttributes.Normal, true, PathFormat.LongFullPath); goto startCopyMove; } @@ -1237,14 +1258,14 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra // MSDN: .NET 3.5+: UnauthorizedAccessException: destinationFileName is read-only. // MSDN: Win32 CopyFileXxx: This function fails with ERROR_ACCESS_DENIED if the destination file already exists // and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_READONLY attribute set. - throw new FileReadOnlyException(destFileNameLp); + throw new FileReadOnlyException(destinationPathLp); } // MSDN: Win32 CopyFileXxx: This function fails with ERROR_ACCESS_DENIED if the destination file already exists // and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_READONLY attribute set. if ((data.dwFileAttributes & FileAttributes.Hidden) != 0) - NativeError.ThrowException(lastError, string.Format(CultureInfo.CurrentCulture, Resources.File_Is_Hidden, destFileNameLp)); + NativeError.ThrowException(lastError, string.Format(CultureInfo.CurrentCulture, Resources.File_Is_Hidden, destinationPathLp)); } } } @@ -1264,6 +1285,7 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra #endregion // Win32 Copy/Move + #region Transfer Timestamps // Apply original Timestamps if requested. @@ -1273,17 +1295,18 @@ internal static CopyMoveResult CopyMoveCore(bool isFolder, KernelTransaction tra { // Currently preserveDates is only used with files. var data = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA(); - var dataInitialised = FillAttributeInfoCore(transaction, sourceFileNameLp, ref data, false, true); + var dataInitialised = FillAttributeInfoCore(transaction, sourcePathLp, ref data, false, true); if (dataInitialised == Win32Errors.ERROR_SUCCESS && data.dwFileAttributes != (FileAttributes) (-1)) - SetFsoDateTimeCore(false, transaction, destFileNameLp, DateTime.FromFileTimeUtc(data.ftCreationTime), + SetFsoDateTimeCore(false, transaction, destinationPathLp, DateTime.FromFileTimeUtc(data.ftCreationTime), DateTime.FromFileTimeUtc(data.ftLastAccessTime), DateTime.FromFileTimeUtc(data.ftLastWriteTime), false, PathFormat.LongFullPath); } #endregion // Transfer Timestamps + // The copy/move operation succeeded, failed or was canceled. - return new CopyMoveResult(sourceFileNameLp, destFileNameLp, isFolder, doMove, cancel, (int) lastError); + return new CopyMoveResult(sourcePathLp, destinationPathLp, isFolder, doMove, cancel, (int) lastError); } #endregion // Internal Methods diff --git a/AlphaFS/Filesystem/FileInfo Class/FileInfo.CopyToMoveTo.cs b/AlphaFS/Filesystem/FileInfo Class/FileInfo.CopyToMoveTo.cs index e6b1b6f2f..89fcbdb43 100644 --- a/AlphaFS/Filesystem/FileInfo Class/FileInfo.CopyToMoveTo.cs +++ b/AlphaFS/Filesystem/FileInfo Class/FileInfo.CopyToMoveTo.cs @@ -290,7 +290,7 @@ public FileInfo CopyTo(string destinationPath, CopyOptions copyOptions, bool pre public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, CopyMoveProgressRoutine progressHandler, object userProgressData) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, false, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); + var cmr = CopyToMoveToCore(destinationPath, false, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -323,7 +323,7 @@ public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, Co public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, PathFormat pathFormat) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, false, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, pathFormat); + var cmr = CopyToMoveToCore(destinationPath, false, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, pathFormat); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -360,7 +360,7 @@ public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, Co public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, bool preserveDates, CopyMoveProgressRoutine progressHandler, object userProgressData) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, preserveDates, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); + var cmr = CopyToMoveToCore(destinationPath, preserveDates, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -396,7 +396,7 @@ public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, bo public CopyMoveResult CopyTo(string destinationPath, CopyOptions copyOptions, bool preserveDates, CopyMoveProgressRoutine progressHandler, object userProgressData, PathFormat pathFormat) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, preserveDates, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, pathFormat); + var cmr = CopyToMoveToCore(destinationPath, preserveDates, copyOptions, null, progressHandler, userProgressData, out destinationPathLp, pathFormat); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -490,7 +490,7 @@ public FileInfo MoveTo(string destinationPath, MoveOptions moveOptions) { string destinationPathLp; CopyToMoveToCore(destinationPath, false, null, moveOptions, null, null, out destinationPathLp, PathFormat.RelativePath); - return new FileInfo(Transaction, destinationPathLp, PathFormat.LongFullPath); + return null != destinationPathLp ? new FileInfo(Transaction, destinationPathLp, PathFormat.LongFullPath) : null; } /// [AlphaFS] Moves a specified file to a new location, providing the option to specify a new file name, can be specified. @@ -518,7 +518,7 @@ public FileInfo MoveTo(string destinationPath, MoveOptions moveOptions, PathForm { string destinationPathLp; CopyToMoveToCore(destinationPath, false, null, moveOptions, null, null, out destinationPathLp, pathFormat); - return new FileInfo(Transaction, destinationPathLp, PathFormat.LongFullPath); + return null != destinationPathLp ? new FileInfo(Transaction, destinationPathLp, PathFormat.LongFullPath) : null; } @@ -549,7 +549,7 @@ public FileInfo MoveTo(string destinationPath, MoveOptions moveOptions, PathForm public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, CopyMoveProgressRoutine progressHandler, object userProgressData) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, false, null, moveOptions, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); + var cmr = CopyToMoveToCore(destinationPath, false, null, moveOptions, progressHandler, userProgressData, out destinationPathLp, PathFormat.RelativePath); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -579,7 +579,7 @@ public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, Co public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, PathFormat pathFormat) { string destinationPathLp; - CopyMoveResult cmr = CopyToMoveToCore(destinationPath, false, null, moveOptions, progressHandler, userProgressData, out destinationPathLp, pathFormat); + var cmr = CopyToMoveToCore(destinationPath, false, null, moveOptions, progressHandler, userProgressData, out destinationPathLp, pathFormat); CopyToMoveToCoreRefresh(destinationPath, destinationPathLp); return cmr; } @@ -588,16 +588,23 @@ public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, Co #endregion // MoveTo + #region Internal Methods - /// Copy/move an existing file to a new file, allowing the overwriting of an existing file. + /// Copy/move an existing Non-/Transacted file to a new file, allowing the overwriting of an existing file. /// A class with the status of the Copy or Move action. /// /// Option is recommended for very large file transfers. /// Whenever possible, avoid using short file names (such as XXXXXX~1.XXX) with this method. /// If two files have equivalent short file names then this method may fail and raise an exception and/or result in undesirable behavior. /// - /// A full path string to the destination directory + /// + /// + /// + /// + /// + /// + /// The destination directory path plus file name. /// if original Timestamps must be preserved, otherwise. /// This parameter can be . Use to specify how the file is to be copied. /// This parameter can be . Use that specify how the file is to be moved. @@ -605,32 +612,30 @@ public CopyMoveResult MoveTo(string destinationPath, MoveOptions moveOptions, Co /// This parameter can be . The argument to be passed to the callback function. /// [out] Returns the retrieved long full path. /// Indicates the format of the path parameter(s). - /// - /// - /// - /// - /// - /// [SecurityCritical] private CopyMoveResult CopyToMoveToCore(string destinationPath, bool preserveDates, CopyOptions? copyOptions, MoveOptions? moveOptions, CopyMoveProgressRoutine progressHandler, object userProgressData, out string longFullPath, PathFormat pathFormat) { - string destinationPathLp = Path.GetExtendedLengthPathCore(Transaction, destinationPath, pathFormat, GetFullPathOptions.TrimEnd | GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck); + // Allow null value for destinationPath when flag MoveOptions.DelayUntilReboot is specified. + var delayUntilReboot = null == destinationPath && null != moveOptions && ((MoveOptions) moveOptions & MoveOptions.DelayUntilReboot) != 0; + + var destinationPathLp = longFullPath = delayUntilReboot + ? null + : Path.GetExtendedLengthPathCore(Transaction, destinationPath, pathFormat, GetFullPathOptions.TrimEnd | GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck); - longFullPath = destinationPathLp; - // Returns false when CopyMoveProgressResult is PROGRESS_CANCEL or PROGRESS_STOP. return File.CopyMoveCore(false, Transaction, LongFullName, destinationPathLp, preserveDates, copyOptions, moveOptions, progressHandler, userProgressData, PathFormat.LongFullPath); } + private void CopyToMoveToCoreRefresh(string destinationPath, string destinationPathLp) { LongFullName = destinationPathLp; - FullPath = Path.GetRegularPathCore(destinationPathLp, GetFullPathOptions.None, false); + FullPath = null != destinationPathLp ? Path.GetRegularPathCore(destinationPathLp, GetFullPathOptions.None, false) : null; OriginalPath = destinationPath; - DisplayPath = Path.GetRegularPathCore(OriginalPath, GetFullPathOptions.None, false); + DisplayPath = null != OriginalPath ? Path.GetRegularPathCore(OriginalPath, GetFullPathOptions.None, false) : null; - _name = Path.GetFileName(destinationPathLp, true); + _name = null != destinationPathLp ? Path.GetFileName(destinationPathLp, true) : null; // Flush any cached information about the file. Reset(); diff --git a/AlphaFS/Filesystem/Path Class/Path.ShortLongConversions.cs b/AlphaFS/Filesystem/Path Class/Path.ShortLongConversions.cs index 5209d0c61..08c757bb0 100644 --- a/AlphaFS/Filesystem/Path Class/Path.ShortLongConversions.cs +++ b/AlphaFS/Filesystem/Path Class/Path.ShortLongConversions.cs @@ -242,6 +242,7 @@ internal static string GetRegularPathCore(string path, GetFullPathOptions option /// Gets the path as a long full path. /// The path as an extended length path. /// + /// /// The transaction. /// Full pathname of the source path to convert. /// The path format to use. diff --git a/CHANGELOG.md b/CHANGELOG.md index eec50b205..4ccba8dc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,11 @@ Version 2.1.3 (2017-XX-XX) - Issue #289: `Alphaleonis.Win32.Network.Host.GetShareInfo` doesn't work since 2.1.0 (Thx Schoolmonkey/damiarnold) - Issue #296: Folder rename (casing) throws IOException with HResult `ERROR_SAME_DRIVE` (Thx doormalena) - Issue #297: Incorrect domain returned from `Host.EnumerateDomainDfsRoot` when specifying domain (Thx damiarnold) +- Issue #299: `FileInfo.MoveTo` and `DirectoryInfo.MoveTo` throw `ArgumentNullException` on empty destination path (Thx doormalena) - Issue #312: `Volume.EnumerateVolumes` skips first volume (Thx springy76) - Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx damiarnold) - Issue #320: Minor changes in comments in `Win32Errors.cs` to eliminate compiler warnings. (Thx besoft) +- Issue #321: `DirectoryInfo.CopyToMoveToCore()` calls `Path.GetExtendedLengthPathCore()` without `Transaction` parameter. Version 2.1.2 (2016-10-30) From 2d2f01767d5d79fadecb7f4546f40a5956339538 Mon Sep 17 00:00:00 2001 From: Yomodo Date: Fri, 26 May 2017 18:42:38 +0200 Subject: [PATCH 14/15] Updated CHANGELOG. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ccba8dc7..ef6875fe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Version 2.1.3 (2017-XX-XX) - Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx damiarnold) - Issue #320: Minor changes in comments in `Win32Errors.cs` to eliminate compiler warnings. (Thx besoft) - Issue #321: `DirectoryInfo.CopyToMoveToCore()` calls `Path.GetExtendedLengthPathCore()` without `Transaction` parameter. +- Issue #325: `DeleteEmptySubdirectories` (with `recursive=true`) throws `System.IO.DirectoryNotFoundException` (Thx kryvoplias) Version 2.1.2 (2016-10-30) From 9fa77ba92a5fdcc2cbd21c60fd6d3dd8b2f84d5d Mon Sep 17 00:00:00 2001 From: Jeffrey Jangli Date: Tue, 30 May 2017 12:33:32 +0200 Subject: [PATCH 15/15] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef6875fe5..4ccba8dc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,6 @@ Version 2.1.3 (2017-XX-XX) - Issue #313: `GetHostShareFromPath()` fails with spaces in share name (Thx damiarnold) - Issue #320: Minor changes in comments in `Win32Errors.cs` to eliminate compiler warnings. (Thx besoft) - Issue #321: `DirectoryInfo.CopyToMoveToCore()` calls `Path.GetExtendedLengthPathCore()` without `Transaction` parameter. -- Issue #325: `DeleteEmptySubdirectories` (with `recursive=true`) throws `System.IO.DirectoryNotFoundException` (Thx kryvoplias) Version 2.1.2 (2016-10-30)