OSFiles

Picks files and folders through the dialog the host offers. On Windows the class opens an Excel FileDialog. On macOS it runs an AppleScript picker. LoadFile, LoadFiles, LoadFolder and LoadFolders each show one dialog and keep what the user chose; File, Files, Folder and Folders answer it back. The four HasValid members say whether the selection points at something that exists on disk.

READING A MULTI SELECTION

HasNextFile and NextFile walk the selected files one at a time, and ResetFilesIterator puts the cursor back to the first one. The folder members work the same way.

THE LAST FOLDER

Each selection stores its folder in the workbook-level name OSFiles_LastFolder, and the next dialog opens on that folder. State bag for OS detection, selections, and cursor positions. Exposed methods Workbook-level Name used to persist the last used folder path.

Version: 1.0 (2026-02-09)

Initialization

Create #

create

Factory returning an initialised interface

Signature:

Public Function Create() As OSFiles

Returns: OSFiles. Ready to use.


Elements

File #

file

Current loaded file path

Signature:

Public Property Get File() As String

Returns: String. The first selected file path.


Files #

files

All loaded file paths from a multi-selection

Signature:

Public Property Get Files() As Variant

Returns: Variant. Zero-based string array of file paths.


Folder #

folder

Current loaded folder path

Signature:

Public Property Get Folder() As String

Returns: String. The first selected folder path.


Folders #

folders

All loaded folder paths from a multi-selection

Signature:

Public Property Get Folders() As Variant

Returns: Variant. Zero-based string array of folder paths.


Selecting

LoadFolder #

load-folder

Show a single-folder picker dialog

Signature:

Public Sub LoadFolder()

LoadFile #

load-file

Show a single-file picker dialog

Signature:

Public Sub LoadFile(ByVal filters As String, _ 
                     Optional ByVal customMacFilter As String = vbNullString)

Parameters:

  • filters: String. File type filters (e.g. "*.xlsx").
  • customMacFilter: String. Optional UTI override for macOS. Defaults to vbNullString.

LoadFiles #

load-files

Show a multi-file picker dialog

Signature:

Public Sub LoadFiles(ByVal filters As String, _
                      Optional ByVal customMacFilter As String = vbNullString)

Parameters:

  • filters: String. File type filters.
  • customMacFilter: String. Optional UTI override for macOS. Defaults to vbNullString.

LoadFolders #

load-folders

Show a multi-folder picker dialog

Signature:

Public Sub LoadFolders()

Iteration

ResetFilesIterator #

reset-files-iterator

Reset the file iteration cursor to the beginning

Signature:

Public Sub ResetFilesIterator()

ResetFoldersIterator #

reset-folders-iterator

Reset the folder iteration cursor to the beginning

Signature:

Public Sub ResetFoldersIterator()

HasNextFile #

has-next-file

Check if another file is available in the iterator

Signature:

Public Function HasNextFile() As Boolean

HasNextFolder #

has-next-folder

Check if another folder is available in the iterator

Signature:

Public Function HasNextFolder() As Boolean

NextFile #

next-file

Return the next file path and advance the cursor

Signature:

Public Function NextFile() As String

NextFolder #

next-folder

Return the next folder path and advance the cursor

Signature:

Public Function NextFolder() As String

Validation

HasValidFile #

has-valid-file

Whether the loaded file path is valid

Signature:

Public Function HasValidFile() As Boolean

Returns: Boolean. True when a non-empty file path exists.


HasValidFiles #

has-valid-files

Whether the loaded files array contains entries

Signature:

Public Function HasValidFiles() As Boolean

Returns: Boolean. True when at least one file was selected.


HasValidFolder #

has-valid-folder

Whether the loaded folder path is valid

Signature:

Public Function HasValidFolder() As Boolean

Returns: Boolean. True when a non-empty folder path exists.


HasValidFolders #

has-valid-folders

Whether the loaded folders array contains entries

Signature:

Public Function HasValidFolders() As Boolean

Returns: Boolean. True when at least one folder was selected.


Internal members (not exported)

Initialization

Class_Initialize #

class-initialize

Set default values for file and folder state

Signature:

Private Sub Class_Initialize()

ResetFileState #

reset-file-state

Clear file-related state and reset the iteration cursor

Signature:

Private Sub ResetFileState()

ResetFolderState #

reset-folder-state

Clear folder-related state and reset the iteration cursor

Signature:

Private Sub ResetFolderState()

EmptyStringArray #

empty-string-array

Return an empty variant array

Signature:

Private Function EmptyStringArray() As Variant

Returns: Variant. Empty array.


SelectFoldersOnWindows #

select-folders-on-windows

Show the Windows multi-folder picker

Signature:

Private Function SelectFoldersOnWindows() As Variant

Returns: Variant. Zero-based string array of folder paths.


TryGetArrayBounds #

try-get-array-bounds

Safely probe array bounds

Signature:

Private Function TryGetArrayBounds(ByVal candidate As Variant, _
                                   ByRef lowerBound As Long, _
                                   ByRef upperBound As Long) As Boolean

CountArrayItems #

count-array-items

Count the number of items in a variant array

Signature:

Private Function CountArrayItems(ByVal candidate As Variant) As Long

ToStringArray #

to-string-array

Coerce a variant into a zero-based string array

Signature:

Private Function ToStringArray(ByVal value As Variant) As Variant

CloneStringArray #

clone-string-array

Create a safe copy of a string array

Signature:

Private Function CloneStringArray(ByVal source As Variant) As Variant

HasNextArrayItem #

has-next-array-item

Check whether the cursor is within array bounds

Signature:

Private Function HasNextArrayItem(ByVal items As Variant, ByVal cursor As Long) As Boolean

GetNextArrayItem #

get-next-array-item

Fetch the item at the cursor and advance it

Signature:

Private Function GetNextArrayItem(ByVal items As Variant, _
                                  ByRef cursor As Long) As String

SetFilesSelection #

set-files-selection

Persist file selections and prime the iterator

Signature:

Private Sub SetFilesSelection(ByVal selectedItems As Variant)

SetFoldersSelection #

set-folders-selection

Persist folder selections and prime the iterator

Signature:

Private Sub SetFoldersSelection(ByVal selectedItems As Variant)

FirstStringOrEmpty #

first-string-or-empty

Return the first array entry or an empty string

Signature:

Private Function FirstStringOrEmpty(ByVal source As Variant) As String

NormalizeFolderPath #

normalize-folder-path

Trim trailing separators and clean the folder path

Signature:

Private Function NormalizeFolderPath(ByVal folderPath As String) As String

ExtractFolderFromPath #

extract-folder-from-path

Identify the parent directory from a file path

Signature:

Private Function ExtractFolderFromPath(ByVal filePath As String) As String

RememberFolder #

remember-folder

Cache the folder locally and in the workbook

Signature:

Private Sub RememberFolder(ByVal folderPath As String)

RememberFolderFromFiles #

remember-folder-from-files

Persist the parent directory of the first selected file

Signature:

Private Sub RememberFolderFromFiles(ByVal filesSelection As Variant)

RememberFolderFromFolders #

remember-folder-from-folders

Persist the first folder from a folder selection

Signature:

Private Sub RememberFolderFromFolders(ByVal folderSelection As Variant)

ResolveLastKnownFolder #

resolve-last-known-folder

Resolve the last-used folder from cache or workbook storage

Signature:

Private Function ResolveLastKnownFolder() As String

HasWorkbookContext #

has-workbook-context

Check whether ThisWorkbook is accessible

Signature:

Private Function HasWorkbookContext() As Boolean

ReadLastFolderFromWorkbook #

read-last-folder-from-workbook

Read the last-used folder from a workbook-level Name

Signature:

Private Function ReadLastFolderFromWorkbook() As String

WriteLastFolderToWorkbook #

write-last-folder-to-workbook

Persist the folder path in a hidden workbook Name

Signature:

Private Sub WriteLastFolderToWorkbook(ByVal folderPath As String)

EncodeWorkbookStorageValue #

encode-workbook-storage-value

Convert a folder path into a Name.RefersTo-safe string literal

Signature:

Private Function EncodeWorkbookStorageValue(ByVal folderPath As String) As String

DecodeWorkbookStorageValue #

decode-workbook-storage-value

Undo Name serialization to recover the original folder path

Signature:

Private Function DecodeWorkbookStorageValue(ByVal storedValue As String) As String

WrapAppleScriptString #

wrap-apple-script-string

Escape a string for safe insertion into AppleScript

Signature:

Private Function WrapAppleScriptString(ByVal value As String) As String

WrapAppleScriptAlias #

wrap-apple-script-alias

Build an AppleScript alias literal from a colon path

Signature:

Private Function WrapAppleScriptAlias(ByVal aliasPath As String) As String

BuildAppleScriptList #

build-apple-script-list

Translate a VBA array into an AppleScript list literal

Signature:

Private Function BuildAppleScriptList(ByVal entries As Variant) As String

FilterExtensions #

filter-extensions

Split a filter string into its bare extensions

Signature:

Private Function FilterExtensions(ByVal filters As String) As Variant

Callers write the filter in whichever shape reads well at the call site: ".xlsx", ".xlsb;.xlsx", ".xlsb, .xlsx". They all mean the same thing, so the separator is read as either a semicolon or a comma and the "." is dropped.

This used to be a Select Case over the whole string, which meant a filter had to be spelled exactly as one of the listed literals or it fell to the Else branch and answered xlsx alone. clickLoadFileDic asks for ".xlsb;.xlsx" and the branch it needed was written "*.xlsb, *.xlsx", so loading a setup on macOS offered xlsx files and hid every .xlsb.

Parameters:

Returns: Variant. Zero-based array of lower case extensions, without the dot.


UtiForExtension #

uti-for-extension

The macOS uniform type identifier of one Excel extension

Signature:

Private Function UtiForExtension(ByVal extension As String) As String

Parameters:

Returns: String. The UTI, or an empty string when it is not one we handle.


BuildWindowsFilter #

build-windows-filter

Rewrite a filter string in the shape Excel's FileDialog documents

Signature:

Private Function BuildWindowsFilter(ByVal filters As String) As String

FileDialogFilters.Add takes its extensions separated by a comma and a space. A caller writing them with a semicolon is saying the same thing, so the string is rebuilt here rather than passed on as typed.

Parameters:

Returns: String. "*.xlsb, *.xlsx", or the original when nothing could be read.


BuildMacFilterList #

build-mac-filter-list

Construct the UTI list for the AppleScript file picker

Signature:

Private Function BuildMacFilterList(ByVal filters As String, _
                                    ByVal customMacFilter As String) As String

Parameters:

Returns: String. An AppleScript list of UTIs.


ParseMacSelectionResult #

parse-mac-selection-result

Split newline-delimited POSIX paths from AppleScript output

Signature:

Private Function ParseMacSelectionResult(ByVal response As String) As Variant

NormalizeFolderSelections #

normalize-folder-selections

Normalize picker results into clean folder paths

Signature:

Private Function NormalizeFolderSelections(ByVal folderSelection As Variant) As Variant

ConvertPosixToColonPath #

convert-posix-to-colon-path

Translate a POSIX path to AppleScript colon-delimited format

Signature:

Private Function ConvertPosixToColonPath(ByVal posixPath As String) As String

GetMacDefaultFolderAlias #

get-mac-default-folder-alias

Resolve the default folder alias for macOS pickers

Signature:

Private Function GetMacDefaultFolderAlias() As String

BuildMacFileSelectionScript #

build-mac-file-selection-script

Compose the AppleScript for file selection on macOS

Signature:

Private Function BuildMacFileSelectionScript(ByVal filters As String, _
                                             ByVal customMacFilter As String, _
                                             ByVal defaultAlias As String, _
                                             ByVal allowMultiple As Boolean) As String

BuildMacFolderSelectionScript #

build-mac-folder-selection-script

Compose the AppleScript for folder selection on macOS

Signature:

Private Function BuildMacFolderSelectionScript(ByVal defaultAlias As String, _
                                               ByVal allowMultiple As Boolean) As String

RunMacFilePicker #

run-mac-file-picker

Execute the AppleScript file picker and return POSIX paths

Signature:

Private Function RunMacFilePicker(ByVal filters As String, _
                                  ByVal customMacFilter As String, _
                                  ByVal allowMultiple As Boolean) As Variant

RunMacFolderPicker #

run-mac-folder-picker

Execute the AppleScript folder picker and return POSIX paths

Signature:

Private Function RunMacFolderPicker(ByVal allowMultiple As Boolean) As Variant

Elements

OS #

os

Current operating system flag

Signature:

Public Property Get OS() As String

Returns: String. "Mac" or "Windows".


OS #

os-set

Override the detected OS flag

Signature:

Public Property Let OS(ByVal currentOS As String)

Parameters:


File #

file-set

Assign a single file path through the selection pipeline

Signature:

Private Property Let File(ByVal ff As String)

Parameters:


Folder #

folder-set

Assign a single folder path through the selection pipeline

Signature:

Private Property Let Folder(ByVal ff As String)

Parameters:


SelectFolderOnMac #

select-folder-on-mac

Show the macOS single-folder picker

Signature:

Private Function SelectFolderOnMac() As String

SelectFoldersOnMac #

select-folders-on-mac

Show the macOS multi-folder picker

Signature:

Private Function SelectFoldersOnMac() As Variant

SelectFolderOnWindows #

select-folder-on-windows

Show the Windows single-folder picker

Signature:

Private Function SelectFolderOnWindows() As String

SelectFilesOnWindows #

select-files-on-windows

Show the Windows multi-file picker

Signature:

Private Function SelectFilesOnWindows(ByVal filters As String) As Variant

SelectFileOnMac #

select-file-on-mac

Show the macOS single-file picker

Signature:

Private Function SelectFileOnMac(ByVal filters As String, _ 
                                 Optional ByVal customMacFilter As String = vbNullString) As String

SelectFilesOnMac #

select-files-on-mac

Show the macOS multi-file picker

Signature:

Private Function SelectFilesOnMac(ByVal filters As String, _
                                  Optional ByVal customMacFilter As String = vbNullString) As Variant

SelectFileOnWindows #

select-file-on-windows

Show the Windows single-file picker

Signature:

Private Function SelectFileOnWindows(ByVal filters As String) As String

Test Support

AssignFilesForTesting #

assign-files-for-testing

Inject file selections without showing a dialog

Signature:

Public Sub AssignFilesForTesting(ByVal selections As Variant)

Parameters:


AssignFoldersForTesting #

assign-folders-for-testing

Inject folder selections without showing a dialog

Signature:

Public Sub AssignFoldersForTesting(ByVal selections As Variant)

Parameters:


Used in (15 file(s))