Changeset 401


Ignore:
Timestamp:
9/27/2008 3:25:45 AM (6 years ago)
Author:
lowjoel
Message:

Changed the Static drive class to work like the DriveInfo? class, only that this one works on the basis of volumes, not paths.

This implements support for non-drive mount points supported in Windows since Windows 2000.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/eraser6/Util/Drive.cs

    r348 r401  
    2727using System.ComponentModel; 
    2828using System.IO; 
     29using Microsoft.Win32.SafeHandles; 
    2930 
    3031namespace Eraser.Util 
    3132{ 
    32     public static class Drive 
     33    public class Volume 
    3334    { 
    3435        /// <summary> 
     36        /// Constructor. 
     37        /// </summary> 
     38        /// <param name="volumeID">The ID of the volume.</param> 
     39        public Volume(string volumeID) 
     40        { 
     41            //Set the volume Id 
     42            this.volumeID = volumeID; 
     43 
     44            //Get the paths of the said volume 
     45            IntPtr pathNamesBuffer = IntPtr.Zero; 
     46            string pathNames = string.Empty; 
     47            try 
     48            { 
     49                uint currentBufferSize = MaxPath; 
     50                uint returnLength = 0; 
     51                pathNamesBuffer = Marshal.AllocHGlobal((int)(currentBufferSize * sizeof(char))); 
     52                while (!GetVolumePathNamesForVolumeName(volumeID, pathNamesBuffer, currentBufferSize, 
     53                    out returnLength)) 
     54                { 
     55                    if (Marshal.GetLastWin32Error() == 234/*ERROR_MORE_DATA*/) 
     56                    { 
     57                        currentBufferSize *= 2; 
     58                        pathNamesBuffer = Marshal.AllocHGlobal((int)(currentBufferSize * sizeof(char))); 
     59                    } 
     60                } 
     61 
     62                pathNames = Marshal.PtrToStringAuto(pathNamesBuffer, (int)returnLength); 
     63            } 
     64            finally 
     65            { 
     66                if (pathNamesBuffer != IntPtr.Zero) 
     67                    Marshal.FreeHGlobal(pathNamesBuffer); 
     68            } 
     69 
     70            //OK, the marshalling is complete. Convert the pathNames string into a list 
     71            //of strings containing all of the volumes mountpoints; because the 
     72            //GetVolumePathNamesForVolumeName function returns a convoluted structure 
     73            //containing the path names. 
     74            for (int lastIndex = 0, i = 0; i != pathNames.Length; ++i) 
     75            { 
     76                if (pathNames[i] == '\0') 
     77                { 
     78                    mountPoints.Add(pathNames.Substring(lastIndex, i - lastIndex)); 
     79 
     80                    lastIndex = i + 1; 
     81                    if (pathNames[lastIndex] == '\0') 
     82                        break; 
     83                } 
     84            } 
     85        } 
     86 
     87        public static List<Volume> GetVolumes() 
     88        { 
     89            List<Volume> result = new List<Volume>(); 
     90            IntPtr nextVolume = Marshal.AllocHGlobal(MaxPath * sizeof(char)); 
     91            try 
     92            { 
     93                SafeHandle handle = FindFirstVolume(nextVolume, MaxPath); 
     94                if (handle.IsInvalid) 
     95                    return result; 
     96 
     97                //Iterate over the volume mountpoints 
     98                do 
     99                    result.Add(new Volume(Marshal.PtrToStringAuto(nextVolume))); 
     100                while (FindNextVolume(handle, nextVolume, MaxPath)); 
     101 
     102                //Close the handle 
     103                if (Marshal.GetLastWin32Error() == 18 /*ERROR_NO_MORE_FILES*/) 
     104                    FindVolumeClose(handle); 
     105                 
     106                return result; 
     107            } 
     108            finally 
     109            { 
     110                Marshal.FreeHGlobal(nextVolume); 
     111            } 
     112        } 
     113 
     114        /// <summary> 
    35115        /// Determines the cluster size of the given volume. 
    36116        /// </summary> 
    37         /// <param name="driveName">The path to the root of the drive, including 
    38         /// the trailing \</param> 
    39117        /// <returns>The size of one cluster, in bytes.</returns> 
    40         public static uint GetClusterSize(string driveName) 
    41         { 
    42             UInt32 clusterSize = 0, sectorSize, freeClusters, totalClusters; 
    43             if (GetDiskFreeSpace(driveName, out clusterSize, out sectorSize, 
     118        public uint GetClusterSize() 
     119        { 
     120            UInt32 clusterSize, sectorSize, freeClusters, totalClusters; 
     121            if (GetDiskFreeSpace(mountPoints[0], out clusterSize, out sectorSize, 
    44122                out freeClusters, out totalClusters)) 
    45123                return clusterSize * sectorSize; 
     
    52130        /// Checks if the current user has disk quotas on the volume provided. 
    53131        /// </summary> 
    54         /// <param name="driveName">The path to the root of the drive, including 
    55         /// the trailing \</param> 
    56132        /// <returns>True if quotas are in effect.</returns> 
    57         public static bool HasQuota(string driveName) 
    58         { 
    59             DriveInfo info = new DriveInfo(driveName); 
    60             return info.TotalFreeSpace != info.AvailableFreeSpace; 
    61         } 
    62  
     133        public bool HasQuota() 
     134        { 
     135            UInt64 freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes; 
     136            if (GetDiskFreeSpaceEx(VolumeID, out freeBytesAvailable, out totalNumberOfBytes, 
     137                out totalNumberOfFreeBytes)) 
     138                return totalNumberOfFreeBytes != freeBytesAvailable; 
     139 
     140            throw new Win32Exception(Marshal.GetLastWin32Error(), 
     141                "Eraser.Util.Drive.HasQuota"); 
     142        } 
     143 
     144        /// <summary> 
     145        /// Retrieves all mountpoints in the drive, if the drive contains volume 
     146        /// mountpoints. 
     147        /// </summary> 
     148        /// <returns>A list containing Volume objects, representing each of the 
     149        /// volumes at the mountpoints.</returns> 
     150        public List<Volume> GetMountpoints() 
     151        { 
     152            List<Volume> result = new List<Volume>(); 
     153            IntPtr nextMountpoint = Marshal.AllocHGlobal(MaxPath * sizeof(char)); 
     154            try 
     155            { 
     156                SafeHandle handle = FindFirstVolumeMountPoint(VolumeID, 
     157                    nextMountpoint, MaxPath); 
     158                if (handle.IsInvalid) 
     159                    return result; 
     160 
     161                //Iterate over the volume mountpoints 
     162                while (FindNextVolumeMountPoint(handle, nextMountpoint, MaxPath)) 
     163                    result.Add(new Volume(Marshal.PtrToStringAuto(nextMountpoint))); 
     164 
     165                //Close the handle 
     166                if (Marshal.GetLastWin32Error() == 18 /*ERROR_NO_MORE_FILES*/) 
     167                    FindVolumeMountPointClose(handle); 
     168 
     169                return result; 
     170            } 
     171            finally 
     172            { 
     173                Marshal.FreeHGlobal(nextMountpoint); 
     174            } 
     175        } 
     176 
     177        /// <summary> 
     178        /// Returns the volume identifier as would be returned from FindFirstVolume. 
     179        /// </summary> 
     180        public string VolumeID 
     181        { 
     182            get { return volumeID; } 
     183        } 
     184        private string volumeID; 
     185 
     186        /// <summary> 
     187        /// The various mountpoints to the root of the volume. This list contains 
     188        /// paths which may be a drive or a mountpoint. Every string includes the 
     189        /// trailing backslash. 
     190        /// </summary> 
     191        public List<string> MountPoints 
     192        { 
     193            get { return mountPoints; } 
     194            set { mountPoints = value; } 
     195        } 
     196        private List<string> mountPoints = new List<string>(); 
     197 
     198        /// <summary> 
     199        /// Gets whether the current volume is mounted at any place. 
     200        /// </summary> 
     201        public bool IsMounted 
     202        { 
     203            get { return mountPoints.Count != 0; } 
     204        } 
     205 
     206        /// <summary> 
     207        /// Gets the drive type; returns one of the System.IO.DriveType values. 
     208        /// </summary> 
     209        public DriveType VolumeType 
     210        { 
     211            get 
     212            { 
     213                return (DriveType)GetDriveType(mountPoints[0]); 
     214            } 
     215        } 
     216 
     217        internal const int MaxPath = 32768; 
     218 
     219        #region Windows API Functions 
    63220        /// <summary> 
    64221        /// Retrieves information about the specified disk, including the amount 
     
    97254        [return: MarshalAs(UnmanagedType.Bool)] 
    98255        internal static extern bool GetDiskFreeSpace( 
    99             [MarshalAs(UnmanagedType.LPStr)]string lpRootPathName, 
     256            [MarshalAs(UnmanagedType.LPStr)] string lpRootPathName, 
    100257            out UInt32 lpSectorsPerCluster, out UInt32 lpBytesPerSector, 
    101258            out UInt32 lpNumberOfFreeClusters, out UInt32 lpTotalNumberOfClusters); 
     259 
     260        [DllImport("Kernel32.dll", SetLastError = true)] 
     261        [return: MarshalAs(UnmanagedType.Bool)] 
     262        internal static extern bool GetDiskFreeSpaceEx( 
     263            [MarshalAs(UnmanagedType.LPStr)] string lpDirectoryName, 
     264            out UInt64 lpFreeBytesAvailable, 
     265            out UInt64 lpTotalNumberOfBytes, 
     266            out UInt64 lpTotalNumberOfFreeBytes); 
     267 
     268        /// <summary> 
     269        /// Retrieves the name of a volume on a computer. FindFirstVolume is used 
     270        /// to begin scanning the volumes of a computer. 
     271        /// </summary> 
     272        /// <param name="lpszVolumeName">A pointer to a buffer that receives a 
     273        /// null-terminated string that specifies the unique volume name of the 
     274        /// first volume found.</param> 
     275        /// <param name="cchBufferLength">The length of the buffer to receive the 
     276        /// name, in TCHARs.</param> 
     277        /// <returns>If the function succeeds, the return value is a search handle 
     278        /// used in a subsequent call to the FindNextVolume and FindVolumeClose 
     279        /// functions. 
     280        ///  
     281        /// If the function fails to find any volumes, the return value is the 
     282        /// INVALID_HANDLE_VALUE error code. To get extended error information, 
     283        /// call GetLastError.</returns> 
     284        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "FindFirstVolumeW")] 
     285        internal static extern SafeFileHandle FindFirstVolume( 
     286            IntPtr lpszVolumeName, 
     287            uint cchBufferLength); 
     288 
     289        /// <summary> 
     290        /// Continues a volume search started by a call to the FindFirstVolume 
     291        /// function. FindNextVolume finds one volume per call. 
     292        /// </summary> 
     293        /// <param name="hFindVolume">The volume search handle returned by a previous 
     294        /// call to the FindFirstVolume function.</param> 
     295        /// <param name="lpszVolumeName">A pointer to a string that receives the 
     296        /// unique volume name found.</param> 
     297        /// <param name="cchBufferLength">The length of the buffer that receives 
     298        /// the name, in TCHARs.</param> 
     299        /// <returns>If the function succeeds, the return value is nonzero. 
     300        ///  
     301        /// If the function fails, the return value is zero. To get extended error 
     302        /// information, call GetLastError. If no matching files can be found, the 
     303        /// GetLastError function returns the ERROR_NO_MORE_FILES error code. In 
     304        /// that case, close the search with the FindVolumeClose function.</returns> 
     305        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "FindNextVolumeW")] 
     306        [return: MarshalAs(UnmanagedType.Bool)] 
     307        internal static extern bool FindNextVolume(SafeHandle hFindVolume, 
     308            IntPtr lpszVolumeName, uint cchBufferLength); 
     309 
     310        /// <summary> 
     311        /// Closes the specified volume search handle. The FindFirstVolume and 
     312        /// FindNextVolume functions use this search handle to locate volumes. 
     313        /// </summary> 
     314        /// <param name="hFindVolume">The volume search handle to be closed. This 
     315        /// handle must have been previously opened by the FindFirstVolume function.</param> 
     316        /// <returns>If the function succeeds, the return value is nonzero. 
     317        ///  
     318        /// If the function fails, the return value is zero. To get extended error 
     319        /// information, call GetLastError.</returns> 
     320        [DllImport("Kernel32.dll", SetLastError = true)] 
     321        [return: MarshalAs(UnmanagedType.Bool)] 
     322        internal static extern bool FindVolumeClose(SafeHandle hFindVolume); 
     323 
     324        /// <summary> 
     325        /// Retrieves the name of a volume mount point on the specified volume. 
     326        /// FindFirstVolumeMountPoint is used to begin scanning the volume mount 
     327        /// points on a volume. 
     328        /// </summary> 
     329        /// <param name="lpszRootPathName">The unique volume name of the volume 
     330        /// to scan for volume mount points. A trailing backslash is required.</param> 
     331        /// <param name="lpszVolumeMountPoint">A pointer to a buffer that receives 
     332        /// the name of the first volume mount point found.</param> 
     333        /// <param name="cchBufferLength">The length of the buffer that receives 
     334        /// the volume mount point name, in TCHARs.</param> 
     335        /// <returns>If the function succeeds, the return value is a search handle 
     336        /// used in a subsequent call to the FindNextVolumeMountPoint and 
     337        /// FindVolumeMountPointClose functions. 
     338        ///  
     339        /// If the function fails to find a volume mount point on the volume, the 
     340        /// return value is the INVALID_HANDLE_VALUE error code. To get extended 
     341        /// error information, call GetLastError.</returns> 
     342        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "FindFirstVolumeMountPointW")] 
     343        internal static extern SafeFileHandle FindFirstVolumeMountPoint( 
     344            [MarshalAs(UnmanagedType.LPTStr)] string lpszRootPathName, 
     345            IntPtr lpszVolumeMountPoint, 
     346            uint cchBufferLength); 
     347 
     348        /// <summary> 
     349        /// Continues a volume mount point search started by a call to the 
     350        /// FindFirstVolumeMountPoint function. FindNextVolumeMountPoint finds one 
     351        /// volume mount point per call. 
     352        /// </summary> 
     353        /// <param name="hFindVolumeMountPoint">A mount-point search handle returned 
     354        /// by a previous call to the FindFirstVolumeMountPoint function.</param> 
     355        /// <param name="lpszVolumeMountPoint">A pointer to a buffer that receives 
     356        /// the name of the volume mount point found.</param> 
     357        /// <param name="cchBufferLength">The length of the buffer that receives 
     358        /// the names, in TCHARs.</param> 
     359        /// <returns>If the function succeeds, the return value is nonzero. 
     360        ///  
     361        /// If the function fails, the return value is zero. To get extended error 
     362        /// information, call GetLastError. If no matching files can be found, the 
     363        /// GetLastError function returns the ERROR_NO_MORE_FILES error code. In 
     364        /// that case, close the search with the FindVolumeMountPointClose function.</returns> 
     365        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "FindNextVolumeMountPointW")] 
     366        [return: MarshalAs(UnmanagedType.Bool)] 
     367        internal static extern bool FindNextVolumeMountPoint(SafeHandle hFindVolumeMountPoint, 
     368            IntPtr lpszVolumeMountPoint, 
     369            uint cchBufferLength); 
     370 
     371        /// <summary> 
     372        /// Closes the specified mount-point search handle. The FindFirstVolumeMountPoint 
     373        /// and FindNextVolumeMountPoint  functions use this search handle to locate 
     374        /// volume mount points on a specified volume. 
     375        /// </summary> 
     376        /// <param name="hFindVolumeMountPoint">The mount-point search handle to 
     377        /// be closed. This handle must have been previously opened by the 
     378        /// FindFirstVolumeMountPoint function.</param> 
     379        /// <returns>If the function succeeds, the return value is nonzero. 
     380        /// 
     381        /// If the function fails, the return value is zero. To get extended error 
     382        /// information, call GetLastError.</returns> 
     383        [DllImport("Kernel32.dll", SetLastError = true)] 
     384        [return: MarshalAs(UnmanagedType.Bool)] 
     385        internal static extern bool FindVolumeMountPointClose(SafeHandle hFindVolumeMountPoint); 
     386 
     387        /// <summary> 
     388        /// Retrieves a list of path names for the specified volume name. 
     389        /// </summary> 
     390        /// <param name="lpszVolumeName">The volume name.</param> 
     391        /// <param name="lpszVolumePathNames">A pointer to a buffer that receives 
     392        /// the list of volume path names. The list is an array of null-terminated 
     393        /// strings terminated by an additional NULL character. If the buffer is 
     394        /// not large enough to hold the complete list, the buffer holds as much 
     395        /// of the list as possible.</param> 
     396        /// <param name="cchBufferLength">The length of the lpszVolumePathNames 
     397        /// buffer, in TCHARs.</param> 
     398        /// <param name="lpcchReturnLength">If the call is successful, this parameter 
     399        /// is the number of TCHARs copied to the lpszVolumePathNames buffer. Otherwise, 
     400        /// this parameter is the size of the buffer required to hold the complete 
     401        /// list, in TCHARs.</param> 
     402        /// <returns></returns> 
     403        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "GetVolumePathNamesForVolumeNameW")] 
     404        internal static extern bool GetVolumePathNamesForVolumeName( 
     405            [MarshalAs(UnmanagedType.LPTStr)] string lpszVolumeName, 
     406            IntPtr lpszVolumePathNames, 
     407            uint cchBufferLength, 
     408            out uint lpcchReturnLength); 
     409 
     410        /// <summary> 
     411        /// Determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, 
     412        /// or network drive. 
     413        /// </summary> 
     414        /// <param name="lpRootPathName">The root directory for the drive. 
     415        ///  
     416        /// A trailing backslash is required. If this parameter is NULL, the function 
     417        /// uses the root of the current directory.</param> 
     418        /// <returns>The return value specifies the type of drive, which can be 
     419        /// one of the DriveInfo.DriveType values.</returns> 
     420        [DllImport("Kernel32.dll", SetLastError = true)] 
     421        internal static extern uint GetDriveType( 
     422            [MarshalAs(UnmanagedType.LPTStr)] string lpRootPathName); 
     423        #endregion 
    102424    } 
    103425} 
Note: See TracChangeset for help on using the changeset viewer.