tbFileSysTools Package

Author: GCuser99. On GitHub: GCuser99/tbFileSysTools, in the folder Package.

The tbFileSysTools contributed package, listed in the Available Packages dialog as File System Tools - Modern Replacement for FSO, reads and changes files, folders and drives, as the FileSystemObject of the Scripting Runtime does, and is written in twinBASIC. It reads and writes text files in any Windows code page, with detection of the encoding and handling of byte-order marks (BOMs), detects and converts line endings, streams files of any size, and works with paths longer than 260 characters.

It has two ways in. The FileSystemTools module holds the code, as procedures that a project calls by name, with no object to create. The FileSystemObject class has the same procedures as members of an object, for code written for the Scripting Runtime. The objects that both return, File, Folder, Drive and TextStream, and the collections Files, Folders and Drives, have the members of the Scripting Runtime’s objects of the same names, and some more.

Dim work As String, path As String
work = BuildPath(Environ$("TEMP"), GetTempName("fst", ""))
CreateFolder work
path = BuildPath(work, "notes.txt")

' Write a string as UTF-8 with a byte-order mark, then ask what the file holds.
StringToTextFile "caf" & ChrW$(233) & vbCrLf & "ok", path, encUtf8Bom
Debug.Print GetFileEncoding(path) = encUtf8Bom    ' True
Debug.Print GetFileLineEnding(path) = nlWindows   ' True
Debug.Print Len(TextFileToString(path))           ' 8

' Read it line by line; the encoding is found from the BOM.
Dim ts As TextStream
Set ts = OpenTextFile(path)
Do Until ts.AtEndOfStream
    Debug.Print Len(ts.ReadLine)
Loop
ts.Close
' Output:
' True
' True
' 8
' 4
' 2

DeleteFolder work

Note

This section documents version 1.1.1.0, the version on the package server, published under the MIT licence. The GitHub repository has moved on: its README describes version 1.8.0.0, and its change log lists changes from 1.2.0.0 on. Some members that the README names, such as FindInTextFiles, FileContainsText and IsFileLocked, do not exist in 1.1.1.0. The change log also records that later versions removed the useLongPathPrefix argument of GetAbsolutePathName and the skipIfUnchanged argument of NormalizeTextFile, which this version has, and that they changed some of the behaviour described on these pages.

The public types of the package are the module and the eight classes on these pages, and the enumerations that the module declares. Three private modules, FSTShared (shared file code and the error mapping), TextCodec (encoding, decoding and detection) and WinAPI (Windows declarations), are not part of the interface and are not described.

Differences from the Scripting Runtime

A call that works with the Scripting Runtime’s FileSystemObject keeps its name here. These are the differences.

Added members.

Members with more arguments. CreateFolder can create the folders above the new one. DeleteFile and DeleteFolder can ignore a missing target. GetAbsolutePathName takes a base directory. GetTempName takes a prefix, an extension and a folder to check.

Missing members. One: the Add method of the Folders collection, which creates a folder. CreateFolder does that. Every other member of the Scripting Runtime’s FileSystemObject, File, Folder, Drive and TextStream is here, and so are Count, Item and the enumerator of the three collections. Drives of the class FileSystemObject is a function, called with or without parentheses.

Arguments that mean something else. The Scripting Runtime selects the encoding of a text file with a Boolean (Unicode, in CreateTextFile) or a Tristate (Format, in OpenTextFile and OpenAsTextStream). Here the argument is a TextEncoding. Code that passes True or TristateTrue, which is -1, gets encAutoDetect, and for writing that is UTF-8, not UTF-16. The defaults also differ: a text file is written in UTF-8 without a BOM, where the Scripting Runtime writes ANSI, and a file opened for reading has its encoding detected, where the Scripting Runtime reads ANSI.

Results that differ.

  • GetSpecialFolder returns the path as a String, where the Scripting Runtime returns a Folder. It accepts 25 folders, and not only the Windows, System and Temporary folders.
  • FileAttribute has no Volume (8) and no Alias (1024). The label of a volume is Drive.VolumeName, and an alias is faReparsePoint. File and Folder add SetAttribute and HasAttribute, which change and test one attribute.
  • Folder.Size does not enter directory junctions and symbolic links. The Scripting Runtime does, and counts the target of a link twice when it is in the same tree: 2,500 bytes there for a folder that holds 1,500.
  • BuildPath and GetParentFolderName turn / into \. BuildPath("C:\a", "b/c") returns C:\a\b\c and the Scripting Runtime C:\a\b/c; GetParentFolderName("C:/a/b.txt") returns C:\a and the Scripting Runtime C:/a.
  • GetAbsolutePathName raises error 52 for an empty string, where the Scripting Runtime returns the current directory.
  • GetDrive accepts a full path and reduces it to its drive: GetDrive("C:\Windows") is C:. The Scripting Runtime raises error 5.
  • CopyFile, CopyFolder, MoveFile and MoveFolder put the source inside an existing destination folder, whether or not the destination ends in a backslash. The Scripting Runtime does so only with a backslash. Without one it raises error 70 for CopyFile, 58 for MoveFile and MoveFolder, and CopyFolder copies the contents of the source straight into the folder.
  • MoveFile moves a folder when source names one. The Scripting Runtime raises error 53.
  • DeleteFolder of a folder that holds a read-only file raises error 75 without force; the Scripting Runtime raises 70.
  • GetFileVersion returns an empty string for a file that does not exist. The Scripting Runtime raises an error.
  • At the end of a stream, ReadAll returns an empty string and Skip does nothing, where the Scripting Runtime raises error 62. A closed stream raises 52 here and 91 there.
  • Files.Item, Folders.Item and Drives.Item take a position as well as a name. A name that is not in the collection raises error 5; the Scripting Runtime raises 53, 76 and 68 for the three. Drives("C:") raises error 5, and the Scripting Runtime accepts it.
  • GetTempName returns eight random letters and digits and .tmp. The Scripting Runtime returns rad and five hexadecimal digits.
  • A path longer than 260 characters works in most procedures. The Scripting Runtime does not accept it: FileExists and FolderExists return False for it, GetFile raises error 53 and OpenTextFile raises 76.
  • StringToTextFile, ArrayToTextFile and WriteStream replace a read-only file without an error. CreateTextFile raises error 70 for it, as the Scripting Runtime’s does.

The same. For these failures the two raise the same error numbers: a missing file (53) or folder (76) from GetFile, GetFolder, CopyFile, DeleteFile, DeleteFolder and OpenTextFile; an existing folder or destination (58) from CreateFolder, CopyFile, MoveFile and CreateTextFile; a missing parent folder (76) from CreateFolder; a drive that does not exist (68) from GetDrive; a read-only destination or file (70) from CopyFile, DeleteFile and CreateTextFile; an unreadable folder (70) from Files, SubFolders and Size; the wrong direction of a stream (54) and a read at the end (62) from TextStream. Both enter a junction or symbolic link when they delete a folder, and both match a pattern against the 8.3 name of a file too.

Using the package with the Scripting Runtime

The package declares types that have the same names as those of the Scripting Runtime library: FileSystemObject, File, Folder, Drive, Files, Folders, Drives, TextStream, and the enumerations FileAttribute, IOMode, SpecialFolderConst, StandardStreamTypes and DriveTypeConst with their members, such as ForReading, Normal and Fixed. In a project that references both libraries, a bare name is found in the library that is listed first among the references, and no error is reported for the other. With the package first, Dim fso As New FileSystemObject creates the package’s class, and Dim f As File is the package’s type. With the Scripting Runtime first, they are the Scripting Runtime’s: f.Encoding then fails to compile (TB5027, Unrecognized member ‘Encoding’ on type ‘IFile’), and fso.BuildPath("C:\a", "b/c") returns C:\a\b/c. The enumeration members (ForReading, Normal, Fixed) compile in either order, and their values are the same in both libraries.

Write the name of the library before the type to be sure which one is meant. Both forms compile in either order:

Dim fso As New tbFileSysTools.FileSystemObject
Dim f As tbFileSysTools.File
Set f = fso.GetFile(fso.BuildPath(fso.GetSpecialFolder(sfSystemFolder), "kernel32.dll"))
Debug.Print f.Size

The other ways out are to remove the reference to the Scripting Runtime, or to list tbFileSysTools above it in the References dialog. The procedures of the module (FileExists, GetFile, CopyFile and the rest) do not collide with anything in the Scripting Runtime, which has no module-level procedures.

Errors

The procedures raise the error numbers that VB6 uses for the same failures. Err.Source names the operation, such as CopyFile, and Err.Description names the path. The errors of OpenTextFile, CreateTextFile and the other procedures that open a stream have an empty Err.Source and only the standard text of their number, without the path. Each entry says which numbers it raises.

  • 5 – Invalid procedure call or argument: a wildcard in a destination, a folder copied into itself, a text procedure given a binary file, a value that is not a member of an enumeration, a name that is not in a collection, and the file not found! of ReadStream.
  • 7 – Out of memory: a whole file that is too large for one array or String (more than 2 GB).
  • 9 – Subscript out of range: a position outside a collection.
  • 52 – Bad file name or number: a name with a character that Windows does not allow, and a closed TextStream.
  • 53 – File not found.
  • 54 – Bad file mode: a read on a stream open for writing, or the reverse.
  • 58 – File already exists. The text says File also when a folder is in the way.
  • 62 – Input past end of file.
  • 68 – Device unavailable: a drive letter that is not in use.
  • 70 – Permission denied: access is refused, or the file is in use.
  • 71 – Disk not ready: a drive with no media.
  • 75 – Path/File access error: any other Windows failure. The text ends with the Windows code, such as 145 for a folder that is not empty and 206 for a path that is too long.
  • 76 – Path not found.

Windows reports a failure with its own code, and the package maps it to one of those numbers: file not found (2) to 53, path not found (3) and bad directory name (267) to 76, file exists (80 and 183) to 58, access denied and the sharing violations (5, 32 and 33) to 70, and invalid name (123) to 52.

Note

Version 1.1.1.0 raises some errors that the Scripting Runtime does not raise, or other numbers. The sample shows the first three.

Dim work As String, s As String, ts As TextStream
work = BuildPath(Environ$("TEMP"), GetTempName("fst", ""))
CreateFolder work

On Error Resume Next
StringToTextFile "x", BuildPath(work, ChrW$(&H8A9E&) & ".txt")
Debug.Print Err.Number                              ' 52
Err.Clear
StringToTextFile "x", BuildPath(work, ChrW$(&H65E5&) & ".txt")
Debug.Print Err.Number                              ' 0
s = TextFileToString(BuildPath(work, "missing.txt"))
Debug.Print Err.Number                              ' 5
Err.Clear
Set ts = OpenTextFile(BuildPath(work, "missing.txt"))
Debug.Print Err.Number                              ' 53
Err.Clear
StringToTextFile "x", BuildPath(work, "nofolder\x.txt")
Debug.Print Err.Number                              ' -2147467259
Err.Clear
On Error GoTo 0

DeleteFolder work

Test with FileExists first when the number of a missing-file error matters.

Classes and modules

See Also

  • Open statement – the VBA statement that opens a file for input and output
  • FileSystem module – the VBA functions and statements for files and folders: FileCopy, Kill, MkDir, Dir and the rest
  • FileProps package – the Windows shell properties of a file
  • TBMANLIB_Dialog package – the Open, Save and folder dialogs, with path helpers
  • tbRegExp package – regular expressions, by the same author