source: branches/eraser6/CodeReview/Eraser.Util/VolumeInfo.cs @ 1705

Revision 1705, 22.3 KB checked in by lowjoel, 4 years ago (diff)

Replace all Marshal.GetExceptionForHR with Win32ErrorCode.GetExceptionForWin32Error since we deal with Win32 errors not unlike COM errors, except with a few exceptions and that we should be throwing Win32Exception instead of COMException.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25
26using System.Runtime.InteropServices;
27using System.ComponentModel;
28using System.IO;
29using Microsoft.Win32.SafeHandles;
30using System.Collections.ObjectModel;
31
32namespace Eraser.Util
33{
34    public class VolumeInfo
35    {
36        /// <summary>
37        /// Constructor.
38        /// </summary>
39        /// <param name="volumeId">The ID of the volume, either in the form
40        /// "\\?\Volume{GUID}\" or as a valid UNC path.</param>
41        public VolumeInfo(string volumeId)
42        {
43            //We only accept UNC paths as well as volume identifiers.
44            if (!(volumeId.StartsWith("\\\\?\\") || volumeId.StartsWith("\\\\")))
45                throw new ArgumentException("The volumeId parameter only accepts volume GUID " +
46                    "and UNC paths", "volumeId");
47
48            //Verify that the path ends with a trailing backslash
49            if (!volumeId.EndsWith("\\"))
50                throw new ArgumentException("The volumeId parameter must end with a trailing " +
51                    "backslash.", "volumeId");
52
53            //Set the volume ID
54            VolumeId = volumeId;
55
56            //Fill up the remaining members of the structure: file system, label, etc.
57            StringBuilder volumeName = new StringBuilder(NativeMethods.MaxPath * sizeof(char)),
58                fileSystemName = new StringBuilder(NativeMethods.MaxPath * sizeof(char));
59            uint serialNumber, maxComponentLength, filesystemFlags;
60            if (!NativeMethods.GetVolumeInformation(volumeId, volumeName, NativeMethods.MaxPath,
61                out serialNumber, out maxComponentLength, out filesystemFlags, fileSystemName,
62                NativeMethods.MaxPath))
63            {
64                int lastError = Marshal.GetLastWin32Error();
65                switch (lastError)
66                {
67                    case Win32ErrorCode.Success:
68                    case Win32ErrorCode.NotReady:
69                    case Win32ErrorCode.InvalidParameter:   //when the volume given is not mounted.
70                    case Win32ErrorCode.UnrecognizedVolume:
71                        break;
72
73                    default:
74                        throw Win32ErrorCode.GetExceptionForWin32Error(lastError);
75                }
76            }
77            else
78            {
79                IsReady = true;
80                VolumeLabel = volumeName.ToString();
81                VolumeFormat = fileSystemName.ToString();
82
83                //Determine whether it is FAT12 or FAT16
84                if (VolumeFormat == "FAT")
85                {
86                    uint clusterSize, sectorSize, freeClusters, totalClusters;
87                    if (NativeMethods.GetDiskFreeSpace(VolumeId, out clusterSize,
88                        out sectorSize, out freeClusters, out totalClusters))
89                    {
90                        if (totalClusters <= 0xFF0)
91                            VolumeFormat += "12";
92                        else
93                            VolumeFormat += "16";
94                    }
95                }
96            }
97        }
98
99        /// <summary>
100        /// Gets the mountpoints associated with the current volume.
101        /// </summary>
102        /// <returns>A list of volume mount points for the current volume.</returns>
103        private List<string> GetLocalVolumeMountPoints()
104        {
105            List<string> result = new List<string>();
106
107            //Get the paths of the said volume
108            string pathNames;
109            {
110                uint returnLength = 0;
111                StringBuilder pathNamesBuffer = new StringBuilder();
112                pathNamesBuffer.EnsureCapacity(NativeMethods.MaxPath);
113                while (!NativeMethods.GetVolumePathNamesForVolumeName(VolumeId,
114                    pathNamesBuffer, (uint)pathNamesBuffer.Capacity, out returnLength))
115                {
116                    int errorCode = Marshal.GetLastWin32Error();
117                    switch (errorCode)
118                    {
119                        case Win32ErrorCode.MoreData:
120                            pathNamesBuffer.EnsureCapacity((int)returnLength);
121                            break;
122                        default:
123                            throw Win32ErrorCode.GetExceptionForWin32Error(errorCode);
124                    }
125                }
126
127                if (pathNamesBuffer.Length < returnLength)
128                    pathNamesBuffer.Length = (int)returnLength;
129                pathNames = pathNamesBuffer.ToString().Substring(0, (int)returnLength);
130            }
131
132            //OK, the marshalling is complete. Convert the pathNames string into a list
133            //of strings containing all of the volumes mountpoints; because the
134            //GetVolumePathNamesForVolumeName function returns a convoluted structure
135            //containing the path names.
136            for (int lastIndex = 0, i = 0; i != pathNames.Length; ++i)
137            {
138                if (pathNames[i] == '\0')
139                {
140                    //If there are no mount points for this volume, the string will only
141                    //have one NULL
142                    if (i - lastIndex == 0)
143                        break;
144
145                    result.Add(pathNames.Substring(lastIndex, i - lastIndex));
146
147                    lastIndex = i + 1;
148                    if (pathNames[lastIndex] == '\0')
149                        break;
150                }
151            }
152
153            return result;
154        }
155
156        /// <summary>
157        /// Gets the mountpoints associated with the network share.
158        /// </summary>
159        /// <returns>A list of network mount points for the given network share.</returns>
160        private List<string> GetNetworkMountPoints()
161        {
162            List<string> result = new List<string>();
163
164            //Open an enumeration handle to list mount points.
165            IntPtr enumHandle;
166            uint errorCode = NativeMethods.WNetOpenEnum(NativeMethods.RESOURCE_CONNECTED,
167                NativeMethods.RESOURCETYPE_DISK, 0, IntPtr.Zero, out enumHandle);
168            if (errorCode != Win32ErrorCode.Success)
169                throw new Win32Exception((int)errorCode);
170
171            try
172            {
173                int resultBufferCount = 32;
174                int resultBufferSize = resultBufferCount *
175                    Marshal.SizeOf(typeof(NativeMethods.NETRESOURCE));
176                IntPtr resultBuffer = Marshal.AllocHGlobal(resultBufferSize);
177
178                try
179                {
180                    for ( ; ; )
181                    {
182                        uint resultBufferStored = (uint)resultBufferCount;
183                        uint resultBufferRequiredSize = (uint)resultBufferSize;
184                        errorCode = NativeMethods.WNetEnumResource(enumHandle,
185                            ref resultBufferStored, resultBuffer,
186                            ref resultBufferRequiredSize);
187
188                        if (errorCode == Win32ErrorCode.NoMoreItems)
189                            break;
190                        else if (errorCode != Win32ErrorCode.Success)
191                            throw new Win32Exception((int)errorCode);
192
193                        unsafe
194                        {
195                            //Marshal the memory block to managed structures.
196                            byte* pointer = (byte*)resultBuffer.ToPointer();
197
198                            for (uint i = 0; i < resultBufferStored;
199                                ++i, pointer += Marshal.SizeOf(typeof(NativeMethods.NETRESOURCE)))
200                            {
201                                NativeMethods.NETRESOURCE resource =
202                                    (NativeMethods.NETRESOURCE)Marshal.PtrToStructure(
203                                        (IntPtr)pointer, typeof(NativeMethods.NETRESOURCE));
204
205                                //Ensure that the path in the resource structure ends with a trailing
206                                //backslash as out volume ID ends with one.
207                                if (string.IsNullOrEmpty(resource.lpRemoteName))
208                                    continue;
209                                if (resource.lpRemoteName[resource.lpRemoteName.Length - 1] != '\\')
210                                    resource.lpRemoteName += '\\';
211
212                                if (resource.lpRemoteName == VolumeId)
213                                    result.Add(resource.lpLocalName);
214                            }
215                        }
216                    }
217                }
218                finally
219                {
220                    Marshal.FreeHGlobal(resultBuffer);
221                }
222            }
223            finally
224            {
225                NativeMethods.WNetCloseEnum(enumHandle);
226            }
227
228            return result;
229        }
230
231        /// <summary>
232        /// Lists all the volumes in the system.
233        /// </summary>
234        /// <returns>Returns a list of volumes representing all volumes present in
235        /// the system.</returns>
236        public static IList<VolumeInfo> Volumes
237        {
238            get
239            {
240                List<VolumeInfo> result = new List<VolumeInfo>();
241                StringBuilder nextVolume = new StringBuilder(NativeMethods.LongPath * sizeof(char));
242                SafeHandle handle = NativeMethods.FindFirstVolume(nextVolume, NativeMethods.LongPath);
243                if (handle.IsInvalid)
244                    return result;
245
246                try
247                {
248                    //Iterate over the volume mountpoints
249                    do
250                        result.Add(new VolumeInfo(nextVolume.ToString()));
251                    while (NativeMethods.FindNextVolume(handle, nextVolume, NativeMethods.LongPath));
252                }
253                finally
254                {
255                    //Close the handle
256                    NativeMethods.FindVolumeClose(handle);
257                }
258
259                return result.AsReadOnly();
260            }
261        }
262
263        /// <summary>
264        /// Creates a Volume object from its mountpoint.
265        /// </summary>
266        /// <param name="mountPoint">The path to the mountpoint.</param>
267        /// <returns>The volume object if such a volume exists, or an exception
268        /// is thrown.</returns>
269        public static VolumeInfo FromMountPoint(string mountPoint)
270        {
271            //Verify that the mountpoint given exists; if it doesn't we'll raise
272            //a DirectoryNotFound exception.
273            DirectoryInfo mountpointDir = new DirectoryInfo(mountPoint);
274            if (!mountpointDir.Exists)
275                throw new DirectoryNotFoundException();
276
277            do
278            {
279                //Ensure that the current path has a trailing backslash
280                string currentDir = mountpointDir.FullName;
281                if (currentDir.Length > 0 && currentDir[currentDir.Length - 1] != '\\')
282                    currentDir += '\\';
283
284                //The path cannot be empty.
285                if (string.IsNullOrEmpty(currentDir))
286                    throw new DirectoryNotFoundException();
287
288                //Get the type of the drive
289                DriveType driveType = (DriveType)NativeMethods.GetDriveType(currentDir);
290
291                //We do different things for different kinds of drives. Network drives
292                //will need us to resolve the drive to a UNC path. Local drives will
293                //be resolved to a volume GUID
294                StringBuilder volumeID = new StringBuilder(NativeMethods.MaxPath);
295                if (driveType == DriveType.Network)
296                {
297                    //Resolve the mountpoint to a UNC path
298                    uint bufferCapacity = (uint)volumeID.Capacity;
299                    uint errorCode = NativeMethods.WNetGetConnection(
300                        currentDir.Substring(0, currentDir.Length - 1),
301                        volumeID, ref bufferCapacity);
302
303                    switch (errorCode)
304                    {
305                        case Win32ErrorCode.Success:
306                            return new VolumeInfo(volumeID.ToString() + '\\');
307
308                        case Win32ErrorCode.BadDevice: //Path is not a network share
309                            break;
310
311                        default:
312                            throw new Win32Exception((int)errorCode);
313                    }
314                }
315                else
316                {
317                    if (!NativeMethods.GetVolumeNameForVolumeMountPoint(currentDir, volumeID, 50))
318                    {
319                        int errorCode = Marshal.GetLastWin32Error();
320                        switch (errorCode)
321                        {
322                            case Win32ErrorCode.InvalidFunction:
323                            case Win32ErrorCode.FileNotFound:
324                            case Win32ErrorCode.PathNotFound:
325                            case Win32ErrorCode.NotAReparsePoint:
326                                break;
327                            default:
328                                throw Win32ErrorCode.GetExceptionForWin32Error(
329                                    Marshal.GetLastWin32Error());
330                        }
331                    }
332                    else
333                    {
334                        return new VolumeInfo(volumeID.ToString());
335                    }
336                }
337
338                mountpointDir = mountpointDir.Parent;
339            }
340            while (mountpointDir != null);
341
342            throw Win32ErrorCode.GetExceptionForWin32Error(Win32ErrorCode.NotAReparsePoint);
343        }
344
345        /// <summary>
346        /// Returns the volume identifier as would be returned from FindFirstVolume.
347        /// </summary>
348        public string VolumeId { get; private set; }
349
350        /// <summary>
351        /// Gets or sets the volume label of a drive.
352        /// </summary>
353        public string VolumeLabel { get; private set; }
354
355        /// <summary>
356        /// Gets the name of the file system, such as NTFS or FAT32.
357        /// </summary>
358        public string VolumeFormat { get; private set; }
359
360        /// <summary>
361        /// Gets the drive type; returns one of the System.IO.DriveType values.
362        /// </summary>
363        public DriveType VolumeType
364        {
365            get
366            {
367                return (DriveType)NativeMethods.GetDriveType(VolumeId);
368            }
369        }
370
371        /// <summary>
372        /// Determines the cluster size of the current volume.
373        /// </summary>
374        public int ClusterSize
375        {
376            get
377            {
378                uint clusterSize, sectorSize, freeClusters, totalClusters;
379                if (NativeMethods.GetDiskFreeSpace(VolumeId, out clusterSize,
380                    out sectorSize, out freeClusters, out totalClusters))
381                {
382                    return (int)(clusterSize * sectorSize);
383                }
384
385                throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
386            }
387        }
388
389        /// <summary>
390        /// Determines the sector size of the current volume.
391        /// </summary>
392        public int SectorSize
393        {
394            get
395            {
396                uint clusterSize, sectorSize, freeClusters, totalClusters;
397                if (NativeMethods.GetDiskFreeSpace(VolumeId, out clusterSize,
398                    out sectorSize, out freeClusters, out totalClusters))
399                {
400                    return (int)sectorSize;
401                }
402
403                throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
404            }
405        }
406
407        /// <summary>
408        /// Checks if the current user has disk quotas on the current volume.
409        /// </summary>
410        public bool HasQuota
411        {
412            get
413            {
414                ulong freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes;
415                if (NativeMethods.GetDiskFreeSpaceEx(VolumeId, out freeBytesAvailable,
416                    out totalNumberOfBytes, out totalNumberOfFreeBytes))
417                {
418                    return totalNumberOfFreeBytes != freeBytesAvailable;
419                }
420                else if (Marshal.GetLastWin32Error() == Win32ErrorCode.NotReady)
421                {
422                    //For the lack of more appropriate responses.
423                    return false;
424                }
425
426                throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
427            }
428        }
429
430        /// <summary>
431        /// Gets a value indicating whether a drive is ready.
432        /// </summary>
433        public bool IsReady { get; private set; }
434
435        /// <summary>
436        /// Gets the total amount of free space available on a drive.
437        /// </summary>
438        public long TotalFreeSpace
439        {
440            get
441            {
442                ulong result, dummy;
443                if (NativeMethods.GetDiskFreeSpaceEx(VolumeId, out dummy, out dummy, out result))
444                {
445                    return (long)result;
446                }
447
448                throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
449            }
450        }
451       
452        /// <summary>
453        /// Gets the total size of storage space on a drive.
454        /// </summary>
455        public long TotalSize
456        {
457            get
458            {
459                ulong result, dummy;
460                if (NativeMethods.GetDiskFreeSpaceEx(VolumeId, out dummy, out result, out dummy))
461                {
462                    return (long)result;
463                }
464
465                throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
466            }
467        }
468
469        /// <summary>
470        /// Indicates the amount of available free space on a drive.
471        /// </summary>
472        public long AvailableFreeSpace
473        {
474            get
475            {
476                ulong result, dummy;
477                if (NativeMethods.GetDiskFreeSpaceEx(VolumeId, out result, out dummy, out dummy))
478                {
479                    return (long)result;
480                }
481
482                throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
483            }
484        }
485
486        /// <summary>
487        /// Retrieves all mountpoints in the current volume, if the current volume
488        /// contains volume mountpoints.
489        /// </summary>
490        public IList<VolumeInfo> MountedVolumes
491        {
492            get
493            {
494                List<VolumeInfo> result = new List<VolumeInfo>();
495                StringBuilder nextMountpoint = new StringBuilder(NativeMethods.LongPath * sizeof(char));
496
497                SafeHandle handle = NativeMethods.FindFirstVolumeMountPoint(VolumeId,
498                    nextMountpoint, NativeMethods.LongPath);
499                if (handle.IsInvalid)
500                    return result;
501
502                try
503                {
504                    //Iterate over the volume mountpoints
505                    while (NativeMethods.FindNextVolumeMountPoint(handle, nextMountpoint,
506                        NativeMethods.LongPath))
507                    {
508                        result.Add(new VolumeInfo(nextMountpoint.ToString()));
509                    }
510                }
511                finally
512                {
513                    //Close the handle
514                    NativeMethods.FindVolumeMountPointClose(handle);
515                }
516
517                return result.AsReadOnly();
518            }
519        }
520
521        /// <summary>
522        /// The various mountpoints to the root of the volume. This list contains
523        /// paths which may be a drive or a mountpoint. Every string includes the
524        /// trailing backslash.
525        /// </summary>
526        public ReadOnlyCollection<string> MountPoints
527        {
528            get
529            {
530                return (VolumeType == DriveType.Network ?
531                    GetNetworkMountPoints() : GetLocalVolumeMountPoints()).AsReadOnly();
532            }
533        }
534
535        /// <summary>
536        /// Gets whether the current volume is mounted at any place.
537        /// </summary>
538        public bool IsMounted
539        {
540            get { return MountPoints.Count != 0; }
541        }
542
543        /// <summary>
544        /// Opens a file with read, write, or read/write access.
545        /// </summary>
546        /// <param name="access">A System.IO.FileAccess constant specifying whether
547        /// to open the file with Read, Write, or ReadWrite file access.</param>
548        /// <returns>A System.IO.FileStream object opened in the specified mode
549        /// and access, unshared, and no special file options.</returns>
550        public FileStream Open(FileAccess access)
551        {
552            return Open(access, FileShare.None, FileOptions.None);
553        }
554
555        /// <summary>
556        /// Opens a file with read, write, or read/write access and the specified
557        /// sharing option.
558        /// </summary>
559        /// <param name="access">A System.IO.FileAccess constant specifying whether
560        /// to open the file with Read, Write, or ReadWrite file access.</param>
561        /// <param name="share">A System.IO.FileShare constant specifying the type
562        /// of access other FileStream objects have to this file.</param>
563        /// <returns>A System.IO.FileStream object opened with the specified mode,
564        /// access, sharing options, and no special file options.</returns>
565        public FileStream Open(FileAccess access, FileShare share)
566        {
567            return Open(access, share, FileOptions.None);
568        }
569
570        /// <summary>
571        /// Opens a file with read, write, or read/write access, the specified
572        /// sharing option, and other advanced options.
573        /// </summary>
574        /// <param name="mode">A System.IO.FileMode constant specifying the mode
575        /// (for example, Open or Append) in which to open the file.</param>
576        /// <param name="access">A System.IO.FileAccess constant specifying whether
577        /// to open the file with Read, Write, or ReadWrite file access.</param>
578        /// <param name="share">A System.IO.FileShare constant specifying the type
579        /// of access other FileStream objects have to this file.</param>
580        /// <param name="options">The System.IO.FileOptions constant specifying
581        /// the advanced file options to use when opening the file.</param>
582        /// <returns>A System.IO.FileStream object opened with the specified mode,
583        /// access, sharing options, and special file options.</returns>
584        public FileStream Open(FileAccess access, FileShare share, FileOptions options)
585        {
586            SafeFileHandle handle = OpenHandle(access, share, options);
587
588            //Check that the handle is valid
589            if (handle.IsInvalid)
590                throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
591
592            //Return the FileStream
593            return new FileStream(handle, access);
594        }
595
596        private SafeFileHandle OpenHandle(FileAccess access, FileShare share, FileOptions options)
597        {
598            //Access mode
599            uint iAccess = 0;
600            switch (access)
601            {
602                case FileAccess.Read:
603                    iAccess = NativeMethods.GENERIC_READ;
604                    break;
605                case FileAccess.ReadWrite:
606                    iAccess = NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE;
607                    break;
608                case FileAccess.Write:
609                    iAccess = NativeMethods.GENERIC_WRITE;
610                    break;
611            }
612
613            //Sharing mode
614            if ((share & FileShare.Inheritable) != 0)
615                throw new NotSupportedException("Inheritable handles are not supported.");
616
617            //Advanced options
618            if ((options & FileOptions.Asynchronous) != 0)
619                throw new NotSupportedException("Asynchronous handles are not implemented.");
620
621            //Create the handle
622            string openPath = VolumeId;
623            if (openPath.Length > 0 && openPath[openPath.Length - 1] == '\\')
624                openPath = openPath.Remove(openPath.Length - 1);
625            SafeFileHandle result = NativeMethods.CreateFile(openPath, iAccess,
626                (uint)share, IntPtr.Zero, (uint)FileMode.Open, (uint)options, IntPtr.Zero);
627            if (result.IsInvalid)
628            {
629                int errorCode = Marshal.GetLastWin32Error();
630                result.Close();
631                throw Win32ErrorCode.GetExceptionForWin32Error(errorCode);
632            }
633
634            return result;
635        }
636
637        /// <summary>
638        /// Queries the performance information for the given disk.
639        /// </summary>
640        public DiskPerformanceInfo Performance
641        {
642            get
643            {
644                using (SafeFileHandle handle = OpenHandle(FileAccess.Read,
645                    FileShare.ReadWrite, FileOptions.None))
646                {
647                    //Check that the handle is valid
648                    if (handle.IsInvalid)
649                        throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
650
651                    //This only works if the user has turned on the disk performance
652                    //counters with 'diskperf -y'. These counters are off by default
653                    NativeMethods.DiskPerformanceInfoInternal result =
654                        new NativeMethods.DiskPerformanceInfoInternal();
655                    uint bytesReturned = 0;
656                    if (NativeMethods.DeviceIoControl(handle, NativeMethods.IOCTL_DISK_PERFORMANCE,
657                        IntPtr.Zero, 0, out result, (uint)Marshal.SizeOf(result),
658                        out bytesReturned, IntPtr.Zero))
659                    {
660                        return new DiskPerformanceInfo(result);
661                    }
662
663                    return null;
664                }
665            }
666        }
667
668        public VolumeLock LockVolume(FileStream stream)
669        {
670            return new VolumeLock(stream);
671        }
672    }
673
674    public class VolumeLock : IDisposable
675    {
676        internal VolumeLock(FileStream stream)
677        {
678            uint result = 0;
679            for (int i = 0; !NativeMethods.DeviceIoControl(stream.SafeFileHandle,
680                    NativeMethods.FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero,
681                    0, out result, IntPtr.Zero); ++i)
682            {
683                if (i > 100)
684                    throw new IOException("Could not lock volume.");
685                System.Threading.Thread.Sleep(100);
686            }
687
688            Stream = stream;
689        }
690
691        ~VolumeLock()
692        {
693            Dispose(false);
694        }
695
696        public void Dispose()
697        {
698            Dispose(true);
699            GC.SuppressFinalize(this);
700        }
701
702        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "disposing")]
703        void Dispose(bool disposing)
704        {
705            //Flush the contents of the buffer to disk since after we unlock the volume
706            //we can no longer write to the volume.
707            Stream.Flush();
708
709            uint result = 0;
710            if (!NativeMethods.DeviceIoControl(Stream.SafeFileHandle,
711                NativeMethods.FSCTL_UNLOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero,
712                0, out result, IntPtr.Zero))
713            {
714                throw new IOException("Could not unlock volume.");
715            }
716        }
717
718        private FileStream Stream;
719    }
720
721    public class DiskPerformanceInfo
722    {
723        internal DiskPerformanceInfo(NativeMethods.DiskPerformanceInfoInternal info)
724        {
725            BytesRead = info.BytesRead;
726            BytesWritten = info.BytesWritten;
727            ReadTime = info.ReadTime;
728            WriteTime = info.WriteTime;
729            IdleTime = info.IdleTime;
730            ReadCount = info.ReadCount;
731            WriteCount = info.WriteCount;
732            QueueDepth = info.QueueDepth;
733            SplitCount = info.SplitCount;
734            QueryTime = info.QueryTime;
735            StorageDeviceNumber = info.StorageDeviceNumber;
736            StorageManagerName = info.StorageManagerName;
737        }
738
739        public long BytesRead { get; private set; }
740        public long BytesWritten { get; private set; }
741        public long ReadTime { get; private set; }
742        public long WriteTime { get; private set; }
743        public long IdleTime { get; private set; }
744        public uint ReadCount { get; private set; }
745        public uint WriteCount { get; private set; }
746        public uint QueueDepth { get; private set; }
747        public uint SplitCount { get; private set; }
748        public long QueryTime { get; private set; }
749        public uint StorageDeviceNumber { get; private set; }
750        public string StorageManagerName { get; private set; }
751    }
752}
Note: See TracBrowser for help on using the repository browser.