diff --git a/src/Runtime/XSharp.Core/Types/FileStream.prg b/src/Runtime/XSharp.Core/Types/FileStream.prg
index 2da1a42b08..e91a7e984f 100644
--- a/src/Runtime/XSharp.Core/Types/FileStream.prg
+++ b/src/Runtime/XSharp.Core/Types/FileStream.prg
@@ -91,11 +91,17 @@ BEGIN NAMESPACE XSharp.IO
///
STATIC METHOD CreateFileStream (path AS STRING, mode AS FileMode, faccess AS FileAccess, share AS FileShare, bufferSize AS LONG, options AS FileOptions) AS FileStream
IF share != FileShare.None
+#ifdef NET6_0_OR_GREATER
+ // The shared stream is plain managed code here, so there is no platform check needed.
+ RETURN CreateWin32FileStream(path, mode, faccess, share, bufferSize, options)
+#else
+ // Unchanged: the shared stream does its IO with the Win32 API, so Windows only.
IF RuntimeState.RunningOnWindows
RETURN CreateWin32FileStream(path, mode, faccess, share, bufferSize, options)
ELSE
RETURN XsFileStream{path, mode, faccess, share, 0xFFFF, options}
ENDIF
+#endif
ELSE
IF UseBufferedFileStream
RETURN XsBufferedFileStream{path, mode, faccess, share, bufferSize, options}
diff --git a/src/Runtime/XSharp.Core/Types/SharedFileStream.prg b/src/Runtime/XSharp.Core/Types/SharedFileStream.prg
index ec1e66c261..1d1923f9b5 100644
--- a/src/Runtime/XSharp.Core/Types/SharedFileStream.prg
+++ b/src/Runtime/XSharp.Core/Types/SharedFileStream.prg
@@ -1,6 +1,6 @@
//
-// Copyright (c) XSharp B.V. All Rights Reserved.
-// Licensed under the Apache License, Version 2.0.
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
@@ -11,6 +11,75 @@ USING System.Collections.Generic
BEGIN NAMESPACE XSharp.IO
///
+#ifdef NET6_0_OR_GREATER
+ // .Net 6 and later. FileStream reads and writes at an explicit offset and does not cache the length
+ // of a file that others may write to, so it reports the truth for shared access by itself. The hand
+ // written Win32 layer below is not only unnecessary there, it is broken: it never overrode Position,
+ // and the .Net 6 rewrite removed the re-verification of the OS position that used to paper over that.
+ ///
+ /// This class used to do all of its IO with the Win32 API (ReadFile, WriteFile, SetFilePointerEx,
+ /// LockFile) on the raw file handle of the base class, because the FileStream of that time cached the
+ /// file position and the file length, which is wrong for a file that other processes may change.
+ ///
+ /// It does not do that anymore. The .Net FileStream reads and writes at an explicit offset and does not
+ /// cache the length of a file that others may write to, so it reports the truth for shared access by
+ /// itself, on every platform. What is left here is the one thing that is special about a shared stream:
+ /// it must not buffer, and Flush() must commit to disk.
+ ///
+ /// The name is kept because this class is public and is handed to user code through DBI_FILESTREAM.
+ ///
+ CLASS XsWin32FileStream INHERIT XsFileStream
+ INTERNAL CONSTRUCTOR(path AS STRING, mode AS FileMode, faccess AS FileAccess, share AS FileShare, bufferSize AS LONG, options AS FileOptions)
+ // bufferSize 1 means unbuffered. A buffer would hand out bytes that another process has already
+ // changed, and would hold back bytes that another process is waiting for, so the bufferSize of
+ // the caller is deliberately ignored: for shared access there is no good buffer size but none.
+ SUPER(path, mode, faccess, share, 1, options)
+ RETURN
+
+ ///
+ /// A shared file stream commits to disk, so that other processes see the change.
+ PUBLIC OVERRIDE METHOD Flush() AS VOID
+ SELF:Flush(TRUE)
+ RETURN
+
+ ///
+ PUBLIC OVERRIDE METHOD Lock(position AS INT64, length AS INT64) AS VOID
+ TRY
+ SUPER:Lock(position, length)
+ CATCH e AS Exception
+ SELF:__SetLockError()
+ THROW e
+ END TRY
+ RETURN
+
+ ///
+ PUBLIC OVERRIDE METHOD Unlock(position AS INT64, length AS INT64) AS VOID
+ TRY
+ SUPER:Unlock(position, length)
+ CATCH e AS Exception
+ SELF:__SetLockError()
+ THROW e
+ END TRY
+ RETURN
+
+ // A failed lock on a shared file is what NetErr() reports in the xBase world. The base class has
+ // already recorded the exception through SetErrorState(), but that only sets NetErr for a sharing
+ // violation (32) and a lock violation is 33. The Win32 implementation used below .Net 6 sets NetErr
+ // itself, so it has to happen here as well - and only here, so that exclusive streams keep behaving
+ // exactly like they always did.
+ PRIVATE METHOD __SetLockError() AS VOID
+ IF RuntimeState.FileError == 0
+ FError(33) // DOS lock violation
+ ENDIF
+ NetErr(TRUE)
+ RETURN
+
+ END CLASS
+#else
+ // Before .Net 6, unchanged. FileStream of that time cached the file position and the file length,
+ // which is wrong for a file that other processes may change, so the IO is done with the Win32 API.
+ // Position is not overridden here on purpose: until .Net 5 FileStream re-verified the OS position on
+ // every access once the SafeFileHandle had been exposed, which keeps this correct.
CLASS XsWin32FileStream INHERIT XsFileStream
PRIVATE hFile AS IntPtr
PRIVATE smallBuff AS BYTE[]
@@ -185,6 +254,6 @@ BEGIN NAMESPACE XSharp.IO
#endregion
END CLASS
-
-
+#endif
+
END NAMESPACE