/// <summary> /// Create a sparse file. /// </summary> /// <param name="fileName">The name of the sparse file</param> /// <returns></returns> public static FileStream Create(string fileName) { // Create a normal file FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); // Use the DeviceIoControl function with the FSCTL_SET_SPARSE control // code to mark the file as sparse. If you don't mark the file as // sparse, the FSCTL_SET_ZERO_DATA control code will actually write // zero bytes to the file instead of marking the region as sparse // zero area. int bytesReturned = 0; NativeOverlapped lpOverlapped = new NativeOverlapped(); NativeMethod.DeviceIoControl(fs.SafeFileHandle, EIoControlCode.FsctlSetSparse, IntPtr.Zero, 0, IntPtr.Zero, 0, ref bytesReturned, ref lpOverlapped); return(fs); }
/// <summary> /// Converting a file region to A sparse zero area. /// </summary> /// <param name="hSparseFile">Safe handle of the sparse file</param> /// <param name="start">Start address of the sparse zero area</param> /// <param name="size"> /// Size of the sparse zero block. The minimum sparse size is 64KB. /// </param> /// <remarks> /// Note that SetSparseRange does not perform actual file I/O, and unlike /// the WriteFile function, it does not move the current file I/O pointer /// or sets the end-of-file pointer. That is, if you want to place a /// sparse zero block in the end of the file, you must move the file /// pointer accordingly using the FileStream.Seek function, otherwise /// DeviceIoControl will have no effect. /// </remarks> public static void SetSparseRange(SafeFileHandle hSparseFile, uint start, uint size) { // Specify the starting and the ending address (not the size) of the // sparse zero block FILE_ZERO_DATA_INFORMATION fzdi; fzdi.FileOffset = start; fzdi.BeyondFinalZero = start + size; GCHandle hfzdi = GCHandle.Alloc(fzdi, GCHandleType.Pinned); // Mark the range as sparse zero block int bytesReturned = 0; NativeOverlapped lpOverlapped = new NativeOverlapped(); NativeMethod.DeviceIoControl(hSparseFile, EIoControlCode.FsctlSetZeroData, hfzdi.AddrOfPinnedObject(), Marshal.SizeOf(fzdi), IntPtr.Zero, 0, ref bytesReturned, ref lpOverlapped); hfzdi.Free(); }
/// <summary> /// Get disk information by P/Invoke. /// </summary> /// <param name="physicalDrive">Index number of physical drive</param> /// <returns>Disk information</returns> internal static DiskInfo GetDiskInfo(int physicalDrive) { var info = new DiskInfo { PhysicalDrive = physicalDrive }; SafeFileHandle hFile = null; try { hFile = NativeMethod.CreateFile( String.Format(@"\\.\PhysicalDrive{0}", physicalDrive), NativeMethod.GENERIC_READ | NativeMethod.GENERIC_WRITE, // Administrative privilege is required. GENERIC_WRITE is for IOCTL_ATA_PASS_THROUGH. NativeMethod.FILE_SHARE_READ | NativeMethod.FILE_SHARE_WRITE, IntPtr.Zero, NativeMethod.OPEN_EXISTING, NativeMethod.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); if (hFile == null || hFile.IsInvalid) { // This is normal when this application is not run by administrator. throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get handle to disk."); } // --------------------------------- // Use IOCTL_STORAGE_QUERY_PROPERTY. // --------------------------------- var storageQuery = new NativeMethod.STORAGE_PROPERTY_QUERY(); storageQuery.PropertyId = NativeMethod.StorageDeviceProperty; storageQuery.QueryType = NativeMethod.PropertyStandardQuery; NativeMethod.STORAGE_DEVICE_DESCRIPTOR storageDescriptor; uint bytesReturned1; var result1 = NativeMethod.DeviceIoControl( hFile, NativeMethod.IOCTL_STORAGE_QUERY_PROPERTY, ref storageQuery, (uint)Marshal.SizeOf(typeof(NativeMethod.STORAGE_PROPERTY_QUERY)), out storageDescriptor, (uint)Marshal.SizeOf(typeof(NativeMethod.STORAGE_DEVICE_DESCRIPTOR)), out bytesReturned1, IntPtr.Zero); if (result1 == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get disk information."); } // Convert to byte array. var size = Marshal.SizeOf(storageDescriptor); var ptr = Marshal.AllocHGlobal(size); var bytes = new byte[size]; Marshal.StructureToPtr(storageDescriptor, ptr, true); Marshal.Copy(ptr, bytes, 0, size); Marshal.FreeHGlobal(ptr); // Set values. info.Vendor = ConvertBytesToString(bytes, (int)storageDescriptor.VendorIdOffset).Trim(); info.Product = ConvertBytesToString(bytes, (int)storageDescriptor.ProductIdOffset).Trim(); info.IsRemovable = storageDescriptor.RemovableMedia; info.BusType = ConvertBusTypeToString(storageDescriptor.BusType); // ------------------------------- // Use IOCTL_DISK_GET_LENGTH_INFO. // ------------------------------- long diskSize; uint bytesReturned2; var result2 = NativeMethod.DeviceIoControl( hFile, NativeMethod.IOCTL_DISK_GET_LENGTH_INFO, IntPtr.Zero, 0, out diskSize, (uint)Marshal.SizeOf(typeof(long)), out bytesReturned2, IntPtr.Zero); if (result2 == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get disk size."); } // Set value. info.SizePInvoke = diskSize; // --------------------------- // Use IOCTL_ATA_PASS_THROUGH. // --------------------------- var ataQuery = new NativeMethod.ATAIdentifyDeviceQuery(); ataQuery.data = new ushort[256]; ataQuery.header.Length = (ushort)Marshal.SizeOf(typeof(NativeMethod.ATA_PASS_THROUGH_EX)); ataQuery.header.AtaFlags = NativeMethod.ATA_FLAGS.ATA_FLAGS_DATA_IN; ataQuery.header.DataTransferLength = (uint)ataQuery.data.Length * 2; // Size of "data" in bytes ataQuery.header.TimeOutValue = 3; // Sec ataQuery.header.DataBufferOffset = Marshal.OffsetOf(typeof(NativeMethod.ATAIdentifyDeviceQuery), "data"); ataQuery.header.PreviousTaskFile = new byte[8]; ataQuery.header.CurrentTaskFile = new byte[8]; ataQuery.header.CurrentTaskFile[6] = 0xec; // ATA IDENTIFY DEVICE uint bytesReturned3; var result3 = NativeMethod.DeviceIoControl( hFile, NativeMethod.IOCTL_ATA_PASS_THROUGH, ref ataQuery, (uint)Marshal.SizeOf(typeof(NativeMethod.ATAIdentifyDeviceQuery)), ref ataQuery, (uint)Marshal.SizeOf(typeof(NativeMethod.ATAIdentifyDeviceQuery)), out bytesReturned3, IntPtr.Zero); if (result3) { const int index = 217; // Word index of nominal media rotation rate (1 means non-rotating media.) info.NominalMediaRotationRate = ataQuery.data[index]; } else { // None. It is normal that IOCTL_ATA_PASS_THROUGH fails when used to external or removable media. } } catch (Win32Exception ex) { Debug.WriteLine("{0} (Code: {1}).", ex.Message.Substring(0, ex.Message.Length - 1), ex.ErrorCode); throw; } catch (Exception ex) { Debug.WriteLine(ex.Message); throw; } finally { if (hFile != null) { // CloseHandle is inappropriate to close SafeFileHandle. // Dispose method is not necessary because Close method will call it internally. hFile.Close(); } } return(info); }
/// <summary> /// Query the sparse file layout. /// </summary> /// <param name="fileName">File name</param> /// <returns></returns> public static bool GetSparseRanges(string fileName) { // Open the file for read using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { // Set the range to be examined (the whole file) FILE_ALLOCATED_RANGE_BUFFER queryRange; queryRange.FileOffset = 0; queryRange.Length = fs.Length; GCHandle hQueryRange = GCHandle.Alloc(queryRange, GCHandleType.Pinned); // Allocated areas info // DeviceIoControl will return as many results as fit into this // buffer and will report error code ERROR_MORE_DATA as long as // more data is available FILE_ALLOCATED_RANGE_BUFFER[] allocRanges = new FILE_ALLOCATED_RANGE_BUFFER[1024]; GCHandle hAllocRanges = GCHandle.Alloc(allocRanges, GCHandleType.Pinned); int nbytes = 0; bool bFinished = false; Console.WriteLine("\nAllocated ranges in the file:"); do { NativeOverlapped lpOverlapped = new NativeOverlapped(); bFinished = NativeMethod.DeviceIoControl(fs.SafeFileHandle, EIoControlCode.FsctlQueryAllocatedRanges, hQueryRange.AddrOfPinnedObject(), Marshal.SizeOf(queryRange), hAllocRanges.AddrOfPinnedObject(), Marshal.SizeOf(typeof(FILE_ALLOCATED_RANGE_BUFFER)) * 1024, ref nbytes, ref lpOverlapped); if (!bFinished) { int error = Marshal.GetLastWin32Error(); // ERROR_MORE_DATA is the only error that is normal if (error != NativeMethod.ERROR_MORE_DATA) { Console.WriteLine("DeviceIoControl failed w/err 0x{0:X}", error); return(false); } } // Calculate the number of records returned int allocRangeCount = nbytes / Marshal.SizeOf(typeof(FILE_ALLOCATED_RANGE_BUFFER)); // Print each allocated range for (int i = 0; i < allocRangeCount; i++) { Console.WriteLine("allocated range: {0} {1}", allocRanges[i].FileOffset, allocRanges[i].Length); } // Set starting address and size for the next query if (!bFinished && allocRangeCount > 0) { queryRange.FileOffset = allocRanges[allocRangeCount - 1].FileOffset + allocRanges[allocRangeCount - 1].Length; queryRange.Length = fs.Length - queryRange.FileOffset; } } while (!bFinished); // Release the pinned GC handles hAllocRanges.Free(); hQueryRange.Free(); } return(true); }