2 // System.IO/FileStream.cs
5 // Dietmar Maurer (dietmar@ximian.com)
6 // Dan Lewis (dihlewis@yahoo.co.uk)
7 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 // (C) 2001-2003 Ximian, Inc. http://www.ximian.com
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Globalization;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
36 using System.Runtime.Remoting.Messaging;
37 using System.Security.Permissions;
38 using System.Threading;
41 using Microsoft.Win32.SafeHandles;
46 public class FileStream : Stream
48 // construct from handle
50 public FileStream (IntPtr handle, FileAccess access)
51 : this (handle, access, true, DefaultBufferSize, false) {}
53 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
54 : this (handle, access, ownsHandle, DefaultBufferSize, false) {}
56 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
57 : this (handle, access, ownsHandle, bufferSize, false) {}
59 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
60 : this (handle, access, ownsHandle, bufferSize, isAsync, false) {}
62 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
63 internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool noBuffering)
65 this.handle = MonoIO.InvalidHandle;
66 if (handle == this.handle)
67 throw new ArgumentException ("handle", Locale.GetText ("Invalid."));
69 if (access < FileAccess.Read || access > FileAccess.ReadWrite)
70 throw new ArgumentOutOfRangeException ("access");
73 MonoFileType ftype = MonoIO.GetFileType (handle, out error);
75 if (error != MonoIOError.ERROR_SUCCESS) {
76 throw MonoIO.GetException (name, error);
79 if (ftype == MonoFileType.Unknown) {
80 throw new IOException ("Invalid handle.");
81 } else if (ftype == MonoFileType.Disk) {
91 this.owner = ownsHandle;
93 this.anonymous = false;
95 InitBuffer (bufferSize, noBuffering);
98 buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
99 if (error != MonoIOError.ERROR_SUCCESS) {
100 throw MonoIO.GetException (name, error);
104 /* Can't set append mode */
105 this.append_startpos=0;
108 // construct from filename
110 public FileStream (string name, FileMode mode)
111 : this (name, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
115 public FileStream (string name, FileMode mode, FileAccess access)
116 : this (name, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
120 public FileStream (string name, FileMode mode, FileAccess access, FileShare share)
121 : this (name, mode, access, share, DefaultBufferSize, false, FileOptions.None)
125 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize)
126 : this (name, mode, access, share, bufferSize, false, FileOptions.None)
130 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync)
131 : this (name, mode, access, share, bufferSize, isAsync, FileOptions.None)
136 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
137 : this (name, mode, access, share, bufferSize, false, options)
142 internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
143 : this (name, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None)
147 internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
150 throw new ArgumentNullException ("name");
153 if (name.Length == 0) {
154 throw new ArgumentException ("Name is empty");
158 // ignore the Inheritable flag
159 share &= ~FileShare.Inheritable;
164 throw new ArgumentOutOfRangeException ("Positive number required.");
166 if (mode < FileMode.CreateNew || mode > FileMode.Append)
167 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
169 if (access < FileAccess.Read || access > FileAccess.ReadWrite)
170 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
172 if (share < FileShare.None || share > FileShare.ReadWrite)
173 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
175 if (name.IndexOfAny (Path.InvalidPathChars) != -1) {
176 throw new ArgumentException ("Name has invalid chars");
179 if (Directory.Exists (name)) {
180 // don't leak the path information for isolated storage
181 string msg = Locale.GetText ("Access to the path '{0}' is denied.");
182 string fname = (anonymous) ? Path.GetFileName (name) : Path.GetFullPath (name);
183 throw new UnauthorizedAccessException (String.Format (msg, fname));
186 /* Append streams can't be read (see FileMode
189 if (mode==FileMode.Append &&
190 (access&FileAccess.Read)==FileAccess.Read) {
191 throw new ArgumentException("Append access can be requested only in write-only mode.");
194 if ((access & FileAccess.Write) == 0 &&
195 (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
196 string msg = Locale.GetText ("Combining FileMode: {0} with " +
197 "FileAccess: {1} is invalid.");
198 throw new ArgumentException (string.Format (msg, access, mode));
201 string dname = Path.GetDirectoryName (name);
202 if (dname.Length > 0) {
203 string fp = Path.GetFullPath (dname);
204 if (!Directory.Exists (fp)) {
205 // don't leak the path information for isolated storage
206 string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
207 string fname = (anonymous) ? dname : fp;
208 throw new DirectoryNotFoundException (String.Format (msg, fname));
212 if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
213 mode != FileMode.CreateNew && !File.Exists (name)) {
214 // don't leak the path information for isolated storage
215 string msg = Locale.GetText ("Could not find file \"{0}\".");
216 string fname = (anonymous) ? Path.GetFileName (name) : name;
217 throw new FileNotFoundException (String.Format (msg, fname), fname);
220 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
224 // TODO: demand permissions
228 this.handle = MonoIO.Open (name, mode, access, share, options, out error);
229 if (handle == MonoIO.InvalidHandle) {
230 // don't leak the path information for isolated storage
231 string fname = (anonymous) ? Path.GetFileName (name) : name;
232 throw MonoIO.GetException (fname, error);
235 this.access = access;
237 this.anonymous = anonymous;
239 /* Can we open non-files by name? */
241 if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
243 this.async = (options & FileOptions.Asynchronous) != 0;
245 this.canseek = false;
250 if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
251 /* Avoid allocating a large buffer for small files */
253 if (bufferSize > len) {
254 bufferSize = (int)(len < 1000 ? 1000 : len);
258 InitBuffer (bufferSize, false);
260 if (mode==FileMode.Append) {
261 this.Seek (0, SeekOrigin.End);
262 this.append_startpos=this.Position;
264 this.append_startpos=0;
270 public override bool CanRead {
272 return access == FileAccess.Read ||
273 access == FileAccess.ReadWrite;
277 public override bool CanWrite {
279 return access == FileAccess.Write ||
280 access == FileAccess.ReadWrite;
284 public override bool CanSeek {
290 public virtual bool IsAsync {
302 public override long Length {
304 if (handle == MonoIO.InvalidHandle)
305 throw new ObjectDisposedException ("Stream has been closed");
308 throw new NotSupportedException ("The stream does not support seeking");
310 // Buffered data might change the length of the stream
311 FlushBufferIfDirty ();
316 length = MonoIO.GetLength (handle, out error);
317 if (error != MonoIOError.ERROR_SUCCESS) {
318 // don't leak the path information for isolated storage
319 string fname = (anonymous) ? Path.GetFileName (name) : name;
320 throw MonoIO.GetException (fname, error);
327 public override long Position {
329 if (handle == MonoIO.InvalidHandle)
330 throw new ObjectDisposedException ("Stream has been closed");
333 throw new NotSupportedException("The stream does not support seeking");
335 return(buf_start + buf_offset);
338 if (handle == MonoIO.InvalidHandle)
339 throw new ObjectDisposedException ("Stream has been closed");
341 if(CanSeek == false) {
342 throw new NotSupportedException("The stream does not support seeking");
346 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
349 Seek (value, SeekOrigin.Begin);
353 public virtual IntPtr Handle {
354 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
355 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
362 public virtual SafeFileHandle SafeFileHandle {
363 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
364 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
365 get { throw new NotImplementedException (); }
371 public override int ReadByte ()
373 if (handle == MonoIO.InvalidHandle)
374 throw new ObjectDisposedException ("Stream has been closed");
377 throw new NotSupportedException ("Stream does not support reading");
380 int n = ReadData (handle, buf, 0, 1);
381 if (n == 0) return -1;
384 else if (buf_offset >= buf_length) {
391 return buf [buf_offset ++];
394 public override void WriteByte (byte value)
396 if (handle == MonoIO.InvalidHandle)
397 throw new ObjectDisposedException ("Stream has been closed");
400 throw new NotSupportedException ("Stream does not support writing");
402 if (buf_offset == buf_size)
405 if (buf_size == 0) { // No buffering
413 buf [buf_offset ++] = value;
414 if (buf_offset > buf_length)
415 buf_length = buf_offset;
420 public override int Read ([In,Out] byte[] dest, int dest_offset, int count)
422 if (handle == MonoIO.InvalidHandle)
423 throw new ObjectDisposedException ("Stream has been closed");
425 throw new ArgumentNullException ("destFile");
427 throw new NotSupportedException ("Stream does not support reading");
428 int len = dest.Length;
430 throw new ArgumentOutOfRangeException ("dest_offset", "< 0");
432 throw new ArgumentOutOfRangeException ("count", "< 0");
433 if (dest_offset > len)
434 throw new ArgumentException ("destination offset is beyond array size");
435 // reordered to avoid possible integer overflow
436 if (dest_offset > len - count)
437 throw new ArgumentException ("Reading would overrun buffer");
440 IAsyncResult ares = BeginRead (dest, dest_offset, count, null, null);
441 return EndRead (ares);
444 return ReadInternal (dest, dest_offset, count);
447 int ReadInternal (byte [] dest, int dest_offset, int count)
451 int n = ReadSegment (dest, dest_offset, count);
456 /* If there was already enough
457 * buffered, no need to read
458 * more from the file.
463 if (count > buf_size) {
464 /* Read as much as we can, up
468 n = ReadData (handle, dest,
472 /* Make the next buffer read
473 * start from the right place
478 n = ReadSegment (dest,
488 delegate int ReadDelegate (byte [] buffer, int offset, int count);
490 public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
491 AsyncCallback cback, object state)
493 if (handle == MonoIO.InvalidHandle)
494 throw new ObjectDisposedException ("Stream has been closed");
497 throw new NotSupportedException ("This stream does not support reading");
500 throw new ArgumentNullException ("buffer");
503 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
506 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
508 // reordered to avoid possible integer overflow
509 if (count > buffer.Length - offset)
510 throw new ArgumentException ("Buffer too small. count/offset wrong.");
513 return base.BeginRead (buffer, offset, count, cback, state);
515 ReadDelegate r = new ReadDelegate (ReadInternal);
516 return r.BeginInvoke (buffer, offset, count, cback, state);
519 public override int EndRead (IAsyncResult async_result)
521 if (async_result == null)
522 throw new ArgumentNullException ("async_result");
525 return base.EndRead (async_result);
527 AsyncResult ares = async_result as AsyncResult;
529 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
531 ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
533 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
535 return r.EndInvoke (async_result);
538 public override void Write (byte[] src, int src_offset, int count)
540 if (handle == MonoIO.InvalidHandle)
541 throw new ObjectDisposedException ("Stream has been closed");
543 throw new ArgumentNullException ("src");
545 throw new ArgumentOutOfRangeException ("src_offset", "< 0");
547 throw new ArgumentOutOfRangeException ("count", "< 0");
548 // ordered to avoid possible integer overflow
549 if (src_offset > src.Length - count)
550 throw new ArgumentException ("Reading would overrun buffer");
552 throw new NotSupportedException ("Stream does not support writing");
555 IAsyncResult ares = BeginWrite (src, src_offset, count, null, null);
560 WriteInternal (src, src_offset, count);
563 void WriteInternal (byte [] src, int src_offset, int count)
565 if (count > buf_size) {
566 // shortcut for long writes
571 MonoIO.Write (handle, src, src_offset, count, out error);
572 if (error != MonoIOError.ERROR_SUCCESS) {
573 // don't leak the path information for isolated storage
574 string fname = (anonymous) ? Path.GetFileName (name) : name;
575 throw MonoIO.GetException (fname, error);
584 int n = WriteSegment (src, src_offset + copied, count);
597 delegate void WriteDelegate (byte [] buffer, int offset, int count);
599 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
600 AsyncCallback cback, object state)
602 if (handle == MonoIO.InvalidHandle)
603 throw new ObjectDisposedException ("Stream has been closed");
606 throw new NotSupportedException ("This stream does not support writing");
609 throw new ArgumentNullException ("buffer");
612 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
615 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
617 // reordered to avoid possible integer overflow
618 if (count > buffer.Length - offset)
619 throw new ArgumentException ("Buffer too small. count/offset wrong.");
622 return base.BeginWrite (buffer, offset, count, cback, state);
624 FileStreamAsyncResult result = new FileStreamAsyncResult (cback, state);
625 result.BytesRead = -1;
626 result.Count = count;
627 result.OriginalCount = count;
630 MemoryStream ms = new MemoryStream ();
631 FlushBufferToStream (ms);
632 ms.Write (buffer, offset, count);
634 count = (int) ms.Length;
637 WriteDelegate w = new WriteDelegate (WriteInternal);
638 return w.BeginInvoke (buffer, offset, count, cback, state);
641 public override void EndWrite (IAsyncResult async_result)
643 if (async_result == null)
644 throw new ArgumentNullException ("async_result");
647 base.EndWrite (async_result);
651 AsyncResult ares = async_result as AsyncResult;
653 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
655 WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
657 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
659 w.EndInvoke (async_result);
663 public override long Seek (long offset, SeekOrigin origin)
667 if (handle == MonoIO.InvalidHandle)
668 throw new ObjectDisposedException ("Stream has been closed");
672 if(CanSeek == false) {
673 throw new NotSupportedException("The stream does not support seeking");
678 pos = Length + offset;
681 case SeekOrigin.Current:
682 pos = Position + offset;
685 case SeekOrigin.Begin:
690 throw new ArgumentException ("origin", "Invalid SeekOrigin");
694 /* LAMESPEC: shouldn't this be
695 * ArgumentOutOfRangeException?
697 throw new IOException("Attempted to Seek before the beginning of the stream");
700 if(pos < this.append_startpos) {
701 /* More undocumented crap */
702 throw new IOException("Can't seek back over pre-existing data in append mode");
709 buf_start = MonoIO.Seek (handle, pos,
713 if (error != MonoIOError.ERROR_SUCCESS) {
714 // don't leak the path information for isolated storage
715 string fname = (anonymous) ? Path.GetFileName (name) : name;
716 throw MonoIO.GetException (fname, error);
722 public override void SetLength (long length)
724 if (handle == MonoIO.InvalidHandle)
725 throw new ObjectDisposedException ("Stream has been closed");
728 throw new NotSupportedException("The stream does not support seeking");
730 if(CanWrite == false)
731 throw new NotSupportedException("The stream does not support writing");
734 throw new ArgumentOutOfRangeException("Length is less than 0");
740 MonoIO.SetLength (handle, length, out error);
741 if (error != MonoIOError.ERROR_SUCCESS) {
742 // don't leak the path information for isolated storage
743 string fname = (anonymous) ? Path.GetFileName (name) : name;
744 throw MonoIO.GetException (fname, error);
747 if (Position > length)
751 public override void Flush ()
753 if (handle == MonoIO.InvalidHandle)
754 throw new ObjectDisposedException ("Stream has been closed");
758 // The flushing is not actually required, in
759 //the mono runtime we were mapping flush to
760 //`fsync' which is not the same.
762 //MonoIO.Flush (handle);
765 public override void Close ()
768 GC.SuppressFinalize (this); // remove from finalize queue
771 public virtual void Lock (long position, long length)
773 if (handle == MonoIO.InvalidHandle)
774 throw new ObjectDisposedException ("Stream has been closed");
776 throw new ArgumentOutOfRangeException ("position must not be negative");
779 throw new ArgumentOutOfRangeException ("length must not be negative");
781 if (handle == MonoIO.InvalidHandle) {
782 throw new ObjectDisposedException ("Stream has been closed");
787 MonoIO.Lock (handle, position, length, out error);
788 if (error != MonoIOError.ERROR_SUCCESS) {
789 // don't leak the path information for isolated storage
790 string fname = (anonymous) ? Path.GetFileName (name) : name;
791 throw MonoIO.GetException (fname, error);
795 public virtual void Unlock (long position, long length)
797 if (handle == MonoIO.InvalidHandle)
798 throw new ObjectDisposedException ("Stream has been closed");
800 throw new ArgumentOutOfRangeException ("position must not be negative");
803 throw new ArgumentOutOfRangeException ("length must not be negative");
808 MonoIO.Unlock (handle, position, length, out error);
809 if (error != MonoIOError.ERROR_SUCCESS) {
810 // don't leak the path information for isolated storage
811 string fname = (anonymous) ? Path.GetFileName (name) : name;
812 throw MonoIO.GetException (fname, error);
824 protected override void Dispose (bool disposing)
826 protected virtual void Dispose (bool disposing)
829 if (handle != MonoIO.InvalidHandle) {
835 MonoIO.Close (handle, out error);
836 if (error != MonoIOError.ERROR_SUCCESS) {
837 // don't leak the path information for isolated storage
838 string fname = (anonymous) ? Path.GetFileName (name) : name;
839 throw MonoIO.GetException (fname, error);
842 handle = MonoIO.InvalidHandle;
855 // ReadSegment, WriteSegment, FlushBuffer,
856 // RefillBuffer and ReadData should only be called
857 // when the Monitor lock is held, but these methods
858 // grab it again just to be safe.
860 private int ReadSegment (byte [] dest, int dest_offset, int count)
862 if (count > buf_length - buf_offset) {
863 count = buf_length - buf_offset;
867 Buffer.BlockCopy (buf, buf_offset,
876 private int WriteSegment (byte [] src, int src_offset,
879 if (count > buf_size - buf_offset) {
880 count = buf_size - buf_offset;
884 Buffer.BlockCopy (src, src_offset,
888 if (buf_offset > buf_length) {
889 buf_length = buf_offset;
898 void FlushBufferToStream (Stream st)
901 if (CanSeek == true) {
903 MonoIO.Seek (handle, buf_start,
906 if (error != MonoIOError.ERROR_SUCCESS) {
907 // don't leak the path information for isolated storage
908 string fname = (anonymous) ? Path.GetFileName (name) : name;
909 throw MonoIO.GetException (fname, error);
912 st.Write (buf, 0, buf_length);
915 buf_start += buf_offset;
916 buf_offset = buf_length = 0;
920 private void FlushBuffer ()
925 if (CanSeek == true) {
926 MonoIO.Seek (handle, buf_start,
929 if (error != MonoIOError.ERROR_SUCCESS) {
930 // don't leak the path information for isolated storage
931 string fname = (anonymous) ? Path.GetFileName (name) : name;
932 throw MonoIO.GetException (fname, error);
935 MonoIO.Write (handle, buf, 0,
936 buf_length, out error);
938 if (error != MonoIOError.ERROR_SUCCESS) {
939 // don't leak the path information for isolated storage
940 string fname = (anonymous) ? Path.GetFileName (name) : name;
941 throw MonoIO.GetException (fname, error);
945 buf_start += buf_offset;
946 buf_offset = buf_length = 0;
950 private void FlushBufferIfDirty ()
956 private void RefillBuffer ()
960 buf_length = ReadData (handle, buf, 0,
964 private int ReadData (IntPtr handle, byte[] buf, int offset,
970 /* when async == true, if we get here we don't suport AIO or it's disabled
971 * and we're using the threadpool */
972 amount = MonoIO.Read (handle, buf, offset, count, out error);
973 if (error == MonoIOError.ERROR_BROKEN_PIPE) {
974 amount = 0; // might not be needed, but well...
975 } else if (error != MonoIOError.ERROR_SUCCESS) {
976 // don't leak the path information for isolated storage
977 string fname = (anonymous) ? Path.GetFileName (name) : name;
978 throw MonoIO.GetException (fname, error);
981 /* Check for read error */
983 throw new IOException ();
989 private void InitBuffer (int size, bool noBuffering)
993 // We need a buffer for the ReadByte method. This buffer won't
994 // be used for anything else since buf_size==0.
999 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1002 buf = new byte [size];
1007 buf_offset = buf_length = 0;
1013 internal const int DefaultBufferSize = 8192;
1015 private FileAccess access;
1018 private bool canseek;
1019 private long append_startpos;
1020 private bool anonymous;
1022 private byte [] buf; // the buffer
1023 private int buf_size; // capacity in bytes
1024 private int buf_length; // number of valid bytes in buffer
1025 private int buf_offset; // position of next byte
1026 private bool buf_dirty; // true if buffer has been written to
1027 private long buf_start; // location of buffer in file
1028 private string name = "[Unknown]"; // name of file.
1030 IntPtr handle; // handle to underlying file