|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146 |
- /* 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.Win32.SafeHandles;
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Security;
- using System.Text;
-
- namespace Alphaleonis.Win32.Filesystem
- {
- /// <summary>Static class providing utility methods for working with Microsoft Windows devices and volumes.</summary>
- public static class Volume
- {
- #region DosDevice
-
- #region DefineDosDevice
-
- /// <summary>Defines, redefines, or deletes MS-DOS device names.</summary>
- /// <param name="deviceName">An MS-DOS device name string specifying the device the function is defining, redefining, or deleting.</param>
- /// <param name="targetPath">An MS-DOS path that will implement this device.</param>
-
- [SecurityCritical]
- public static void DefineDosDevice(string deviceName, string targetPath)
- {
- DefineDosDeviceCore(true, deviceName, targetPath, DosDeviceAttributes.None, false);
- }
-
- /// <summary>Defines, redefines, or deletes MS-DOS device names.</summary>
- /// <param name="deviceName">
- /// An MS-DOS device name string specifying the device the function is defining, redefining, or deleting.
- /// </param>
- /// <param name="targetPath">
- /// >An MS-DOS path that will implement this device. If <paramref name="deviceAttributes"/> parameter has the
- /// <see cref="DosDeviceAttributes.RawTargetPath"/> flag specified, <paramref name="targetPath"/> is used as is.
- /// </param>
- /// <param name="deviceAttributes">
- /// The controllable aspects of the DefineDosDevice function, <see cref="DosDeviceAttributes"/> flags which will be combined with the
- /// default.
- /// </param>
- [SecurityCritical]
- public static void DefineDosDevice(string deviceName, string targetPath, DosDeviceAttributes deviceAttributes)
- {
- DefineDosDeviceCore(true, deviceName, targetPath, deviceAttributes, false);
- }
-
- #endregion // DefineDosDevice
-
- #region DeleteDosDevice
-
- /// <summary>Deletes an MS-DOS device name.</summary>
- /// <param name="deviceName">An MS-DOS device name specifying the device to delete.</param>
- [SecurityCritical]
- public static void DeleteDosDevice(string deviceName)
- {
- DefineDosDeviceCore(false, deviceName, null, DosDeviceAttributes.RemoveDefinition, false);
- }
-
- /// <summary>Deletes an MS-DOS device name.</summary>
- /// <param name="deviceName">An MS-DOS device name string specifying the device to delete.</param>
- /// <param name="targetPath">
- /// A pointer to a path string that will implement this device. The string is an MS-DOS path string unless the
- /// <see cref="DosDeviceAttributes.RawTargetPath"/> flag is specified, in which case this string is a path string.
- /// </param>
- [SecurityCritical]
- public static void DeleteDosDevice(string deviceName, string targetPath)
- {
- DefineDosDeviceCore(false, deviceName, targetPath, DosDeviceAttributes.RemoveDefinition, false);
- }
-
- /// <summary>Deletes an MS-DOS device name.</summary>
- /// <param name="deviceName">An MS-DOS device name string specifying the device to delete.</param>
- /// <param name="targetPath">
- /// A pointer to a path string that will implement this device. The string is an MS-DOS path string unless the
- /// <see cref="DosDeviceAttributes.RawTargetPath"/> flag is specified, in which case this string is a path string.
- /// </param>
- /// <param name="exactMatch">
- /// Only delete MS-DOS device on an exact name match. If <paramref name="exactMatch"/> is <see langword="true"/>,
- /// <paramref name="targetPath"/> must be the same path used to create the mapping.
- /// </param>
- [SecurityCritical]
- public static void DeleteDosDevice(string deviceName, string targetPath, bool exactMatch)
- {
- DefineDosDeviceCore(false, deviceName, targetPath, DosDeviceAttributes.RemoveDefinition, exactMatch);
- }
-
- /// <summary>Deletes an MS-DOS device name.</summary>
- /// <param name="deviceName">An MS-DOS device name string specifying the device to delete.</param>
- /// <param name="targetPath">
- /// A pointer to a path string that will implement this device. The string is an MS-DOS path string unless the
- /// <see cref="DosDeviceAttributes.RawTargetPath"/> flag is specified, in which case this string is a path string.
- /// </param>
- /// <param name="deviceAttributes">
- /// The controllable aspects of the DefineDosDevice function <see cref="DosDeviceAttributes"/> flags which will be combined with the
- /// default.
- /// </param>
- /// <param name="exactMatch">
- /// Only delete MS-DOS device on an exact name match. If <paramref name="exactMatch"/> is <see langword="true"/>,
- /// <paramref name="targetPath"/> must be the same path used to create the mapping.
- /// </param>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [SecurityCritical]
- public static void DeleteDosDevice(string deviceName, string targetPath, DosDeviceAttributes deviceAttributes, bool exactMatch)
- {
- DefineDosDeviceCore(false, deviceName, targetPath, deviceAttributes, exactMatch);
- }
-
- #endregion // DeleteDosDevice
-
- #region QueryAllDosDevices
-
- /// <summary>Retrieves a list of all existing MS-DOS device names.</summary>
- /// <returns>An <see cref="IEnumerable{String}"/> with one or more existing MS-DOS device names.</returns>
- [SecurityCritical]
- public static IEnumerable<string> QueryAllDosDevices()
- {
- return QueryDosDevice(null, null);
- }
-
- /// <summary>Retrieves a list of all existing MS-DOS device names.</summary>
- /// <param name="deviceName">
- /// (Optional, default: <see langword="null"/>) An MS-DOS device name string specifying the target of the query. This parameter can be
- /// "sort". In that case a sorted list of all existing MS-DOS device names is returned. This parameter can be <see langword="null"/>.
- /// In that case, the <see cref="QueryDosDevice"/> function will store a list of all existing MS-DOS device names into the buffer.
- /// </param>
- /// <returns>An <see cref="IEnumerable{String}"/> with or more existing MS-DOS device names.</returns>
- [SecurityCritical]
- public static IEnumerable<string> QueryAllDosDevices(string deviceName)
- {
- return QueryDosDevice(null, deviceName);
- }
-
- #endregion // QueryAllDosDevices
-
- #region QueryDosDevice
-
- /// <summary>
- /// Retrieves information about MS-DOS device names. The function can obtain the current mapping for a particular MS-DOS device name.
- /// The function can also obtain a list of all existing MS-DOS device names.
- /// </summary>
- /// <param name="deviceName">
- /// An MS-DOS device name string, or part of, specifying the target of the query. This parameter can be <see langword="null"/>. In that
- /// case, the QueryDosDevice function will store a list of all existing MS-DOS device names into the buffer.
- /// </param>
- /// <param name="options">
- /// (Optional, default: <see langword="false"/>) If options[0] = <see langword="true"/> a sorted list will be returned.
- /// </param>
- /// <returns>An <see cref="IEnumerable{String}"/> with one or more existing MS-DOS device names.</returns>
- [SecurityCritical]
- public static IEnumerable<string> QueryDosDevice(string deviceName, params string[] options)
- {
- // deviceName is allowed to be null.
- // The deviceName cannot have a trailing backslash.
- deviceName = Path.RemoveTrailingDirectorySeparator(deviceName, false);
-
- var searchFilter = (deviceName != null);
-
- // Only process options if a device is supplied.
- if (searchFilter)
- {
- // Check that at least one "options[]" has something to say. If so, rebuild them.
- options = options != null && options.Any() ? new[] { deviceName, options[0] } : new[] { deviceName, string.Empty };
-
- deviceName = null;
- }
-
- // Choose sorted output.
- var doSort = options != null &&
- options.Any(s => s != null && s.Equals("sort", StringComparison.OrdinalIgnoreCase));
-
- // Start with a larger buffer when using a searchFilter.
- var bufferSize = (uint) (searchFilter || doSort || (options == null) ? 8*NativeMethods.DefaultFileBufferSize : 256);
- uint bufferResult = 0;
-
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- while (bufferResult == 0)
- {
- var cBuffer = new char[bufferSize];
-
- // QueryDosDevice()
- // In the ANSI version of this function, the name is limited to MAX_PATH characters.
- // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
- // 2014-01-29: MSDN does not confirm LongPath usage but a Unicode version of this function exists.
-
- bufferResult = NativeMethods.QueryDosDevice(deviceName, cBuffer, bufferSize);
- var lastError = Marshal.GetLastWin32Error();
-
- if (bufferResult == 0)
- switch ((uint) lastError)
- {
- case Win32Errors.ERROR_MORE_DATA:
- case Win32Errors.ERROR_INSUFFICIENT_BUFFER:
- bufferSize *= 2;
- continue;
-
- default:
- NativeError.ThrowException(lastError, deviceName);
- break;
- }
-
- var dosDev = new List<string>();
- var buffer = new StringBuilder();
-
- for (var i = 0; i < bufferResult; i++)
- {
- if (cBuffer[i] != Path.StringTerminatorChar)
- buffer.Append(cBuffer[i]);
-
- else if (buffer.Length > 0)
- {
- dosDev.Add(buffer.ToString());
- buffer.Length = 0;
- }
- }
-
- // Choose the yield back query; filtered or list.
- var selectQuery = searchFilter
- ? dosDev.Where(dev => options != null && dev.StartsWith(options[0], StringComparison.OrdinalIgnoreCase))
- : dosDev;
-
- foreach (var dev in (doSort) ? selectQuery.OrderBy(n => n) : selectQuery)
- yield return dev;
- }
- }
-
- #endregion // QueryDosDevice
-
- #endregion // DosDevice
-
- #region Drive
-
- #region GetDriveFormat
-
- /// <summary>Gets the name of the file system, such as NTFS or FAT32.</summary>
- /// <remarks>Use DriveFormat to determine what formatting a drive uses.</remarks>
- /// <param name="drivePath">
- /// A path to a drive. For example: "C:\", "\\server\share", or "\\?\Volume{c0580d5e-2ad6-11dc-9924-806e6f6e6963}\".
- /// </param>
- /// <returns>The name of the file system on the specified drive or <see langword="null"/> on failure or if not available.</returns>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [SecurityCritical]
- public static string GetDriveFormat(string drivePath)
- {
- var fsName = new VolumeInfo(drivePath, true, true).FileSystemName;
- return Utils.IsNullOrWhiteSpace(fsName) ? null : fsName;
- }
-
- #endregion // GetDriveFormat
-
- #region GetDriveNameForNtDeviceName
-
- /// <summary>Gets the drive letter from an MS-DOS device name. For example: "\Device\HarddiskVolume2" returns "C:\".</summary>
- /// <param name="deviceName">An MS-DOS device name.</param>
- /// <returns>The drive letter from an MS-DOS device name.</returns>
- [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Nt")]
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nt")]
- public static string GetDriveNameForNtDeviceName(string deviceName)
- {
- return (from drive in Directory.EnumerateLogicalDrivesCore(false, false)
- where drive.DosDeviceName.Equals(deviceName, StringComparison.OrdinalIgnoreCase)
- select drive.Name).FirstOrDefault();
- }
-
- #endregion // GetDriveNameForNtDeviceName
-
- #region GetCurrentDriveType
-
- /// <summary>
- /// Determines, based on the root of the current directory, whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network
- /// drive.
- /// </summary>
- /// <returns>A <see cref="DriveType"/> object.</returns>
- [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
- [SecurityCritical]
- public static DriveType GetCurrentDriveType()
- {
- return GetDriveType(null);
- }
-
- #endregion // GetCurrentDriveType
-
- #region GetDriveType
-
- /// <summary>Determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network drive.</summary>
- /// <param name="drivePath">A path to a drive. For example: "C:\", "\\server\share", or "\\?\Volume{c0580d5e-2ad6-11dc-9924-806e6f6e6963}\"</param>
- /// <returns>A <see cref="System.IO.DriveType"/> object.</returns>
- [SecurityCritical]
- public static DriveType GetDriveType(string drivePath)
- {
- // drivePath is allowed to be == null.
-
- drivePath = Path.AddTrailingDirectorySeparator(drivePath, false);
-
- // ChangeErrorMode is for the Win32 SetThreadErrorMode() method, used to suppress possible pop-ups.
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- return NativeMethods.GetDriveType(drivePath);
- }
-
- #endregion // GetDriveType
-
- #region GetDiskFreeSpace
-
- /// <summary>
- /// Retrieves information about the amount of space that is available on a disk volume, which is the total amount of space, the total
- /// amount of free space, and the total amount of free space available to the user that is associated with the calling thread.
- /// </summary>
- /// <remarks>The calling application must have FILE_LIST_DIRECTORY access rights for this directory.</remarks>
- /// <param name="drivePath">
- /// A path to a drive. For example: "C:\", "\\server\share", or "\\?\Volume{c0580d5e-2ad6-11dc-9924-806e6f6e6963}\".
- /// </param>
- /// <returns>A <see ref="Alphaleonis.Win32.Filesystem.DiskSpaceInfo"/> class instance.</returns>
- [SecurityCritical]
- public static DiskSpaceInfo GetDiskFreeSpace(string drivePath)
- {
- return new DiskSpaceInfo(drivePath, null, true, true);
- }
-
- /// <summary>
- /// Retrieves information about the amount of space that is available on a disk volume, which is the total amount of space, the total
- /// amount of free space, and the total amount of free space available to the user that is associated with the calling thread.
- /// </summary>
- /// <remarks>The calling application must have FILE_LIST_DIRECTORY access rights for this directory.</remarks>
- /// <param name="drivePath">
- /// A path to a drive. For example: "C:\", "\\server\share", or "\\?\Volume{c0580d5e-2ad6-11dc-9924-806e6f6e6963}\".
- /// </param>
- /// <param name="spaceInfoType">
- /// <see langword="null"/> gets both size- and disk cluster information. <see langword="true"/> Get only disk cluster information,
- /// <see langword="false"/> Get only size information.
- /// </param>
- /// <returns>A <see ref="Alphaleonis.Win32.Filesystem.DiskSpaceInfo"/> class instance.</returns>
- [SecurityCritical]
- public static DiskSpaceInfo GetDiskFreeSpace(string drivePath, bool? spaceInfoType)
- {
- return new DiskSpaceInfo(drivePath, spaceInfoType, true, true);
- }
-
- #endregion // GetDiskFreeSpace
-
- #region IsReady
-
- /// <summary>Gets a value indicating whether a drive is ready.</summary>
- /// <param name="drivePath">
- /// A path to a drive. For example: "C:\", "\\server\share", or "\\?\Volume{c0580d5e-2ad6-11dc-9924-806e6f6e6963}\".
- /// </param>
- /// <returns><see langword="true"/> if <paramref name="drivePath"/> is ready; otherwise, <see langword="false"/>.</returns>
- [SecurityCritical]
- public static bool IsReady(string drivePath)
- {
- return File.ExistsCore(true, null, drivePath, PathFormat.FullPath);
- }
-
- #endregion // IsReady
-
- #endregion // Drive
-
- #region Volume
-
- #region DeleteCurrentVolumeLabel
-
- /// <summary>Deletes the label of the file system volume that is the root of the current directory.
- /// </summary>
- [SecurityCritical]
- public static void DeleteCurrentVolumeLabel()
- {
- SetVolumeLabel(null, null);
- }
- #endregion // DeleteCurrentVolumeLabel
-
- #region DeleteVolumeLabel
-
- /// <summary>Deletes the label of a file system volume.</summary>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="rootPathName">The root directory of a file system volume. This is the volume the function will remove the label.</param>
- [SecurityCritical]
- public static void DeleteVolumeLabel(string rootPathName)
- {
- if (Utils.IsNullOrWhiteSpace(rootPathName))
- throw new ArgumentNullException("rootPathName");
-
- SetVolumeLabel(rootPathName, null);
- }
-
- #endregion // DeleteVolumeLabel
-
- #region DeleteVolumeMountPoint
-
- /// <summary>Deletes a Drive letter or mounted folder.</summary>
- /// <remarks>Deleting a mounted folder does not cause the underlying directory to be deleted.</remarks>
- /// <remarks>
- /// If the <paramref name="volumeMountPoint"/> parameter is a directory that is not a mounted folder, the function does nothing. The
- /// directory is not deleted.
- /// </remarks>
- /// <remarks>
- /// It's not an error to attempt to unmount a volume from a volume mount point when there is no volume actually mounted at that volume
- /// mount point.
- /// </remarks>
- /// <param name="volumeMountPoint">The Drive letter or mounted folder to be deleted. For example, X:\ or Y:\MountX\.</param>
- [SecurityCritical]
- public static void DeleteVolumeMountPoint(string volumeMountPoint)
- {
- DeleteVolumeMountPointCore(volumeMountPoint, false);
- }
-
- #endregion // DeleteVolumeMountPoint
-
- #region EnumerateVolumeMountPoints
-
- /// <summary>
- /// Returns an enumerable collection of <see cref="String"/> of all mounted folders (volume mount points) on the specified volume.
- /// </summary>
- /// <exception cref="ArgumentNullException"/>
- /// <exception cref="ArgumentException"/>
- /// <param name="volumeGuid">A <see cref="string"/> containing the volume <see cref="Guid"/>.</param>
- /// <returns>An enumerable collection of <see cref="String"/> of all volume mount points on the specified volume.</returns>
- [SecurityCritical]
- public static IEnumerable<string> EnumerateVolumeMountPoints(string volumeGuid)
- {
- if (Utils.IsNullOrWhiteSpace(volumeGuid))
- throw new ArgumentNullException("volumeGuid");
-
- if (!volumeGuid.StartsWith(Path.VolumePrefix + "{", StringComparison.OrdinalIgnoreCase))
- throw new ArgumentException(Resources.Not_A_Valid_Guid, volumeGuid);
-
- // A trailing backslash is required.
- volumeGuid = Path.AddTrailingDirectorySeparator(volumeGuid, false);
-
- var buffer = new StringBuilder(NativeMethods.MaxPathUnicode);
-
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- using (var handle = NativeMethods.FindFirstVolumeMountPoint(volumeGuid, buffer, (uint)buffer.Capacity))
- {
- var lastError = Marshal.GetLastWin32Error();
-
- if (handle.IsInvalid)
- {
- handle.Close();
-
- switch ((uint) lastError)
- {
- case Win32Errors.ERROR_NO_MORE_FILES:
- case Win32Errors.ERROR_PATH_NOT_FOUND: // Observed with USB stick, FAT32 formatted.
- yield break;
-
- default:
- NativeError.ThrowException(lastError, volumeGuid);
- break;
- }
- }
-
- yield return buffer.ToString();
-
-
- while (NativeMethods.FindNextVolumeMountPoint(handle, buffer, (uint)buffer.Capacity))
- {
- lastError = Marshal.GetLastWin32Error();
-
- if (handle.IsInvalid)
- {
- handle.Close();
-
- 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;
-
- default:
- NativeError.ThrowException(lastError, volumeGuid);
- break;
- }
- }
-
- yield return buffer.ToString();
- }
- }
- }
-
- #endregion // EnumerateVolumeMountPoints
-
- #region EnumerateVolumePathNames
-
- /// <summary>
- /// Returns an enumerable collection of <see cref="String"/> drive letters and mounted folder paths for the specified volume.
- /// </summary>
- /// <exception cref="ArgumentNullException"/>
- /// <exception cref="ArgumentException"/>
- /// <param name="volumeGuid">A volume <see cref="Guid"/> path: \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\.</param>
- /// <returns>An enumerable collection of <see cref="String"/> containing the path names for the specified volume.</returns>
- [SecurityCritical]
- public static IEnumerable<string> EnumerateVolumePathNames(string volumeGuid)
- {
- if (Utils.IsNullOrWhiteSpace(volumeGuid))
- throw new ArgumentNullException("volumeGuid");
-
- if (!volumeGuid.StartsWith(Path.VolumePrefix + "{", StringComparison.OrdinalIgnoreCase))
- throw new ArgumentException(Resources.Not_A_Valid_Guid, volumeGuid);
-
- var volName = Path.AddTrailingDirectorySeparator(volumeGuid, false);
-
- uint requiredLength = 10;
- var cBuffer = new char[requiredLength];
-
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- while (!NativeMethods.GetVolumePathNamesForVolumeName(volName, cBuffer, (uint)cBuffer.Length, out requiredLength))
- {
- var lastError = Marshal.GetLastWin32Error();
-
- switch ((uint)lastError)
- {
- case Win32Errors.ERROR_MORE_DATA:
- case Win32Errors.ERROR_INSUFFICIENT_BUFFER:
- cBuffer = new char[requiredLength];
- break;
-
- default:
- NativeError.ThrowException(lastError, volumeGuid);
- break;
- }
- }
-
- var buffer = new StringBuilder(cBuffer.Length);
- foreach (var c in cBuffer)
- {
- if (c != Path.StringTerminatorChar)
- buffer.Append(c);
- else
- {
- if (buffer.Length > 0)
- {
- yield return buffer.ToString();
- buffer.Length = 0;
- }
- }
- }
- }
-
- #endregion // EnumerateVolumePathNames
-
- #region EnumerateVolumes
-
- /// <summary>Returns an enumerable collection of <see cref="String"/> volumes on the computer.</summary>
- /// <returns>An enumerable collection of <see cref="String"/> volume names on the computer.</returns>
- [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
- [SecurityCritical]
- public static IEnumerable<string> EnumerateVolumes()
- {
- var buffer = new StringBuilder(NativeMethods.MaxPathUnicode);
-
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- using (var handle = NativeMethods.FindFirstVolume(buffer, (uint)buffer.Capacity))
- {
- while (handle != null && !handle.IsInvalid)
- {
- if (NativeMethods.FindNextVolume(handle, buffer, (uint)buffer.Capacity))
- yield return buffer.ToString();
-
- else
- {
- var lastError = Marshal.GetLastWin32Error();
-
- handle.Close();
-
- if (lastError == Win32Errors.ERROR_NO_MORE_FILES)
- yield break;
-
- NativeError.ThrowException(lastError);
- }
- }
- }
- }
-
- #endregion // EnumerateVolumes
-
- #region GetUniqueVolumeNameForPath
-
- /// <summary>
- /// Get the unique volume name for the given path.
- /// </summary>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="volumePathName">
- /// A path string. Both absolute and relative file and directory names, for example "..", is acceptable in this path. If you specify a
- /// relative file or directory name without a volume qualifier, GetUniqueVolumeNameForPath returns the Drive letter of the current
- /// volume.
- /// </param>
- /// <returns>
- /// <para>Returns the unique volume name in the form: "\\?\Volume{GUID}\",</para>
- /// <para>or <see langword="null"/> on error or if unavailable.</para>
- /// </returns>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [SecurityCritical]
- public static string GetUniqueVolumeNameForPath(string volumePathName)
- {
- if (Utils.IsNullOrWhiteSpace(volumePathName))
- throw new ArgumentNullException("volumePathName");
-
- try
- {
- return GetVolumeGuid(GetVolumePathName(volumePathName));
- }
- catch
- {
- return null;
- }
- }
-
- #endregion // GetUniqueVolumeNameForPath
-
- #region GetVolumeDeviceName
-
- /// <summary>Retrieves the Win32 Device name from the Volume name.</summary>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="volumeName">Name of the Volume.</param>
- /// <returns>
- /// The Win32 Device name from the Volume name (for example: "\Device\HarddiskVolume2"), or <see langword="null"/> on error or if
- /// unavailable.
- /// </returns>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [SecurityCritical]
- public static string GetVolumeDeviceName(string volumeName)
- {
- if (Utils.IsNullOrWhiteSpace(volumeName))
- throw new ArgumentNullException("volumeName");
-
- volumeName = Path.RemoveTrailingDirectorySeparator(volumeName, false);
-
- #region GlobalRoot
-
- if (volumeName.StartsWith(Path.GlobalRootPrefix, StringComparison.OrdinalIgnoreCase))
- return volumeName.Substring(Path.GlobalRootPrefix.Length);
-
- #endregion // GlobalRoot
-
- bool doQueryDos;
-
- #region Volume
-
- if (volumeName.StartsWith(Path.VolumePrefix, StringComparison.OrdinalIgnoreCase))
- {
- // Isolate the DOS Device from the Volume name, in the format: Volume{GUID}
- volumeName = volumeName.Substring(Path.LongPathPrefix.Length);
- doQueryDos = true;
- }
-
- #endregion // Volume
-
- #region Logical Drive
-
- // Check for Logical Drives: C:, D:, ...
- else
- {
- // Don't use char.IsLetter() here as that can be misleading.
- // The only valid drive letters are: a-z and A-Z.
- var c = volumeName[0];
- doQueryDos = (volumeName[1] == Path.VolumeSeparatorChar && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')));
- }
-
- #endregion // Logical Drive
-
- if (doQueryDos)
- {
- try
- {
- // Get the real Device underneath.
- var dev = QueryDosDevice(volumeName).FirstOrDefault();
- return !Utils.IsNullOrWhiteSpace(dev) ? dev : null;
- }
- catch
- {
- }
- }
-
- return null;
- }
-
- #endregion // GetVolumeDeviceName
-
- #region GetVolumeDisplayName
-
- /// <summary>Gets the shortest display name for the specified <paramref name="volumeName"/>.</summary>
- /// <remarks>This method basically returns the shortest string returned by <see cref="EnumerateVolumePathNames"/></remarks>
- /// <param name="volumeName">A volume <see cref="Guid"/> path: \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\.</param>
- /// <returns>
- /// The shortest display name for the specified volume found, or <see langword="null"/> if no display names were found.
- /// </returns>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [SecurityCritical]
- public static string GetVolumeDisplayName(string volumeName)
- {
- string[] smallestMountPoint = { new string(Path.WildcardStarMatchAllChar, NativeMethods.MaxPathUnicode) };
-
- try
- {
- foreach (var m in EnumerateVolumePathNames(volumeName).Where(m => !Utils.IsNullOrWhiteSpace(m) && m.Length < smallestMountPoint[0].Length))
- smallestMountPoint[0] = m;
- }
- catch
- {
- }
-
- var result = smallestMountPoint[0][0] == Path.WildcardStarMatchAllChar ? null : smallestMountPoint[0];
- return Utils.IsNullOrWhiteSpace(result) ? null : result;
- }
-
- #endregion // GetVolumeDisplayName
-
- #region GetVolumeGuid
-
- /// <summary>
- /// Retrieves a volume <see cref="Guid"/> path for the volume that is associated with the specified volume mount point (drive letter,
- /// volume GUID path, or mounted folder).
- /// </summary>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="volumeMountPoint">
- /// The path of a mounted folder (for example, "Y:\MountX\") or a drive letter (for example, "X:\").
- /// </param>
- /// <returns>The unique volume name of the form: "\\?\Volume{GUID}\".</returns>
- [SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke", Justification = "Marshal.GetLastWin32Error() is manipulated.")]
- [SecurityCritical]
- public static string GetVolumeGuid(string volumeMountPoint)
- {
- if (Utils.IsNullOrWhiteSpace(volumeMountPoint))
- throw new ArgumentNullException("volumeMountPoint");
-
- // The string must end with a trailing backslash ('\').
- volumeMountPoint = Path.GetFullPathCore(null, volumeMountPoint, GetFullPathOptions.AsLongPath | GetFullPathOptions.AddTrailingDirectorySeparator | GetFullPathOptions.FullCheck);
-
- var volumeGuid = new StringBuilder(100);
- var uniqueName = new StringBuilder(100);
-
- try
- {
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- {
- // GetVolumeNameForVolumeMountPoint()
- // In the ANSI version of this function, the name is limited to 248 characters.
- // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
- // 2013-07-18: MSDN does not confirm LongPath usage but a Unicode version of this function exists.
-
- return NativeMethods.GetVolumeNameForVolumeMountPoint(volumeMountPoint, volumeGuid, (uint) volumeGuid.Capacity)
- ? NativeMethods.GetVolumeNameForVolumeMountPoint(Path.AddTrailingDirectorySeparator(volumeGuid.ToString(), false), uniqueName, (uint) uniqueName.Capacity)
- ? uniqueName.ToString()
- : null
- : null;
- }
- }
- finally
- {
- var lastError = (uint) Marshal.GetLastWin32Error();
-
- switch (lastError)
- {
- case Win32Errors.ERROR_INVALID_NAME:
- NativeError.ThrowException(lastError, volumeMountPoint);
- break;
-
- case Win32Errors.ERROR_MORE_DATA:
- // (1) When GetVolumeNameForVolumeMountPoint() succeeds, lastError is set to Win32Errors.ERROR_MORE_DATA.
- break;
-
- default:
- // (2) When volumeMountPoint is a network drive mapping or UNC path, lastError is set to Win32Errors.ERROR_INVALID_PARAMETER.
-
- // Throw IOException.
- NativeError.ThrowException(lastError, volumeMountPoint);
- break;
- }
- }
- }
-
- #endregion // GetVolumeGuid
-
- #region GetVolumeGuidForNtDeviceName
-
- /// <summary>
- /// Tranlates DosDevicePath to a Volume GUID. For example: "\Device\HarddiskVolumeX\path\filename.ext" can translate to: "\path\
- /// filename.ext" or: "\\?\Volume{GUID}\path\filename.ext".
- /// </summary>
- /// <param name="dosDevice">A DosDevicePath, for example: \Device\HarddiskVolumeX\path\filename.ext.</param>
- /// <returns>A translated dos path.</returns>
- [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Nt")]
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nt")]
- public static string GetVolumeGuidForNtDeviceName(string dosDevice)
- {
- return (from drive in Directory.EnumerateLogicalDrivesCore(false, false)
- where drive.DosDeviceName.Equals(dosDevice, StringComparison.OrdinalIgnoreCase)
- select drive.VolumeInfo.Guid).FirstOrDefault();
- }
-
- #endregion // GetVolumeGuidForNtDeviceName
-
- #region GetVolumeInfo
-
- /// <summary>Retrieves information about the file system and volume associated with the specified root file or directorystream.</summary>
- /// <param name="volumePath">A path that contains the root directory.</param>
- /// <returns>A <see cref="VolumeInfo"/> instance describing the volume associatied with the specified root directory.</returns>
- [SecurityCritical]
- public static VolumeInfo GetVolumeInfo(string volumePath)
- {
- return new VolumeInfo(volumePath, true, false);
- }
-
- /// <summary>Retrieves information about the file system and volume associated with the specified root file or directorystream.</summary>
- /// <param name="volumeHandle">An instance to a <see cref="SafeFileHandle"/> handle.</param>
- /// <returns>A <see cref="VolumeInfo"/> instance describing the volume associatied with the specified root directory.</returns>
- [SecurityCritical]
- public static VolumeInfo GetVolumeInfo(SafeFileHandle volumeHandle)
- {
- return new VolumeInfo(volumeHandle, true, true);
- }
-
- #endregion // GetVolumeInfo
-
- #region GetVolumeLabel
-
- /// <summary>Retrieve the label of a file system volume.</summary>
- /// <param name="volumePath">
- /// A path to a volume. For example: "C:\", "\\server\share", or "\\?\Volume{c0580d5e-2ad6-11dc-9924-806e6f6e6963}\".
- /// </param>
- /// <returns>
- /// The the label of the file system volume. This function can return <c>string.Empty</c> since a volume label is generally not
- /// mandatory.
- /// </returns>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [SecurityCritical]
- public static string GetVolumeLabel(string volumePath)
- {
- return new VolumeInfo(volumePath, true, true).Name;
- }
-
- #endregion // GetVolumeLabel
-
- #region GetVolumePathName
-
- /// <summary>Retrieves the volume mount point where the specified path is mounted.</summary>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="path">The path to the volume, for example: "C:\Windows".</param>
- /// <returns>
- /// <para>Returns the nearest volume root path for a given directory.</para>
- /// <para>The volume path name, for example: "C:\Windows" returns: "C:\".</para>
- /// </returns>
- [SecurityCritical]
- public static string GetVolumePathName(string path)
- {
- if (Utils.IsNullOrWhiteSpace(path))
- throw new ArgumentNullException("path");
-
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- {
- var volumeRootPath = new StringBuilder(NativeMethods.MaxPathUnicode / 32);
- var pathLp = Path.GetFullPathCore(null, path, GetFullPathOptions.AsLongPath | GetFullPathOptions.FullCheck);
-
- // GetVolumePathName()
- // In the ANSI version of this function, the name is limited to 248 characters.
- // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
- // 2013-07-18: MSDN does not confirm LongPath usage but a Unicode version of this function exists.
-
- var getOk = NativeMethods.GetVolumePathName(pathLp, volumeRootPath, (uint) volumeRootPath.Capacity);
- var lastError = Marshal.GetLastWin32Error();
-
- if (getOk)
- return Path.GetRegularPathCore(volumeRootPath.ToString(), GetFullPathOptions.None, false);
-
- switch ((uint) lastError)
- {
- // Don't throw exception on these errors.
- case Win32Errors.ERROR_NO_MORE_FILES:
- case Win32Errors.ERROR_INVALID_PARAMETER:
- case Win32Errors.ERROR_INVALID_NAME:
- break;
-
- default:
- NativeError.ThrowException(lastError, path);
- break;
- }
-
- // Return original path.
- return path;
- }
- }
-
- #endregion // GetVolumePathName
-
- #region IsSameVolume
-
- /// <summary>Determines whether the volume of two file system objects is the same.</summary>
- /// <param name="path1">The first filesystem ojbect with full path information.</param>
- /// <param name="path2">The second file system object with full path information.</param>
- /// <returns><see langword="true"/> if both filesytem objects reside on the same volume, <see langword="false"/> otherwise.</returns>
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [SecurityCritical]
- public static bool IsSameVolume(string path1, string path2)
- {
- try
- {
- var volInfo1 = new VolumeInfo(GetVolumePathName(path1), true, true);
- var volInfo2 = new VolumeInfo(GetVolumePathName(path2), true, true);
-
- return volInfo1.SerialNumber == volInfo2.SerialNumber;
- }
- catch { }
-
- return false;
- }
-
- #endregion // IsSameVolume
-
- #region IsVolume
-
- /// <summary>Determines whether the specified volume name is a defined volume on the current computer.</summary>
- /// <param name="volumeMountPoint">
- /// A path to a volume. For example: "C:\", "\\server\share", or "\\?\Volume{c0580d5e-2ad6-11dc-9924-806e6f6e6963}\".
- /// </param>
- /// <returns><see langword="true"/> on success, <see langword="false"/> otherwise.</returns>
- [SecurityCritical]
- public static bool IsVolume(string volumeMountPoint)
- {
- return !Utils.IsNullOrWhiteSpace(GetVolumeGuid(volumeMountPoint));
- }
-
- #endregion // IsVolume
-
- #region SetCurrentVolumeLabel
-
- /// <summary>Sets the label of the file system volume that is the root of the current directory.</summary>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="volumeName">A name for the volume.</param>
- [SecurityCritical]
- public static void SetCurrentVolumeLabel(string volumeName)
- {
- if (Utils.IsNullOrWhiteSpace(volumeName))
- throw new ArgumentNullException("volumeName");
-
- if (!NativeMethods.SetVolumeLabel(null, volumeName))
- Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
- }
-
- #endregion // SetCurrentVolumeLabel
-
- #region SetVolumeLabel
-
- /// <summary>Sets the label of a file system volume.</summary>
- /// <param name="volumePath">
- /// <para>A path to a volume. For example: "C:\", "\\server\share", or "\\?\Volume{c0580d5e-2ad6-11dc-9924-806e6f6e6963}\"</para>
- /// <para>If this parameter is <see langword="null"/>, the function uses the current drive.</para>
- /// </param>
- /// <param name="volumeName">
- /// <para>A name for the volume.</para>
- /// <para>If this parameter is <see langword="null"/>, the function deletes any existing label</para>
- /// <para>from the specified volume and does not assign a new label.</para>
- /// </param>
- [SecurityCritical]
- public static void SetVolumeLabel(string volumePath, string volumeName)
- {
- // rootPathName == null is allowed, means current drive.
-
- // Setting volume label only applies to Logical Drives pointing to local resources.
- //if (!Path.IsLocalPath(rootPathName))
- //return false;
-
- volumePath = Path.AddTrailingDirectorySeparator(volumePath, false);
-
- // NTFS uses a limit of 32 characters for the volume label as of Windows Server 2003.
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- if (!NativeMethods.SetVolumeLabel(volumePath, volumeName))
- NativeError.ThrowException(volumePath, volumeName);
- }
-
- #endregion // SetVolumeLabel
-
- #region SetVolumeMountPoint
-
- /// <summary>Associates a volume with a Drive letter or a directory on another volume.</summary>
- /// <exception cref="ArgumentNullException"/>
- /// <exception cref="ArgumentException"/>
- /// <param name="volumeMountPoint">
- /// The user-mode path to be associated with the volume. This may be a Drive letter (for example, "X:\")
- /// or a directory on another volume (for example, "Y:\MountX\").
- /// </param>
- /// <param name="volumeGuid">A <see cref="string"/> containing the volume <see cref="Guid"/>.</param>
- [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Utils.IsNullOrWhiteSpace validates arguments.")]
- [SecurityCritical]
- public static void SetVolumeMountPoint(string volumeMountPoint, string volumeGuid)
- {
- if (Utils.IsNullOrWhiteSpace(volumeMountPoint))
- throw new ArgumentNullException("volumeMountPoint");
-
- if (Utils.IsNullOrWhiteSpace(volumeGuid))
- throw new ArgumentNullException("volumeGuid");
-
- if (!volumeGuid.StartsWith(Path.VolumePrefix + "{", StringComparison.OrdinalIgnoreCase))
- throw new ArgumentException(Resources.Not_A_Valid_Guid, volumeGuid);
-
- volumeMountPoint = Path.GetFullPathCore(null, volumeMountPoint, GetFullPathOptions.AsLongPath | GetFullPathOptions.AddTrailingDirectorySeparator | GetFullPathOptions.FullCheck);
-
- // This string must be of the form "\\?\Volume{GUID}\"
- volumeGuid = Path.AddTrailingDirectorySeparator(volumeGuid, false);
-
-
- // ChangeErrorMode is for the Win32 SetThreadErrorMode() method, used to suppress possible pop-ups.
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
-
- // SetVolumeMountPoint()
- // In the ANSI version of this function, the name is limited to MAX_PATH characters.
- // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
- // 2014-01-29: MSDN does not confirm LongPath usage but a Unicode version of this function exists.
-
- if (!NativeMethods.SetVolumeMountPoint(volumeMountPoint, volumeGuid))
- {
- var lastError = Marshal.GetLastWin32Error();
-
- // If the lpszVolumeMountPoint parameter contains a path to a mounted folder,
- // GetLastError returns ERROR_DIR_NOT_EMPTY, even if the directory is empty.
-
- if (lastError != Win32Errors.ERROR_DIR_NOT_EMPTY)
- NativeError.ThrowException(lastError, volumeGuid);
- }
- }
-
- #endregion // SetVolumeMountPoint
-
- #endregion // Volume
-
-
- #region Internal Methods
-
- /// <summary>Defines, redefines, or deletes MS-DOS device names.</summary>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="isDefine">
- /// <see langword="true"/> defines a new MS-DOS device. <see langword="false"/> deletes a previously defined MS-DOS device.
- /// </param>
- /// <param name="deviceName">
- /// An MS-DOS device name string specifying the device the function is defining, redefining, or deleting.
- /// </param>
- /// <param name="targetPath">
- /// A pointer to a path string that will implement this device. The string is an MS-DOS path string unless the
- /// <see cref="DosDeviceAttributes.RawTargetPath"/> flag is specified, in which case this string is a path string.
- /// </param>
- /// <param name="deviceAttributes">
- /// The controllable aspects of the DefineDosDevice function, <see cref="DosDeviceAttributes"/> flags which will be combined with the
- /// default.
- /// </param>
- /// <param name="exactMatch">
- /// Only delete MS-DOS device on an exact name match. If <paramref name="exactMatch"/> is <see langword="true"/>,
- /// <paramref name="targetPath"/> must be the same path used to create the mapping.
- /// </param>
- ///
- /// <returns><see langword="true"/> on success, <see langword="false"/> otherwise.</returns>
- [SecurityCritical]
- internal static void DefineDosDeviceCore(bool isDefine, string deviceName, string targetPath, DosDeviceAttributes deviceAttributes, bool exactMatch)
- {
- if (Utils.IsNullOrWhiteSpace(deviceName))
- throw new ArgumentNullException("deviceName");
-
- if (isDefine)
- {
- // targetPath is allowed to be null.
-
- // In no case is a trailing backslash ("\") allowed.
- deviceName = Path.GetRegularPathCore(deviceName, GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.CheckInvalidPathChars, false);
-
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- if (!NativeMethods.DefineDosDevice(deviceAttributes, deviceName, targetPath))
- NativeError.ThrowException(deviceName, targetPath);
- }
- else
- {
- // A pointer to a path string that will implement this device.
- // The string is an MS-DOS path string unless the DDD_RAW_TARGET_PATH flag is specified, in which case this string is a path string.
-
- if (exactMatch && !Utils.IsNullOrWhiteSpace(targetPath))
- deviceAttributes = deviceAttributes | DosDeviceAttributes.ExactMatchOnRemove | DosDeviceAttributes.RawTargetPath;
-
- // Remove the MS-DOS device name. First, get the name of the Windows NT device
- // from the symbolic link and then delete the symbolic link from the namespace.
-
- DefineDosDevice(deviceName, targetPath, deviceAttributes);
- }
- }
-
- /// <summary>Deletes a Drive letter or mounted folder.</summary>
- /// <remarks>
- /// <para>It's not an error to attempt to unmount a volume from a volume mount point when there is no volume actually mounted at that volume mount point.</para>
- /// <para>Deleting a mounted folder does not cause the underlying directory to be deleted.</para>
- /// </remarks>
- /// <exception cref="ArgumentNullException"/>
- /// <param name="volumeMountPoint">The Drive letter or mounted folder to be deleted. For example, X:\ or Y:\MountX\.</param>
- /// <param name="continueOnException">
- /// <see langword="true"/> suppress any Exception that might be thrown as a result from a failure, such as unavailable resources.
- /// </param>
- /// <returns>If completed successfully returns <see cref="Win32Errors.ERROR_SUCCESS"/>, otherwise the last error number.</returns>
- [SecurityCritical]
- internal static int DeleteVolumeMountPointCore(string volumeMountPoint, bool continueOnException)
- {
- if (Utils.IsNullOrWhiteSpace(volumeMountPoint))
- throw new ArgumentNullException("volumeMountPoint");
-
- var lastError = (int) Win32Errors.ERROR_SUCCESS;
-
- using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
- {
- // DeleteVolumeMountPoint()
- // In the ANSI version of this function, the name is limited to MAX_PATH characters.
- // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
- // 2013-01-13: MSDN does not confirm LongPath usage but a Unicode version of this function exists.
-
- if (!NativeMethods.DeleteVolumeMountPoint(Path.AddTrailingDirectorySeparator(volumeMountPoint, false)))
- lastError = Marshal.GetLastWin32Error();
-
- if (lastError != Win32Errors.ERROR_SUCCESS && !continueOnException)
- {
- if (lastError == Win32Errors.ERROR_FILE_NOT_FOUND)
- lastError = (int) Win32Errors.ERROR_PATH_NOT_FOUND;
-
- NativeError.ThrowException(lastError, volumeMountPoint);
- }
- }
-
- return lastError;
- }
-
- #endregion // Internal Methods
- }
- }
|