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

Revision 1701, 22.2 KB checked in by lowjoel, 5 years ago (diff)

Forward port changes from trunk till r1700

  • 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 Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
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 Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
329                        }
330                    }
331                    else
332                    {
333                        return new VolumeInfo(volumeID.ToString());
334                    }
335                }
336
337                mountpointDir = mountpointDir.Parent;
338            }
339            while (mountpointDir != null);
340
341            throw Win32ErrorCode.GetExceptionForWin32Error(Win32ErrorCode.NotAReparsePoint);
342        }
343
344        /// <summary>
345        /// Returns the volume identifier as would be returned from FindFirstVolume.
346        /// </summary>
347        public string VolumeId { get; private set; }
348
349        /// <summary>
350        /// Gets or sets the volume label of a drive.
351        /// </summary>
352        public string VolumeLabel { get; private set; }
353
354        /// <summary>
355        /// Gets the name of the file system, such as NTFS or FAT32.
356        /// </summary>
357        public string VolumeFormat { get; private set; }
358
359        /// <summary>
360        /// Gets the drive type; returns one of the System.IO.DriveType values.
361        /// </summary>
362        public DriveType VolumeType
363        {
364            get
365            {
366                return (DriveType)NativeMethods.GetDriveType(VolumeId);
367            }
368        }
369
370        /// <summary>
371        /// Determines the cluster size of the current volume.
372        /// </summary>
373        public int ClusterSize
374        {
375            get
376            {
377                uint clusterSize, sectorSize, freeClusters, totalClusters;
378                if (NativeMethods.GetDiskFreeSpace(VolumeId, out clusterSize,
379                    out sectorSize, out freeClusters, out totalClusters))
380                {
381                    return (int)(clusterSize * sectorSize);
382                }
383
384                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
385            }
386        }
387
388        /// <summary>
389        /// Determines the sector size of the current volume.
390        /// </summary>
391        public int SectorSize
392        {
393            get
394            {
395                uint clusterSize, sectorSize, freeClusters, totalClusters;
396                if (NativeMethods.GetDiskFreeSpace(VolumeId, out clusterSize,
397                    out sectorSize, out freeClusters, out totalClusters))
398                {
399                    return (int)sectorSize;
400                }
401
402                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
403            }
404        }
405
406        /// <summary>
407        /// Checks if the current user has disk quotas on the current volume.
408        /// </summary>
409        public bool HasQuota
410        {
411            get
412            {
413                ulong freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes;
414                if (NativeMethods.GetDiskFreeSpaceEx(VolumeId, out freeBytesAvailable,
415                    out totalNumberOfBytes, out totalNumberOfFreeBytes))
416                {
417                    return totalNumberOfFreeBytes != freeBytesAvailable;
418                }
419                else if (Marshal.GetLastWin32Error() == Win32ErrorCode.NotReady)
420                {
421                    //For the lack of more appropriate responses.
422                    return false;
423                }
424
425                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
426            }
427        }
428
429        /// <summary>
430        /// Gets a value indicating whether a drive is ready.
431        /// </summary>
432        public bool IsReady { get; private set; }
433
434        /// <summary>
435        /// Gets the total amount of free space available on a drive.
436        /// </summary>
437        public long TotalFreeSpace
438        {
439            get
440            {
441                ulong result, dummy;
442                if (NativeMethods.GetDiskFreeSpaceEx(VolumeId, out dummy, out dummy, out result))
443                {
444                    return (long)result;
445                }
446
447                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
448            }
449        }
450       
451        /// <summary>
452        /// Gets the total size of storage space on a drive.
453        /// </summary>
454        public long TotalSize
455        {
456            get
457            {
458                ulong result, dummy;
459                if (NativeMethods.GetDiskFreeSpaceEx(VolumeId, out dummy, out result, out dummy))
460                {
461                    return (long)result;
462                }
463
464                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
465            }
466        }
467
468        /// <summary>
469        /// Indicates the amount of available free space on a drive.
470        /// </summary>
471        public long AvailableFreeSpace
472        {
473            get
474            {
475                ulong result, dummy;
476                if (NativeMethods.GetDiskFreeSpaceEx(VolumeId, out result, out dummy, out dummy))
477                {
478                    return (long)result;
479                }
480
481                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
482            }
483        }
484
485        /// <summary>
486        /// Retrieves all mountpoints in the current volume, if the current volume
487        /// contains volume mountpoints.
488        /// </summary>
489        public IList<VolumeInfo> MountedVolumes
490        {
491            get
492            {
493                List<VolumeInfo> result = new List<VolumeInfo>();
494                StringBuilder nextMountpoint = new StringBuilder(NativeMethods.LongPath * sizeof(char));
495
496                SafeHandle handle = NativeMethods.FindFirstVolumeMountPoint(VolumeId,
497                    nextMountpoint, NativeMethods.LongPath);
498                if (handle.IsInvalid)
499                    return result;
500
501                try
502                {
503                    //Iterate over the volume mountpoints
504                    while (NativeMethods.FindNextVolumeMountPoint(handle, nextMountpoint,
505                        NativeMethods.LongPath))
506                    {
507                        result.Add(new VolumeInfo(nextMountpoint.ToString()));
508                    }
509                }
510                finally
511                {
512                    //Close the handle
513                    NativeMethods.FindVolumeMountPointClose(handle);
514                }
515
516                return result.AsReadOnly();
517            }
518        }
519
520        /// <summary>
521        /// The various mountpoints to the root of the volume. This list contains
522        /// paths which may be a drive or a mountpoint. Every string includes the
523        /// trailing backslash.
524        /// </summary>
525        public ReadOnlyCollection<string> MountPoints
526        {
527            get
528            {
529                return (VolumeType == DriveType.Network ?
530                    GetNetworkMountPoints() : GetLocalVolumeMountPoints()).AsReadOnly();
531            }
532        }
533
534        /// <summary>
535        /// Gets whether the current volume is mounted at any place.
536        /// </summary>
537        public bool IsMounted
538        {
539            get { return MountPoints.Count != 0; }
540        }
541
542        /// <summary>
543        /// Opens a file with read, write, or read/write access.
544        /// </summary>
545        /// <param name="access">A System.IO.FileAccess constant specifying whether
546        /// to open the file with Read, Write, or ReadWrite file access.</param>
547        /// <returns>A System.IO.FileStream object opened in the specified mode
548        /// and access, unshared, and no special file options.</returns>
549        public FileStream Open(FileAccess access)
550        {
551            return Open(access, FileShare.None, FileOptions.None);
552        }
553
554        /// <summary>
555        /// Opens a file with read, write, or read/write access and the specified
556        /// sharing option.
557        /// </summary>
558        /// <param name="access">A System.IO.FileAccess constant specifying whether
559        /// to open the file with Read, Write, or ReadWrite file access.</param>
560        /// <param name="share">A System.IO.FileShare constant specifying the type
561        /// of access other FileStream objects have to this file.</param>
562        /// <returns>A System.IO.FileStream object opened with the specified mode,
563        /// access, sharing options, and no special file options.</returns>
564        public FileStream Open(FileAccess access, FileShare share)
565        {
566            return Open(access, share, FileOptions.None);
567        }
568
569        /// <summary>
570        /// Opens a file with read, write, or read/write access, the specified
571        /// sharing option, and other advanced options.
572        /// </summary>
573        /// <param name="mode">A System.IO.FileMode constant specifying the mode
574        /// (for example, Open or Append) in which to open the file.</param>
575        /// <param name="access">A System.IO.FileAccess constant specifying whether
576        /// to open the file with Read, Write, or ReadWrite file access.</param>
577        /// <param name="share">A System.IO.FileShare constant specifying the type
578        /// of access other FileStream objects have to this file.</param>
579        /// <param name="options">The System.IO.FileOptions constant specifying
580        /// the advanced file options to use when opening the file.</param>
581        /// <returns>A System.IO.FileStream object opened with the specified mode,
582        /// access, sharing options, and special file options.</returns>
583        public FileStream Open(FileAccess access, FileShare share, FileOptions options)
584        {
585            SafeFileHandle handle = OpenHandle(access, share, options);
586
587            //Check that the handle is valid
588            if (handle.IsInvalid)
589                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
590
591            //Return the FileStream
592            return new FileStream(handle, access);
593        }
594
595        private SafeFileHandle OpenHandle(FileAccess access, FileShare share, FileOptions options)
596        {
597            //Access mode
598            uint iAccess = 0;
599            switch (access)
600            {
601                case FileAccess.Read:
602                    iAccess = NativeMethods.GENERIC_READ;
603                    break;
604                case FileAccess.ReadWrite:
605                    iAccess = NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE;
606                    break;
607                case FileAccess.Write:
608                    iAccess = NativeMethods.GENERIC_WRITE;
609                    break;
610            }
611
612            //Sharing mode
613            if ((share & FileShare.Inheritable) != 0)
614                throw new NotSupportedException("Inheritable handles are not supported.");
615
616            //Advanced options
617            if ((options & FileOptions.Asynchronous) != 0)
618                throw new NotSupportedException("Asynchronous handles are not implemented.");
619
620            //Create the handle
621            string openPath = VolumeId;
622            if (openPath.Length > 0 && openPath[openPath.Length - 1] == '\\')
623                openPath = openPath.Remove(openPath.Length - 1);
624            SafeFileHandle result = NativeMethods.CreateFile(openPath, iAccess,
625                (uint)share, IntPtr.Zero, (uint)FileMode.Open, (uint)options, IntPtr.Zero);
626            if (result.IsInvalid)
627            {
628                int errorCode = Marshal.GetLastWin32Error();
629                result.Close();
630                throw Win32ErrorCode.GetExceptionForWin32Error(errorCode);
631            }
632
633            return result;
634        }
635
636        /// <summary>
637        /// Queries the performance information for the given disk.
638        /// </summary>
639        public DiskPerformanceInfo Performance
640        {
641            get
642            {
643                using (SafeFileHandle handle = OpenHandle(FileAccess.Read,
644                    FileShare.ReadWrite, FileOptions.None))
645                {
646                    //Check that the handle is valid
647                    if (handle.IsInvalid)
648                        throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
649
650                    //This only works if the user has turned on the disk performance
651                    //counters with 'diskperf -y'. These counters are off by default
652                    NativeMethods.DiskPerformanceInfoInternal result =
653                        new NativeMethods.DiskPerformanceInfoInternal();
654                    uint bytesReturned = 0;
655                    if (NativeMethods.DeviceIoControl(handle, NativeMethods.IOCTL_DISK_PERFORMANCE,
656                        IntPtr.Zero, 0, out result, (uint)Marshal.SizeOf(result),
657                        out bytesReturned, IntPtr.Zero))
658                    {
659                        return new DiskPerformanceInfo(result);
660                    }
661
662                    return null;
663                }
664            }
665        }
666
667        public VolumeLock LockVolume(FileStream stream)
668        {
669            return new VolumeLock(stream);
670        }
671    }
672
673    public class VolumeLock : IDisposable
674    {
675        internal VolumeLock(FileStream stream)
676        {
677            uint result = 0;
678            for (int i = 0; !NativeMethods.DeviceIoControl(stream.SafeFileHandle,
679                    NativeMethods.FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero,
680                    0, out result, IntPtr.Zero); ++i)
681            {
682                if (i > 100)
683                    throw new IOException("Could not lock volume.");
684                System.Threading.Thread.Sleep(100);
685            }
686
687            Stream = stream;
688        }
689
690        ~VolumeLock()
691        {
692            Dispose(false);
693        }
694
695        public void Dispose()
696        {
697            Dispose(true);
698            GC.SuppressFinalize(this);
699        }
700
701        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "disposing")]
702        void Dispose(bool disposing)
703        {
704            //Flush the contents of the buffer to disk since after we unlock the volume
705            //we can no longer write to the volume.
706            Stream.Flush();
707
708            uint result = 0;
709            if (!NativeMethods.DeviceIoControl(Stream.SafeFileHandle,
710                NativeMethods.FSCTL_UNLOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero,
711                0, out result, IntPtr.Zero))
712            {
713                throw new IOException("Could not unlock volume.");
714            }
715        }
716
717        private FileStream Stream;
718    }
719
720    public class DiskPerformanceInfo
721    {
722        internal DiskPerformanceInfo(NativeMethods.DiskPerformanceInfoInternal info)
723        {
724            BytesRead = info.BytesRead;
725            BytesWritten = info.BytesWritten;
726            ReadTime = info.ReadTime;
727            WriteTime = info.WriteTime;
728            IdleTime = info.IdleTime;
729            ReadCount = info.ReadCount;
730            WriteCount = info.WriteCount;
731            QueueDepth = info.QueueDepth;
732            SplitCount = info.SplitCount;
733            QueryTime = info.QueryTime;
734            StorageDeviceNumber = info.StorageDeviceNumber;
735            StorageManagerName = info.StorageManagerName;
736        }
737
738        public long BytesRead { get; private set; }
739        public long BytesWritten { get; private set; }
740        public long ReadTime { get; private set; }
741        public long WriteTime { get; private set; }
742        public long IdleTime { get; private set; }
743        public uint ReadCount { get; private set; }
744        public uint WriteCount { get; private set; }
745        public uint QueueDepth { get; private set; }
746        public uint SplitCount { get; private set; }
747        public long QueryTime { get; private set; }
748        public uint StorageDeviceNumber { get; private set; }
749        public string StorageManagerName { get; private set; }
750    }
751}
Note: See TracBrowser for help on using the repository browser.