![]() |
Sylloge
A C# helper library
|
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 }
1.7.4