Skip to content

Repository files navigation

tbFileSysTools

A modern replacement for Scripting Runtime's FileSystemObject, written in twinBASIC.

It does everything FSO does, plus many things FSO cannot: full text-encoding support, files larger than 2 GB, line-ending detection and normalization, and long paths beyond the legacy 260-character limit.

' Read a UTF-8 file with a BOM, a UTF-16LE file, and a Shift-JIS file.' You don't have to know which is which.Dim text AsString
text = TextFileToString("data.txt") ' encoding auto-detected

Comparison with Scripting FSO

Scripting.FileSystemObject shipped in 1996 and shows it:

FeatureFSOtbFileSysTools
Text encodingsANSI and UTF-16 onlyAny Windows code page, with BOM handling and auto-detection
Files > 2 GBReads them as empty, silentlyStreams them, in both directions
Appending to a > 2 GB fileNot possibleWorks
Line endingsNo supportDetect, preserve, normalize
Paths > 260 charsFails, and FileExistsreturns False on a file that existsSupported transparently

The claims above were measured through comprehensive testing.


Install

The library comes in three forms — pick whichever fits how you work:

FormUse it whenUpdates
twinBASIC packageYou want a referenced .twinpack, no copied codeAutomatic via the Package Server
Single-file drop-inYou want one .twin in your project, no referenceManual
ActiveX DLLYou're calling from VBA, twinBASIC, or another COM hostRe-install the DLL (Inno script included)

The package can be referenced through twinBASIC's Package Server. In the IDE, click References -> Available Packages. Scroll to find the entry and click the checkbox on the left of the entry. Then click Apply Changes button in lower-right.

Package


Two ways in

The library has one implementation and two pathways to it.

FileSystemTools — the Standard Module

Preferred for twinBASIC code. Call it directly; there's no object to create. Potentially smaller compile footprint than the object version.

If FileExists(path) Then
Debug.Print GetFile(path).Size
End If

FileSystemObject — the Class Module

A drop-in replacement for the Scripting Runtime object. Use it for hosts that need an object, or when porting existing FSO code that you'd rather not rewrite.

Dim fso AsNew FileSystemObject
Debug.Print fso.GetFile(path).Size

The class is a one-line delegation to the module for every member — same behavior, same defaults. It uses FSO's argument names (fileSpec, folderSpec) so named arguments in ported code keep working.

Note: the class is deliberately named FileSystemObject, the same as Scripting's. In a project referencing both, qualify it - tbFileSysTools.FileSystemObject, drop the Scripting Runtime reference, or move tbFileSysTools' position in the References dialog ABOVE Scripting Runtime so that it takes unqualified precedence.


Text encodings

The headline feature. FSO can read system ANSI and UTF-16. This reads anything Windows has a code page for.

' Auto-detect: BOM first, then UTF-16/32 and UTF-8 heuristics, then system ANSI.Dim s AsString
s = TextFileToString("mystery.txt")
' Or be explicit. A BOM in the file still wins.
s = TextFileToString("legacy.txt", encGB18030)
' Write with a BOM by choosing a BOM-bearing encoding.
StringToTextFile s, "out.txt", encUtf8Bom
' What encoding IS this file?
Debug.Print GetFileEncoding("mystery.txt") ' e.g. encUtf16Bom

Supported: UTF-8, UTF-16 LE/BE, UTF-32 LE/BE, UTF-7, GB2312, GB18030, Big5, Latin-1, Latin-9, US-ASCII, system ANSI — each with and without a BOM (if applicable) — plus any other code page installed on the machine.

Auto-detection on Read. Defaults to auto-detect using a reliable heuristical algorithm but user can specify a code-page if known.

Line endings

Windows (CRLF), Unix (LF) and classic-Mac (CR) endings are all supported and auto-detected on read — and a file that mixes styles is reported as mixed rather than guessed at. You don't have to know a file's convention to read, normalize, or append to it.

Debug.Print GetFileLineEnding("script.sh") ' nlUnix' All textfile read/write procedures allow for lineEnding controlDim ts asTextStreamSet ts = CreateTextFile(filePath, lineEnding:=nlUnix)

Appending to an existing file adopts that file's newline style, so you can't accidentally turn a clean file into a mixed one.

Text File Normalization

The NormalizeTextFile/File.Normalize methods make checking and normalizing encoding and line endings easy:

Dim f AsFileDim fdrPath AsString
fdrPath = "path\to\my\twin\files"' Normalize .twin files to twinBASIC encoding and line endings (skipped if already there)For Each f In GetFolder(fdrPath).Files
If f.ExtensionName = "twin"Then
f.Normalize toEncoding:=encUtf8, toLineEnding:=nlWindows
End IfNext f

Large files

FSO cannot read a file larger than 2 GB. It doesn't error — it returns an empty string. This library streams them.

Dim ts AsTextStreamSet ts = OpenTextFile("huge.log", ForReading)
Do Until ts.AtEndOfStream
ProcessLine ts.ReadLine() ' no size limitLoop
ts.Close' Appending to a 4 GB file also works.Set ts = OpenTextFile("huge.log", ForAppending)
ts.WriteLine "another line"
ts.Close

ReadLine, Read, Write and append are unbounded. ReadAll is capped at 2 GB by the size of a VB String and raises a clear error rather than misbehaving — use ReadLine for anything larger.

Multi-byte encodings are handled correctly across chunk boundaries, including surrogate pairs split by a 64 KB read. This is verified against 54 million lines of mixed 1-, 2-, 3- and 4-byte characters.


Long paths

Windows' legacy MAX_PATH limit is 260 characters. Scripting.FileSystemObject is bound by it — on a longer path, FileExists returns False for a file that exists, and GetFile and OpenTextFile raise "path not found". This library supports long paths transparently: reading, writing, creating, copying, moving, deleting, enumerating, normalizing and merging all work well past 260 characters. You pass a normal path; the library canonicalizes it and applies the \\?\ prefix to the underlying Win32 calls when needed. No prefix, no flag, no manifest — it works regardless of the LongPathsEnabled registry setting, and identically with it enabled.

A few members remain 260-bound because the specific Win32 APIs behind them don't accept the prefix: GetFileType, GetFileVersion, GetRelativePath, File.ShortPath, SetCurrentDir, and wildcard patterns (matched items are unaffected). Each degrades or raises clearly rather than returning a wrong answer — see each member's description for its exact behavior.

' A 400-plus-character path is just a path.Dim deep AsString
deep = "C:\...\a\very\deeply\nested\...\structure\notes.txt"' > 260 chars
StringToTextFile "hello", deep
Debug.Print FileExists(deep) ' True

Objects

File, Folder, Drive and their collections work as they do in FSO.

Dim f AsFolderSet f = GetFolder("C:\Projects")
Debug.Print f.Size ' total bytes, subtreeFor Each fl In f.Files
Debug.Print fl.Name, fl.Size, fl.DateLastModified
Next

File and Folder objects are live: every property read re-stats the path, so values are never stale — and a deleted file raises rather than reporting stale values. The trade-off is that each property read costs a round trip, so hoist values out of tight loops.

Folder.Files and Folder.SubFolders return a snapshot of the membership. The objects inside are live; the list is not.


Deviations from FSO

Parity is the goal, but not at any price. Each of these was checked against the Scripting.FileSystemObject, and broken deliberately:

Folder.Size does not follow directory reparse points. FSO does. A junction pointing into its own subtree makes FSO double-count (measured: 2500 vs 1500), and a junction pointing at an ancestor makes it recurse until it dies. This library reports what physically lives in the tree.

An unreadable folder raises. FSO silently reports it as empty. A size or file list that quietly omits a subtree that can't be read is worse than an error.

FileAttribute.Volume and .Alias are not provided.Volume has no Win32 equivalent (use Drive.VolumeName). Alias is FILE\_ATTRIBUTE\_REPARSE\_POINT under a misleading name — and collides with VBA's vbAlias (64 vs 1024). Use ReparsePoint.


Beyond FSO...

The following members are either added, or their function significantly improved.

MemberEnhancement Description
CleanFileNameStrip reserved characters and device names
CreateFolderOptionally create path intermediates
CreateTextFileCreate any format - not just ANSI/UTF-16
DeleteFile / DeleteFolderOptional ignoreMissing argument
GetAbsolutePathNameOptional baseDirectory argument
GetCurrentDir / SetCurrentDirGets/Sets the current directory or folder
GetFileEncodingDetect a file's encoding
GetFileLineEndingDetect CRLF / LF / CR / mixed
GetFilePathsEnumerate with a wildcard, recursion, hidden/system filters
GetFileTypeShell type description ("Text Document")
GetRelativePathPath from A to B
GetSpecialFolder25 known folders, including FSO's 3
IsFileLockedDetermines if locked by another process
MergeTextFilesMerges two text files, normalizing the encoding to the first
NormalizeTextFileRewrite encoding + newlines in place, idempotently
OpenTextFileFormat auto-detection or user-specified code page
ReadStream / WriteStreamRaw bytes, Unicode-safe
RenameIn-place rename
TextFileToArray / ArrayToTextFileWhole-file line I/O
TextFileToString / StringToTextFileWhole-file text I/O
File.EncodingDetect a file's encoding
File.HasAttributeDetermines if an attribute is set
File.IsLockedDetermines if locked by another process
File.LineEndingDetect CRLF / LF / CR / mixed
File.NormalizeRewrite encoding + newlines in place, idempotently
File.OpenAsTextStreamFormat auto-detection or user-specified codepage
File.SetAttributeSets a single attribute
File.ToStreamReads file to byte array
File.ToStringReads file to string
File.VersionGets the file version string
Folder.HasAttributeDetermines if an attribute is set
Folder.SetAttributeSets a single attribute
TextStream.EncodingReturns a file's encoding
TextStream.IsStreamingWhether byte-streaming or buffered access is supported

Project Structure

FileDescription
FileSystemToolsThe API where all the logic lives
FileSystemObjectCOM-creatable thin wrapper class over the above
TextStreamStreaming text reader/writer
TextCodecEncoding detection, encode/decode, BOM handling
FSTSharedShared file/folder/drive procs
WinAPIWinDevLib's Win32 declarations - not needed if referenced to WinDevLib
File, Folder, DriveObjects
Files, Folders, DrivesCollections

Acknowledgements

  • fafalone — for WinDevLib, source of standardized Win32 API declares.
  • Wayne Phillips — for twinBASIC.

License

MIT © 2026 GCUser99

About

Modern replacement for Scripting.FileSystemObject written in twinBASIC

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages