Ignore:
Timestamp:
9/27/2008 4:55:32 AM (6 years ago)
Author:
lowjoel
Message:

-Always use Marshal.PtrToStringUni? since we are specifically using the Unicode functions
-Implement almost all the other members of System.IO.DriveInfo?, for drop-in replacement in Eraser
-Added the Volume.FromMountpoint? function to create a Volume object from the path to the mount point or Drive (since the constructor takes a volume ID, unlike the usual DriveInfo? function)
-Changed the Get functions to properties since we are a full class now

File:
1 edited

Legend:

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

    r403 r404  
    3636        /// Constructor. 
    3737        /// </summary> 
    38         /// <param name="volumeID">The ID of the volume.</param> 
     38        /// <param name="volumeID">The ID of the volume, in the form "\\?\Volume{GUID}\"</param> 
    3939        public Volume(string volumeID) 
    4040        { 
     
    5858                        pathNamesBuffer = Marshal.AllocHGlobal((int)(currentBufferSize * sizeof(char))); 
    5959                    } 
     60                    else 
     61                        throw new Win32Exception(Marshal.GetLastWin32Error(), 
     62                            "Eraser.Util.Volume.Volume"); 
    6063                } 
    6164 
    62                 pathNames = Marshal.PtrToStringAuto(pathNamesBuffer, (int)returnLength); 
     65                pathNames = Marshal.PtrToStringUni(pathNamesBuffer, (int)returnLength); 
    6366            } 
    6467            finally 
     
    8386                } 
    8487            } 
     88 
     89            //Fill up the remaining members of the structure: file system, label, etc. 
     90            IntPtr volumeName = Marshal.AllocHGlobal(MaxPath * sizeof(char)), 
     91                   fileSystemName = Marshal.AllocHGlobal(MaxPath * sizeof(char)); 
     92            try 
     93            { 
     94                uint serialNumber, maxComponentLength, filesystemFlags; 
     95                if (!GetVolumeInformation(volumeID, volumeName, MaxPath, out serialNumber, 
     96                    out maxComponentLength, out filesystemFlags, fileSystemName, MaxPath)) 
     97                { 
     98                    if (Marshal.GetLastWin32Error() != 21 /*ERROR_NOT_READY*/) 
     99                        throw new Win32Exception(Marshal.GetLastWin32Error(), "Eraser.Util.Volume.Volume"); 
     100                } 
     101                else 
     102                { 
     103                    //OK, the volume information's queried, store what we know. 
     104                    driveFormat = Marshal.PtrToStringUni(fileSystemName); 
     105                    volumeLabel = Marshal.PtrToStringUni(volumeName); 
     106                    isReady = true; 
     107                } 
     108            } 
     109            finally 
     110            { 
     111                Marshal.FreeHGlobal(volumeName); 
     112                Marshal.FreeHGlobal(fileSystemName); 
     113            } 
    85114        } 
    86115 
     
    93122        { 
    94123            List<Volume> result = new List<Volume>(); 
    95             IntPtr nextVolume = Marshal.AllocHGlobal(MaxPath * sizeof(char)); 
     124            IntPtr nextVolume = Marshal.AllocHGlobal(LongPath * sizeof(char)); 
    96125            try 
    97126            { 
    98                 SafeHandle handle = FindFirstVolume(nextVolume, MaxPath); 
     127                SafeHandle handle = FindFirstVolume(nextVolume, LongPath); 
    99128                if (handle.IsInvalid) 
    100129                    return result; 
     
    102131                //Iterate over the volume mountpoints 
    103132                do 
    104                     result.Add(new Volume(Marshal.PtrToStringAuto(nextVolume))); 
    105                 while (FindNextVolume(handle, nextVolume, MaxPath)); 
     133                    result.Add(new Volume(Marshal.PtrToStringUni(nextVolume))); 
     134                while (FindNextVolume(handle, nextVolume, LongPath)); 
    106135 
    107136                //Close the handle 
     
    118147 
    119148        /// <summary> 
     149        /// Creates a Volume object from its mountpoint. 
     150        /// </summary> 
     151        /// <param name="mountpoint">The path to the mountpoint.</param> 
     152        /// <returns>The volume object if such a volume exists, or an exception 
     153        /// is thrown.</returns> 
     154        public static Volume FromMountpoint(string mountpoint) 
     155        { 
     156            DirectoryInfo mountpointDir = new DirectoryInfo(mountpoint); 
     157            IntPtr volumeID = Marshal.AllocHGlobal(50 * sizeof(char)); 
     158            try 
     159            { 
     160                do 
     161                { 
     162                    string currentDir = mountpointDir.FullName; 
     163                    if (currentDir.Length > 0 && currentDir[currentDir.Length - 1] != '\\') 
     164                        currentDir += '\\'; 
     165                    if (GetVolumeNameForVolumeMountPoint(currentDir, volumeID, 50)) 
     166                        return new Volume(Marshal.PtrToStringUni(volumeID)); 
     167                    else if (Marshal.GetLastWin32Error() != 4390 /*ERROR_NOT_A_REPARSE_POINT*/) 
     168                        throw new Win32Exception(Marshal.GetLastWin32Error()); 
     169                    mountpointDir = mountpointDir.Parent; 
     170                } 
     171                while (mountpointDir != null); 
     172 
     173                throw new Win32Exception(4390 /*ERROR_NOT_A_REPARSE_POINT*/); 
     174            } 
     175            finally 
     176            { 
     177                Marshal.FreeHGlobal(volumeID); 
     178            } 
     179        } 
     180 
     181        /// <summary> 
     182        /// Returns the volume identifier as would be returned from FindFirstVolume. 
     183        /// </summary> 
     184        public string VolumeID 
     185        { 
     186            get { return volumeID; } 
     187        } 
     188        private string volumeID; 
     189 
     190        /// <summary> 
     191        /// Gets or sets the volume label of a drive. 
     192        /// </summary> 
     193        public string VolumeLabel 
     194        { 
     195            get { return VolumeLabel; } 
     196            set { throw new NotImplementedException();  } 
     197        } 
     198        private string volumeLabel; 
     199 
     200        /// <summary> 
     201        /// Gets the name of the file system, such as NTFS or FAT32. 
     202        /// </summary> 
     203        public string VolumeFormat 
     204        { 
     205            get 
     206            { 
     207                return driveFormat; 
     208            } 
     209        } 
     210        private string driveFormat; 
     211 
     212        /// <summary> 
     213        /// Gets the drive type; returns one of the System.IO.DriveType values. 
     214        /// </summary> 
     215        public DriveType VolumeType 
     216        { 
     217            get 
     218            { 
     219                return (DriveType)GetDriveType(VolumeID); 
     220            } 
     221        } 
     222 
     223        /// <summary> 
    120224        /// Determines the cluster size of the current volume. 
    121225        /// </summary> 
    122         /// <returns>The size of one cluster, in bytes.</returns> 
    123         public uint GetClusterSize() 
    124         { 
    125             UInt32 clusterSize, sectorSize, freeClusters, totalClusters; 
    126             if (GetDiskFreeSpace(mountPoints[0], out clusterSize, out sectorSize, 
    127                 out freeClusters, out totalClusters)) 
    128                 return clusterSize * sectorSize; 
    129  
    130             throw new Win32Exception(Marshal.GetLastWin32Error(), 
    131                 "Eraser.Util.Drive.GetClusterSize"); 
     226        public int ClusterSize 
     227        { 
     228            get 
     229            { 
     230                uint clusterSize, sectorSize, freeClusters, totalClusters; 
     231                if (GetDiskFreeSpace(volumeID, out clusterSize, out sectorSize, 
     232                    out freeClusters, out totalClusters)) 
     233                    return (int)(clusterSize * sectorSize); 
     234 
     235                throw new Win32Exception(Marshal.GetLastWin32Error(), 
     236                    "Eraser.Util.Drive.GetClusterSize"); 
     237            } 
    132238        } 
    133239 
     
    135241        /// Checks if the current user has disk quotas on the current volume. 
    136242        /// </summary> 
    137         /// <returns>True if quotas are in effect.</returns> 
    138         public bool HasQuota() 
    139         { 
    140             UInt64 freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes; 
    141             if (GetDiskFreeSpaceEx(VolumeID, out freeBytesAvailable, out totalNumberOfBytes, 
    142                 out totalNumberOfFreeBytes)) 
    143                 return totalNumberOfFreeBytes != freeBytesAvailable; 
    144  
    145             throw new Win32Exception(Marshal.GetLastWin32Error(), 
    146                 "Eraser.Util.Drive.HasQuota"); 
     243        public bool HasQuota 
     244        { 
     245            get 
     246            { 
     247                ulong freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes; 
     248                if (GetDiskFreeSpaceEx(volumeID, out freeBytesAvailable, out totalNumberOfBytes, 
     249                    out totalNumberOfFreeBytes)) 
     250                    return totalNumberOfFreeBytes != freeBytesAvailable; 
     251 
     252                throw new Win32Exception(Marshal.GetLastWin32Error(), 
     253                    "Eraser.Util.Drive.HasQuota"); 
     254            } 
     255        } 
     256 
     257        /// <summary> 
     258        /// Gets a value indicating whether a drive is ready. 
     259        /// </summary> 
     260        public bool IsReady 
     261        { 
     262            get { return isReady; } 
     263        } 
     264        private bool isReady = false; 
     265 
     266        /// <summary> 
     267        /// Gets the total amount of free space available on a drive. 
     268        /// </summary> 
     269        public long TotalFreeSpace 
     270        { 
     271            get 
     272            { 
     273                ulong result, dummy; 
     274                if (GetDiskFreeSpaceEx(volumeID, out dummy, out dummy, out result)) 
     275                    return (long)result; 
     276 
     277                throw new Win32Exception(Marshal.GetLastWin32Error(), 
     278                    "Eraser.Util.Drive.TotalFreeSpace"); 
     279            } 
     280        } 
     281         
     282        /// <summary> 
     283        /// Gets the total size of storage space on a drive. 
     284        /// </summary> 
     285        public long TotalSize 
     286        { 
     287            get 
     288            { 
     289                UInt64 result, dummy; 
     290                if (GetDiskFreeSpaceEx(volumeID, out dummy, out result, out dummy)) 
     291                    return (long)result; 
     292 
     293                throw new Win32Exception(Marshal.GetLastWin32Error(), 
     294                    "Eraser.Util.Drive.TotalSize"); 
     295            } 
     296        } 
     297 
     298        /// <summary> 
     299        /// Indicates the amount of available free space on a drive. 
     300        /// </summary> 
     301        public long AvailableFreeSpace 
     302        { 
     303            get 
     304            { 
     305                UInt64 result, dummy; 
     306                if (GetDiskFreeSpaceEx(volumeID, out result, out dummy, out dummy)) 
     307                    return (long)result; 
     308 
     309                throw new Win32Exception(Marshal.GetLastWin32Error(), 
     310                    "Eraser.Util.Drive.AvailableFreeSpace"); 
     311            } 
    147312        } 
    148313 
     
    151316        /// contains volume mountpoints. 
    152317        /// </summary> 
    153         /// <returns>A list containing Volume objects, representing each of the 
    154         /// volumes at the mountpoints.</returns> 
    155         public List<Volume> GetMountpoints() 
    156         { 
    157             List<Volume> result = new List<Volume>(); 
    158             IntPtr nextMountpoint = Marshal.AllocHGlobal(MaxPath * sizeof(char)); 
    159             try 
    160             { 
    161                 SafeHandle handle = FindFirstVolumeMountPoint(VolumeID, 
    162                     nextMountpoint, MaxPath); 
    163                 if (handle.IsInvalid) 
     318        public List<Volume> MountedVolumes 
     319        { 
     320            get 
     321            { 
     322                List<Volume> result = new List<Volume>(); 
     323                IntPtr nextMountpoint = Marshal.AllocHGlobal(LongPath * sizeof(char)); 
     324                try 
     325                { 
     326                    SafeHandle handle = FindFirstVolumeMountPoint(VolumeID, 
     327                        nextMountpoint, LongPath); 
     328                    if (handle.IsInvalid) 
     329                        return result; 
     330 
     331                    //Iterate over the volume mountpoints 
     332                    while (FindNextVolumeMountPoint(handle, nextMountpoint, LongPath)) 
     333                        result.Add(new Volume(Marshal.PtrToStringUni(nextMountpoint))); 
     334 
     335                    //Close the handle 
     336                    if (Marshal.GetLastWin32Error() == 18 /*ERROR_NO_MORE_FILES*/) 
     337                        FindVolumeMountPointClose(handle); 
     338 
    164339                    return result; 
    165  
    166                 //Iterate over the volume mountpoints 
    167                 while (FindNextVolumeMountPoint(handle, nextMountpoint, MaxPath)) 
    168                     result.Add(new Volume(Marshal.PtrToStringAuto(nextMountpoint))); 
    169  
    170                 //Close the handle 
    171                 if (Marshal.GetLastWin32Error() == 18 /*ERROR_NO_MORE_FILES*/) 
    172                     FindVolumeMountPointClose(handle); 
    173  
    174                 return result; 
    175             } 
    176             finally 
    177             { 
    178                 Marshal.FreeHGlobal(nextMountpoint); 
    179             } 
    180         } 
    181  
    182         /// <summary> 
    183         /// Returns the volume identifier as would be returned from FindFirstVolume. 
    184         /// </summary> 
    185         public string VolumeID 
    186         { 
    187             get { return volumeID; } 
    188         } 
    189         private string volumeID; 
     340                } 
     341                finally 
     342                { 
     343                    Marshal.FreeHGlobal(nextMountpoint); 
     344                } 
     345            } 
     346        } 
    190347 
    191348        /// <summary> 
     
    209366        } 
    210367 
    211         /// <summary> 
    212         /// Gets the drive type; returns one of the System.IO.DriveType values. 
    213         /// </summary> 
    214         public DriveType VolumeType 
    215         { 
    216             get 
    217             { 
    218                 return (DriveType)GetDriveType(VolumeID); 
    219             } 
    220         } 
    221  
    222         internal const int MaxPath = 32768; 
    223  
    224368        #region Windows API Functions 
     369        internal const int MaxPath = 260; 
     370        internal const int LongPath = 32768; 
     371 
     372        /// <summary> 
     373        /// Retrieves information about the file system and volume associated with 
     374        /// the specified root directory. 
     375        ///  
     376        /// To specify a handle when retrieving this information, use the 
     377        /// GetVolumeInformationByHandleW function. 
     378        ///  
     379        /// To retrieve the current compression state of a file or directory, use 
     380        /// FSCTL_GET_COMPRESSION. 
     381        /// </summary> 
     382        /// <param name="lpRootPathName">    A pointer to a string that contains 
     383        /// the root directory of the volume to be described. 
     384        ///  
     385        /// If this parameter is NULL, the root of the current directory is used. 
     386        /// A trailing backslash is required. For example, you specify 
     387        /// \\MyServer\MyShare as "\\MyServer\MyShare\", or the C drive as "C:\".</param> 
     388        /// <param name="lpVolumeNameBuffer">A pointer to a buffer that receives 
     389        /// the name of a specified volume. The maximum buffer size is MAX_PATH+1.</param> 
     390        /// <param name="nVolumeNameSize">The length of a volume name buffer, in 
     391        /// TCHARs. The maximum buffer size is MAX_PATH+1. 
     392        ///  
     393        /// This parameter is ignored if the volume name buffer is not supplied.</param> 
     394        /// <param name="lpVolumeSerialNumber">A pointer to a variable that receives 
     395        /// the volume serial number. 
     396        ///  
     397        /// This parameter can be NULL if the serial number is not required. 
     398        ///  
     399        /// This function returns the volume serial number that the operating system 
     400        /// assigns when a hard disk is formatted. To programmatically obtain the 
     401        /// hard disk's serial number that the manufacturer assigns, use the 
     402        /// Windows Management Instrumentation (WMI) Win32_PhysicalMedia property 
     403        /// SerialNumber.</param> 
     404        /// <param name="lpMaximumComponentLength">A pointer to a variable that 
     405        /// receives the maximum length, in TCHARs, of a file name component that 
     406        /// a specified file system supports. 
     407        ///  
     408        /// A file name component is the portion of a file name between backslashes. 
     409        ///  
     410        /// The value that is stored in the variable that *lpMaximumComponentLength 
     411        /// points to is used to indicate that a specified file system supports 
     412        /// long names. For example, for a FAT file system that supports long names, 
     413        /// the function stores the value 255, rather than the previous 8.3 indicator. 
     414        /// Long names can also be supported on systems that use the NTFS file system.</param> 
     415        /// <param name="lpFileSystemFlags">A pointer to a variable that receives 
     416        /// flags associated with the specified file system. 
     417        ///  
     418        /// This parameter can be one or more of the FS_FILE* flags. However, 
     419        /// FS_FILE_COMPRESSION and FS_VOL_IS_COMPRESSED are mutually exclusive.</param> 
     420        /// <param name="lpFileSystemNameBuffer">A pointer to a buffer that receives 
     421        /// the name of the file system, for example, the FAT file system or the 
     422        /// NTFS file system. The maximum buffer size is MAX_PATH+1.</param> 
     423        /// <param name="nFileSystemNameSize">The length of the file system name 
     424        /// buffer, in TCHARs. The maximum buffer size is MAX_PATH+1. 
     425        ///  
     426        /// This parameter is ignored if the file system name buffer is not supplied.</param> 
     427        /// <returns>If all the requested information is retrieved, the return value 
     428        /// is nonzero. 
     429        ///  
     430        ///  
     431        /// If not all the requested information is retrieved, the return value is 
     432        /// zero (0). To get extended error information, call GetLastError.</returns> 
     433        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "GetVolumeInformationW")] 
     434        [return: MarshalAs(UnmanagedType.Bool)] 
     435        internal static extern bool GetVolumeInformation( 
     436            [MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, 
     437            IntPtr lpVolumeNameBuffer, 
     438            uint nVolumeNameSize, 
     439            out uint lpVolumeSerialNumber, 
     440            out uint lpMaximumComponentLength, 
     441            out uint lpFileSystemFlags, 
     442            IntPtr lpFileSystemNameBuffer, 
     443            uint nFileSystemNameSize); 
     444 
    225445        /// <summary> 
    226446        /// Retrieves information about the specified disk, including the amount 
     
    256476        /// <returns>If the function succeeds, the return value is true. To get 
    257477        /// extended error information, call Marshal.GetLastWin32Error().</returns> 
    258         [DllImport("Kernel32.dll", SetLastError = true)] 
     478        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "GetDiskFreeSpaceW")] 
    259479        [return: MarshalAs(UnmanagedType.Bool)] 
    260480        internal static extern bool GetDiskFreeSpace( 
    261             [MarshalAs(UnmanagedType.LPStr)] string lpRootPathName, 
     481            [MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, 
    262482            out UInt32 lpSectorsPerCluster, out UInt32 lpBytesPerSector, 
    263483            out UInt32 lpNumberOfFreeClusters, out UInt32 lpTotalNumberOfClusters); 
    264484 
    265         [DllImport("Kernel32.dll", SetLastError = true)] 
     485        /// <summary> 
     486        /// Retrieves information about the amount of space that is available on 
     487        /// a disk volume, which is the total amount of space, the total amount 
     488        /// of free space, and the total amount of free space available to the 
     489        /// user that is associated with the calling thread. 
     490        /// </summary> 
     491        /// <param name="lpDirectoryName">A directory on the disk. 
     492        ///  
     493        /// If this parameter is NULL, the function uses the root of the current 
     494        /// disk. 
     495        ///  
     496        /// If this parameter is a UNC name, it must include a trailing backslash, 
     497        /// for example, "\\MyServer\MyShare\". 
     498        ///  
     499        /// This parameter does not have to specify the root directory on a disk. 
     500        /// The function accepts any directory on a disk. 
     501        ///  
     502        /// The calling application must have FILE_LIST_DIRECTORY access rights 
     503        /// for this directory.</param> 
     504        /// <param name="lpFreeBytesAvailable">A pointer to a variable that receives 
     505        /// the total number of free bytes on a disk that are available to the 
     506        /// user who is associated with the calling thread. 
     507        ///  
     508        /// This parameter can be NULL. 
     509        ///  
     510        /// If per-user quotas are being used, this value may be less than the 
     511        /// total number of free bytes on a disk.</param> 
     512        /// <param name="lpTotalNumberOfBytes">A pointer to a variable that receives 
     513        /// the total number of bytes on a disk that are available to the user who 
     514        /// is associated with the calling thread. 
     515        ///  
     516        /// This parameter can be NULL. 
     517        ///  
     518        /// If per-user quotas are being used, this value may be less than the 
     519        /// total number of bytes on a disk. 
     520        ///  
     521        /// To determine the total number of bytes on a disk or volume, use 
     522        /// IOCTL_DISK_GET_LENGTH_INFO.</param> 
     523        /// <param name="lpTotalNumberOfFreeBytes">A pointer to a variable that 
     524        /// receives the total number of free bytes on a disk. 
     525        ///  
     526        /// This parameter can be NULL.</param> 
     527        /// <returns>If the function succeeds, the return value is nonzero. 
     528        ///  
     529        /// If the function fails, the return value is zero (0). To get extended 
     530        /// error information, call GetLastError.</returns> 
     531        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "GetDiskFreeSpaceExW")] 
    266532        [return: MarshalAs(UnmanagedType.Bool)] 
    267533        internal static extern bool GetDiskFreeSpaceEx( 
    268             [MarshalAs(UnmanagedType.LPStr)] string lpDirectoryName, 
     534            [MarshalAs(UnmanagedType.LPWStr)] string lpDirectoryName, 
    269535            out UInt64 lpFreeBytesAvailable, 
    270536            out UInt64 lpTotalNumberOfBytes, 
     
    389655        [return: MarshalAs(UnmanagedType.Bool)] 
    390656        internal static extern bool FindVolumeMountPointClose(SafeHandle hFindVolumeMountPoint); 
     657 
     658        /// <summary> 
     659        /// Retrieves the unique volume name for the specified volume mount point or root directory. 
     660        /// </summary> 
     661        /// <param name="lpszVolumeMountPoint">The path of a volume mount point (with a trailing 
     662        /// backslash, "\") or a drive letter indicating a root directory (in the 
     663        /// form "D:\").</param> 
     664        /// <param name="lpszVolumeName">A pointer to a string that receives the 
     665        /// volume name. This name is a unique volume name of the form 
     666        /// "\\?\Volume{GUID}\" where GUID is the GUID that identifies the volume.</param> 
     667        /// <param name="cchBufferLength">The length of the output buffer, in TCHARs. 
     668        /// A reasonable size for the buffer to accommodate the largest possible 
     669        /// volume name is 50 characters.</param> 
     670        /// <returns>If the function succeeds, the return value is nonzero. 
     671        ///  
     672        /// If the function fails, the return value is zero. To get extended 
     673        /// error information, call GetLastError.</returns> 
     674        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "GetVolumeNameForVolumeMountPointW")] 
     675        [return: MarshalAs(UnmanagedType.Bool)] 
     676        internal static extern bool GetVolumeNameForVolumeMountPoint( 
     677            [MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeMountPoint, 
     678            IntPtr lpszVolumeName, 
     679            uint cchBufferLength); 
    391680 
    392681        /// <summary> 
Note: See TracChangeset for help on using the changeset viewer.