/// <summary> /// This function query the file and returns a list of allocated ranges (The non-zero ranges!) /// Please note that the file can contain zero-written areas but if the is not defined /// as sparse and/or the chunks are smaller than 64kb (or 128 kb if the chunk is not /// located in a start of a 64 kb chunk) the query will recognize those areas as allocated ones. /// PLEASE NOTE! This function is the only one that doesn't work well on 64-bit systems /// </summary> /// <param name="fs"></param> /// <returns></returns> public static List <FILE_ALLOCATED_RANGE_BUFFER> QueryAllocatedRanges(FileStream fs) { FILE_ALLOCATED_RANGE_BUFFER _queryRange; _queryRange._offset = 0; _queryRange._length = fs.Length; FILE_ALLOCATED_RANGE_BUFFER[] _ranges = new FILE_ALLOCATED_RANGE_BUFFER[64]; List <FILE_ALLOCATED_RANGE_BUFFER> _resultList = new List <FILE_ALLOCATED_RANGE_BUFFER>(); bool _br = false; int _bytesReturned; int _errNumber; do { _br = false; //In case this is not the first cycle of the loop if (!DeviceIoControlQueryAllocatedRanges(fs.SafeFileHandle, FSCTL_QUERY_ALLOCATED_RANGES, ref _queryRange, Marshal.SizeOf(_queryRange), out _ranges[0], _ranges.Length * Marshal.SizeOf(_queryRange), out _bytesReturned, IntPtr.Zero)) { /*If the error accors because there's some data left (the array was not big enough) the whole process will happen again and again untill all the data is transfered*/ if ((_errNumber = Marshal.GetLastWin32Error()) == ERROR_MORE_DATA) { _br = true; } else { throw new Win32Exception(_errNumber); } } int _nBytes = _bytesReturned / Marshal.SizeOf(_queryRange); for (int i = 0; i < _nBytes; i++) { FILE_ALLOCATED_RANGE_BUFFER _tempBuf; _tempBuf._offset = _ranges[i]._offset; _tempBuf._length = _ranges[i]._length; _resultList.Add(_tempBuf); } if (_br) { _queryRange._offset = _ranges[_nBytes - 1]._offset + _ranges[_nBytes - 1]._length; _queryRange._length = fs.Length - _queryRange._offset; } } while (_br); return(_resultList); }
static extern bool DeviceIoControlQueryAllocatedRanges([In] SafeFileHandle hDevice, [In] uint dwIoControlCode, [In] ref FILE_ALLOCATED_RANGE_BUFFER fAllocRangeBuffer, [In] int nInBufferSize, [Out] out FILE_ALLOCATED_RANGE_BUFFER fRanges, [In] int nOutBufferSize, out int lpBytesReturned, [In] IntPtr lpOverlapped);
/// <summary> /// This function query the file and returns a list of allocated ranges (The non-zero ranges!) /// Please note that the file can contain zero-written areas but if the is not defined /// as sparse and/or the chunks are smaller than 64kb (or 128 kb if the chunk is not /// located in a start of a 64 kb chunk) the query will recognize those areas as allocated ones. /// PLEASE NOTE! This function is the only one that doesn't work well on 64-bit systems /// </summary> /// <param name="fs"></param> /// <returns></returns> public static List<FILE_ALLOCATED_RANGE_BUFFER> QueryAllocatedRanges(FileStream fs) { FILE_ALLOCATED_RANGE_BUFFER _queryRange; _queryRange._offset = 0; _queryRange._length = fs.Length; FILE_ALLOCATED_RANGE_BUFFER[] _ranges = new FILE_ALLOCATED_RANGE_BUFFER[64]; List<FILE_ALLOCATED_RANGE_BUFFER> _resultList = new List<FILE_ALLOCATED_RANGE_BUFFER>(); bool _br = false; int _bytesReturned; int _errNumber; do { _br = false; //In case this is not the first cycle of the loop if (!DeviceIoControlQueryAllocatedRanges(fs.SafeFileHandle, FSCTL_QUERY_ALLOCATED_RANGES, ref _queryRange, Marshal.SizeOf(_queryRange), out _ranges[0], _ranges.Length * Marshal.SizeOf(_queryRange), out _bytesReturned, IntPtr.Zero)) { /*If the error accors because there's some data left (the array was not big enough) the whole process will happen again and again untill all the data is transfered*/ if ((_errNumber = Marshal.GetLastWin32Error()) == ERROR_MORE_DATA) _br = true; else { throw new Win32Exception(_errNumber); } } int _nBytes = _bytesReturned / Marshal.SizeOf(_queryRange); for (int i = 0; i < _nBytes; i++) { FILE_ALLOCATED_RANGE_BUFFER _tempBuf; _tempBuf._offset = _ranges[i]._offset; _tempBuf._length = _ranges[i]._length; _resultList.Add(_tempBuf); } if (_br) { _queryRange._offset = _ranges[_nBytes - 1]._offset + _ranges[_nBytes - 1]._length; _queryRange._length = fs.Length - _queryRange._offset; } } while (_br); return _resultList; }