Sylloge
A C# helper library
code/FileSystem/Drive.cs
Go to the documentation of this file.
00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 using System;
00017 using System.Collections.Generic;
00018 using System.Runtime.InteropServices;
00019 using System.Text;
00020 using Microsoft.Win32.SafeHandles;
00021 
00022 namespace Sylloge
00023 {
00024     public static partial class FileSystem
00025     {
00029         public class Drive : System.IDisposable
00030         {
00031             #region Events
00032 
00038             public delegate void DelegateError(Sylloge.FileSystem.Drive sender, string error);
00042             public event DelegateError Error;
00043 
00048             private void OnError(string error)
00049             {
00050                 if (this.Error != null) { this.Error(this, error); }
00051             }
00052 
00053             #endregion
00054 
00055             #region Attributes
00056 
00060             public int BytesPerSector { get; protected set; }
00064             public string Caption { get; protected set; }
00068             public string DeviceID { get; protected set; }
00072             public string[] DriveLetters { get; protected set; }
00076             public string Description { get; protected set; }
00080             public ulong FreeSpace { get; protected set; }
00084             public Microsoft.Win32.SafeHandles.SafeFileHandle Handle { get; protected set; }
00088             public bool IsLogicalDrive { get; protected set; }
00092             public string Name { get; protected set; }
00096             public uint Partitions { get; protected set; }
00100             public uint SectorsPerTrack { get; protected set; }
00104             public ulong Size { get; protected set; }
00108             public long TotalCylinders { get; protected set; }
00112             public ulong TotalSectors { get; protected set; }
00116             public ulong TotalTracks { get; protected set; }
00120             public uint TracksPerCylinder { get; protected set; }
00124             public System.IO.DriveType Type { get; protected set; }
00128             public ulong UsedSpace { get; protected set; }
00129 
00130             private bool m_IsOpen { get; set; }
00131             private System.IO.Stream m_Stream { get; set; }
00132             private System.Management.ManagementObject m_DiskObject { get; set; }
00133 
00134             #endregion
00135 
00136             #region Methods
00137 
00141             public Drive()
00142             {
00143             }
00144 
00149             public Drive(string letterOrDeviceID)
00150             {
00151                 this.DeviceID = letterOrDeviceID;
00152                 this.Initialize(true);
00153             }
00154 
00160             public Drive(string letterOrDeviceID, bool open)
00161             {
00162                 this.DeviceID = letterOrDeviceID;
00163                 this.Initialize(open);
00164             }
00165 
00169             ~Drive()
00170             {
00171                 this.Dispose();
00172             }
00173 
00177             public void Dispose()
00178             {
00179                 this.Close();
00180                 GC.SuppressFinalize(this);
00181             }
00182 
00186             public void Close()
00187             {
00188                 if (this.Handle != null && !this.Handle.IsClosed) {
00189                     Sylloge.WinAPI.Kernel32.CloseHandle(this.Handle);
00190                     this.Handle.Close();
00191                 }
00192                 if (this.m_Stream != null) {
00193                     try {
00194                         this.m_Stream.Close();
00195                     } catch (Exception ex) {
00196                         Sylloge.App.Out(ex);
00197                     }
00198                 }
00199                 this.IsOpen = false;
00200             }
00201 
00206             public Sylloge.FileSystem.Drive Clone()
00207             {
00208                 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter val = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
00209                 System.IO.MemoryStream s = new System.IO.MemoryStream();
00210                 val.Serialize(s, this);
00211                 Sylloge.FileSystem.Drive res = (Sylloge.FileSystem.Drive)val.Deserialize(s);
00212                 s.Close();
00213                 return res;
00214             }
00215 
00216             private long CTL_CODE(long fileSystemCode, long functionCode, long method, long access)
00217             {
00218                 return ((fileSystemCode * ((long)System.Math.Pow(2, 16))) | (access * ((long)System.Math.Pow(2, 14))) | (functionCode * ((long)System.Math.Pow(2, 2))) | method);
00219             }
00220 
00225             public int DeletePartitionInformation()
00226             {
00227                 if (!this.IsReady) { return -1; }
00228                 uint dummy = 0;
00229                 if (!Sylloge.WinAPI.Kernel32.DeviceIoControl(this.Handle, (uint)Sylloge.WinAPI.Constants.IOCTL_DISK_DELETE_DRIVE_LAYOUT, IntPtr.Zero, 0, IntPtr.Zero, 0, ref dummy, IntPtr.Zero)) {
00230                     Sylloge.App.Out("Error Deleting Partition Information: " + Sylloge.WinAPI.GetLastError().ToString());
00231                     return Sylloge.WinAPI.GetLastError().Code;
00232                 }
00233                 return 0;
00234             }
00235 
00240             public int Eject()
00241             {
00242                 if (!this.IsReady) { return -1; }
00243                 // For this, we mearly need to lock and dismount the volume. Ejecting will make it inaccessable to anything
00244                 uint dummy = 0;
00245                 long Function = 0;
00246                 // Lock the volume
00247                 Function = CTL_CODE(Sylloge.WinAPI.Constants.FILE_DEVICE_FILE_SYSTEM, Sylloge.WinAPI.Constants.FSCTL_LOCK_VOLUME, 0, 0);
00248                 bool Locked = false;
00249                 for (int i = 0; i < 5; i++) {
00250                     Locked = Sylloge.WinAPI.Kernel32.DeviceIoControl(this.Handle, (uint)Function, IntPtr.Zero, 0, IntPtr.Zero, 0, ref dummy, IntPtr.Zero);
00251                     if (Locked) { break; } else { Console.WriteLine("Error Locking Drive: " + Sylloge.WinAPI.GetLastError().ToString()); }
00252                     System.Threading.Thread.Sleep(500);
00253                 }
00254                 if (!Locked) { return -2; }
00255                 // Dismount the volume
00256                 Function = CTL_CODE(Sylloge.WinAPI.Constants.FILE_DEVICE_FILE_SYSTEM, Sylloge.WinAPI.Constants.FSCTL_DISMOUNT_VOLUME, 0, 0);
00257                 if (!Sylloge.WinAPI.Kernel32.DeviceIoControl(this.Handle, (uint)Function, IntPtr.Zero, 0, IntPtr.Zero, 0, ref dummy, IntPtr.Zero)) {
00258                     Console.WriteLine("Error Dismount Drive: " + Sylloge.WinAPI.GetLastError().ToString());
00259                     return Sylloge.WinAPI.GetLastError().Code;
00260                 }
00261                 return 0;
00262             }
00263 
00269             private string GetValue(string value)
00270             {
00271                 return this.GetValue(value, "");
00272             }
00273 
00280             private string GetValue(string value, string defaultValue)
00281             {
00282                 return this.GetValue(this.m_DiskObject, value, defaultValue);
00283             }
00284 
00292             private string GetValue(System.Management.ManagementObject mgmtObject, string value, string defaultValue)
00293             {
00294                 string RetValue = defaultValue;
00295                 try {
00296                     object Retrieved = mgmtObject[value];
00297                     if (Retrieved == null) { return defaultValue; }
00298                     RetValue = Retrieved.ToString();
00299                 } catch (Exception ex) {
00300                     this.OnError(ex.Message);
00301                     RetValue = defaultValue;
00302                 }
00303                 return RetValue;
00304             }
00305 
00310             private void Initialize(bool autoOpen)
00311             {
00312                 if (this.DeviceID.ToUpper().Contains("PHYSICALDRIVE")) {
00313                     this.IsLogicalDrive = false;
00314                     string DeviceIndex = this.DeviceID.Substring(17);
00315                     int OutRes = 0;
00316                     bool IsValidIndex = Int32.TryParse(DeviceIndex, out OutRes);
00317                     if (this.DeviceID.Substring(0, 17).ToLower() != "\\\\.\\physicaldrive" || !IsValidIndex) {
00318                         throw new Exception("Device ID must be a valid drive id in the form of \"\\\\.\\PhysicalDriveX\" where X is a valid number");
00319                     }
00320                 } else {
00321                     if (this.DeviceID.Length > 3 && this.DeviceID.Substring(0, 4) == "\\\\.\\") {
00322                         this.DeviceID = DeviceID.Substring(4);
00323                     }
00324                     if (this.DeviceID.Substring(this.DeviceID.Length - 1) == "\\") {
00325                         this.DeviceID = this.DeviceID.Substring(0, this.DeviceID.Length - 1);
00326                     }
00327                     if (this.DeviceID.Length > 2) {
00328                         this.DeviceID = this.DeviceID.Substring(0, 2);
00329                     }
00330                     if (this.DeviceID[1] == ':') {
00331                         if (this.DeviceID.Length > 2) {
00332                             this.DeviceID = this.DeviceID.Substring(0, 2);
00333                         }
00334                         this.IsLogicalDrive = true;
00335                     } else {
00336                         throw new Exception("Device ID must be a valid letter or ID in the form of one of the following: \"\\\\.\\A:\" or just the drive letter \"A:\"");
00337                     }
00338                 }
00339                 if (autoOpen) { this.Open(); }
00340             }
00341 
00345             public bool IsReady
00346             {
00347                 get
00348                 {
00349                     if (!this.IsOpen || this.Handle == null) { return false; }
00350                     uint dummy = 0;
00351                     var DiskGeometry = new Sylloge.WinAPI.DiskGeometry();
00352                     bool Success = Sylloge.WinAPI.Kernel32.DeviceIoControl(this.Handle, Sylloge.WinAPI.Constants.IOCTL_DISK_GET_DRIVE_GEOMETRY,
00353                                     IntPtr.Zero, 0, out DiskGeometry, (uint)Marshal.SizeOf(DiskGeometry), ref dummy, IntPtr.Zero);
00354                     return (Success && DiskGeometry.SectorsPerTrack > 0);
00355                 }
00356             }
00357 
00361             public bool IsOpen
00362             {
00363                 get
00364                 {
00365                     return (this.m_IsOpen && this.Handle != null && !this.Handle.IsClosed);
00366                 }
00367                 private set
00368                 {
00369                     this.m_IsOpen = value;
00370                 }
00371             }
00372 
00376             public string MediaType
00377             {
00378                 get
00379                 {
00380                     return this.GetValue("MediaType", "NoMedia");
00381                 }
00382             }
00383 
00389             public bool Open(string driveLetterOrDeviceId)
00390             {
00391                 if (this.IsOpen) { this.Close(); }
00392                 this.DeviceID = driveLetterOrDeviceId;
00393                 this.Initialize(false);
00394                 return this.Open();
00395             }
00396 
00401             public bool Open()
00402             {
00403                 if (!this.IsLogicalDrive) {
00404                     this.m_DiskObject = new System.Management.ManagementObject("Win32_DiskDrive.DeviceID=\"" + this.DeviceID.Replace("\\", "\\\\") + "\"");
00405                     // Get the partitions
00406                     List<string> LogicalPartitions = new List<string>();
00407                     System.Management.ManagementObjectSearcher Partitions = new System.Management.ManagementObjectSearcher("ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\"" + this.DeviceID.Replace("\\", "\\\\") + "\"} WHERE AssocClass = Win32_DiskDriveToDiskPartition");
00408                     foreach (System.Management.ManagementObject Partition in Partitions.Get()) {
00409                         // Now get the logical drives associated with the partitions
00410                         System.Management.ManagementObjectSearcher LogicalDrives = new System.Management.ManagementObjectSearcher("ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"" + Partition["DeviceID"].ToString() + "\"} WHERE AssocClass = Win32_LogicalDiskToPartition");
00411                         foreach (System.Management.ManagementObject LogicalDrive in LogicalDrives.Get()) {
00412                             LogicalPartitions.Add(LogicalDrive["DeviceID"].ToString());
00413                         }
00414                     }
00415                     this.DriveLetters = LogicalPartitions.ToArray();
00416                 } else {
00417                     this.m_DiskObject = new System.Management.ManagementObject("Win32_LogicalDisk.DeviceID=\"" + this.DeviceID + "\"");
00418                     this.DriveLetters = new string[] { this.DeviceID };
00419                 }
00420                 this.Type = (System.IO.DriveType)Sylloge.WinAPI.Kernel32.GetDriveType(this.DeviceID + "\\");
00421                 this.Caption = this.GetValue("Caption");
00422                 this.Description = this.GetValue("Description");
00423                 this.Name = this.GetValue("Name");
00424                 this.Size = Convert.ToUInt64(this.GetValue("Size", "0"));
00425                 this.Partitions = Convert.ToUInt32(this.GetValue("Partitions", "0"));
00426                 string Path = (this.IsLogicalDrive ? ("\\\\.\\" + this.DeviceID) : this.DeviceID);
00427                 if (this.Type == System.IO.DriveType.Removable && this.DriveLetters.Length > 0) {
00428                     // If we are trying to open a USB drive (thumb/sd/etc.) or a logical drive 
00429                     // then we must specify the drive (i.e. '\\.\X:')
00430                     Path = "\\\\.\\" + this.DriveLetters[0].Substring(0, 2);
00431                 }
00432                 this.Handle = Sylloge.WinAPI.Kernel32.CreateFile(Path,
00433                             Sylloge.WinAPI.Constants.GENERIC_READ | Sylloge.WinAPI.Constants.GENERIC_WRITE,
00434                             Sylloge.WinAPI.Constants.FILE_SHARE_READ | Sylloge.WinAPI.Constants.FILE_SHARE_WRITE,
00435                             IntPtr.Zero,
00436                             Sylloge.WinAPI.Constants.OPEN_EXISTING,
00437                             Sylloge.WinAPI.Constants.FILE_ATTRIBUTE_NORMAL | Sylloge.WinAPI.Constants.FILE_FLAG_NO_BUFFERING,
00438                             IntPtr.Zero);
00439 
00440                 if (Sylloge.WinAPI.GetLastError().Code != 0 || this.Handle.IsInvalid) { return false; }
00441                 this.IsOpen = (this.Handle != null && !this.Handle.IsClosed);
00442                 if (this.IsOpen) {
00443                     this.m_Stream = new System.IO.FileStream(this.Handle, System.IO.FileAccess.ReadWrite);
00444                 }
00445                 uint dummy = 0;
00446                 var diskGeo = new Sylloge.WinAPI.DiskGeometry();
00447                 Sylloge.WinAPI.Kernel32.DeviceIoControl(this.Handle, Sylloge.WinAPI.Constants.IOCTL_DISK_GET_DRIVE_GEOMETRY,
00448                                 IntPtr.Zero, 0, out diskGeo, (uint)Marshal.SizeOf(diskGeo), ref dummy, IntPtr.Zero);
00449                 this.TotalCylinders = diskGeo.Cylinders;
00450                 this.TracksPerCylinder = (uint)diskGeo.TracksPerCylinder;
00451                 this.TotalTracks = (ulong)(this.TracksPerCylinder * this.TotalCylinders);
00452                 this.SectorsPerTrack = (uint)diskGeo.SectorsPerTrack;
00453                 this.TotalSectors = this.SectorsPerTrack * this.TotalTracks;
00454                 this.BytesPerSector = diskGeo.BytesPerSector;
00455                 if (this.IsLogicalDrive && this.BytesPerSector > 0) {
00456                     this.TotalSectors = (ulong)(this.Size / (ulong)this.BytesPerSector);
00457                 }
00458                 ulong Free = 0, Available = 0, Total = 0;
00459                 if (this.Size == 0) {
00460                     // For some reason, we could not get the size of the drive, so calculate it the hard way
00461                     this.Size = (this.TotalSectors * (ulong)this.BytesPerSector);
00462                 }
00463                 Sylloge.WinAPI.Kernel32.GetDiskFreeSpaceEx(this.DeviceID, out Available, out Total, out Free);
00464                 this.FreeSpace = Free;
00465                 this.UsedSpace = (this.Size - this.FreeSpace);
00466                 return true;
00467             }
00468 
00472             public long Position
00473             {
00474                 get
00475                 {
00476                     return this.m_Stream.Position;
00477                 }
00478             }
00479 
00485             public byte[] Read(ulong sector)
00486             {
00487                 if (!this.IsReady) {
00488                     this.OnError("Read failed, device is not ready");
00489                     return null;
00490                 }
00491                 if (sector > this.TotalSectors) {
00492                     this.OnError(("Read failed, sector is not within the range of the current device: sector=" + sector.ToString() + ", Total Sectors=" + this.TotalSectors.ToString()));
00493                     return null;
00494                 }
00495                 if (!this.Seek(sector, System.IO.SeekOrigin.Begin)) {
00496                     this.OnError("Read failed, could not seek to the specified sector " + sector.ToString());
00497                     return null;
00498                 }
00499                 byte[] Buffer = new byte[this.BytesPerSector];
00500                 try {
00501                     this.m_Stream.Read(Buffer, 0, Buffer.Length);
00502                 } catch (Exception ex) {
00503                     this.OnError("Read failed, " + ex.Message);
00504                     return null;
00505                 }
00506                 return Buffer;
00507             }
00508 
00515             public byte[] Read(ulong sector, int count)
00516             {
00517                 if (!this.IsReady) {
00518                     this.OnError("Read failed, device is not ready");
00519                     return null;
00520                 }
00521                 if (sector > this.TotalSectors) {
00522                     this.OnError(("Read failed, sector is not within the range of the current device: sector=" + sector.ToString() + ", Total Sectors=" + this.TotalSectors.ToString()));
00523                     return null;
00524                 }
00525                 if (!this.Seek(sector, System.IO.SeekOrigin.Begin)) {
00526                     this.OnError("Read failed, could not seek to the specified sector " + sector.ToString());
00527                     return null;
00528                 }
00529                 byte[] Buffer = new byte[(this.BytesPerSector * count)];
00530                 try {
00531                     this.m_Stream.Read(Buffer, 0, Buffer.Length);
00532                 } catch (Exception ex) {
00533                     this.OnError("Read failed, " + ex.Message);
00534                     return null;
00535                 }
00536                 return Buffer;
00537             }
00538 
00545             public bool Seek(ulong sector, System.IO.SeekOrigin origin)
00546             {
00547                 if (!this.IsReady) {
00548                     this.OnError("Seek failed, device is not ready");
00549                     return false;
00550                 }
00551                 if (sector > this.TotalSectors) {
00552                     this.OnError(("Seek failed, sector is not within the range of the current device: sector=" + sector.ToString() + ", Total Sectors=" + this.TotalSectors.ToString()));
00553                     return false;
00554                 }
00555                 long Pos = 0, Seeked = 0;
00556                 try {
00557                     Pos = ((long)sector * (long)this.BytesPerSector);
00558                     Seeked = this.m_Stream.Seek((long)Pos, origin);
00559                 } catch (Exception ex) {
00560                     this.OnError("Seek failed, " + ex.Message);
00561                     return false;
00562                 }
00563                 return (Pos == Seeked);
00564             }
00565 
00570             public override string ToString()
00571             {
00572                 if (!this.IsReady) { return this.DeviceID + " - Device not ready"; }
00573                 string Value = this.DeviceID;
00574                 if (!this.IsLogicalDrive) {
00575                     Value += " - ";
00576                     if (this.DriveLetters.Length == 0) {
00577                         if (this.Type == System.IO.DriveType.Removable) {
00578                             Value += "No Media";
00579                         } else {
00580                             Value += "No Readable Windows Volumes";
00581                         }
00582                     } else {
00583                         foreach (string Letter in this.DriveLetters) {
00584                             Value += " { " + Letter + " }";
00585                         }
00586                     }
00587                 }
00588                 return Value;
00589             }
00590 
00597             public bool Write(ulong sector, byte[] data)
00598             {
00599                 if (!this.IsReady) {
00600                     this.OnError("Write failed, device is not ready");
00601                     return false;
00602                 }
00603                 if ((data.Length % this.BytesPerSector) != 0) {
00604                     this.OnError(("Write failed, data length to write should be on clean byte sectors (multiples of the BPS): data.Length=" + data.Length.ToString() + ", Bytes Per Sector=" + this.BytesPerSector.ToString()));
00605                     return false;
00606                 }
00607                 if (!this.Seek(sector, System.IO.SeekOrigin.Begin)) {
00608                     this.OnError("Write failed, could not seek to the specified sector " + sector.ToString());
00609                     return false;
00610                 }
00611                 try {
00612                     this.m_Stream.Write(data, 0, data.Length);
00613                     this.m_Stream.Flush();
00614                 } catch (Exception ex) {
00615                     this.OnError("Write failed, " + ex.Message);
00616                     return false;
00617                 }
00618                 return true;
00619             }
00620 
00621             #endregion
00622 
00623             #region Static Members
00624 
00630             public static Sylloge.FileSystem.Drive[] GetDrives(bool autoOpen)
00631             {
00632                 System.Collections.Generic.List<Sylloge.FileSystem.Drive> ret = new System.Collections.Generic.List<Sylloge.FileSystem.Drive>();
00633                 System.Management.ManagementObjectSearcher Disks = new System.Management.ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
00634                 foreach (System.Management.ManagementObject Disk in Disks.Get()) {
00635                     ret.Add((new Sylloge.FileSystem.Drive(Disk["DeviceID"].ToString(), autoOpen)));
00636                 }
00637                 return ret.ToArray();
00638             }
00639 
00645             public static Sylloge.FileSystem.Drive[] GetVolumes(bool autoOpen)
00646             {
00647                 System.Collections.Generic.List<Sylloge.FileSystem.Drive> ret = new System.Collections.Generic.List<Sylloge.FileSystem.Drive>();
00648                 foreach (string dev in System.Environment.GetLogicalDrives()) {
00649                     System.IO.DriveType Type = (new System.IO.DriveInfo(dev)).DriveType;
00650                     // Only add Fixed or Removable devices (in other words, no network/RAM/cd drives)
00651                     if (Type == System.IO.DriveType.Fixed || Type == System.IO.DriveType.Removable) {
00652                         ret.Add((new Sylloge.FileSystem.Drive(dev.Substring(0, 2), autoOpen)));
00653                     }
00654                 }
00655                 return ret.ToArray();
00656             }
00657 
00658             #endregion
00659         }
00660     }
00661 }
 All Classes Namespaces Files Functions Variables Enumerations Properties Events