Today, I thought I’d demonstrate a couple distinct methods that can be used to validate whether a file exists or not.
- VBA Dir Function
- File System Object‘s (FSO) FileExists Method
- GetAttr Function
- FindFirstFileA API
- PathFileExistsA API
- WizHook (Access Only)
- Windows Management Instrumentation (WMI)
Using The VBA Dir Function
'---------------------------------------------------------------------------------------
' Procedure : File_Exist
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Test for the existence of a file; Returns True/False
' True => File exists
' False => File does not exist
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile : Fully qualified path and filename to check for
'
' Usage:
' ~~~~~~
' ? File_Exist("C:\Temp\Flow-Undo-Dont.jpg")
' Returns -> True
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2007-03-06 Initial Release
' 2 2022-11-24 Update Function header
' Update Error Handler
'---------------------------------------------------------------------------------------
Function File_Exist(ByVal sFile As String) As Boolean
On Error GoTo Error_Handler
File_Exist = (Len(Dir(sFile)) > 0)
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: File_Exist" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
Usage Example
If File_Exist("C:\Temp\Flow-Undo-Dont.jpg") = True Then
'Do something because the file exists
End If
Or
If File_Exist("C:\Temp\Flow-Undo-Dont.jpg") = False Then
MsgBox "The file could not be found!", vbCritical Or vbOKOnly, "Operation Aborted"
End If
Using The File System Object’s FileExists Method
'---------------------------------------------------------------------------------------
' Procedure : FSO_File_Exist
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Test for the existence of a file; Returns True/False
' True => File exists
' False => File does not exist
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding -> None required
' Early Binding -> Microsoft Scripting Runtime
' Also check out: https://www.devhut.net/vba-fso-files-folders-drives-and-more/
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile : Fully qualified path and filename to check for
'
' Usage:
' ~~~~~~
' ? FSO_File_Exist("C:\Temp\Flow-Undo-Dont.jpg")
' Returns -> True
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2022-11-24 Initial Release
'---------------------------------------------------------------------------------------
Function FSO_File_Exist(ByVal sFile As String) As Boolean
On Error GoTo Error_Handler
#Const FSO_EarlyBind = False 'True = Early Binding, False = Late Binding
#If FSO_EarlyBind = True Then
Dim oFSO As Scripting.FileSystemObject
Set oFSO = New FileSystemObject
#Else
Dim oFSO As Object
Set oFSO = CreateObject("Scripting.FileSystemObject")
#End If
FSO_File_Exist = oFSO.FileExists(sFile)
Error_Handler_Exit:
On Error Resume Next
Set oFSO = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: FSO_File_Exist" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has occurred!"
Resume Error_Handler_Exit
End Function
If you go the route of FSO, I’d recommend implementing Self-Healing Object Variables, so be sure to checkout:

Usage Example
If FSO_File_Exist("C:\Temp\Flow-Undo-Dont.jpg") = True Then
'Do something because the file exists
End If
Or
If FSO_File_Exist("C:\Temp\Flow-Undo-Dont.jpg") = False Then
MsgBox "The file could not be found!", vbCritical Or vbOKOnly, "Operation Aborted"
End If
Using The GetAttr Function
'---------------------------------------------------------------------------------------
' Procedure : GA_FileExist
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Test for the existence of a file; Returns True/False
' True => File exists
' False => File does not exist
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required
' References: https://learn.microsoft.com/en-us/office/vba/Language/Reference/user-interface-help/getattr-function
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile : Fully qualified path and filename to check for
'
' Usage:
' ~~~~~~
' GA_FileExist("C:\Temp\Flow-Undo-Dont.jpg")
' Returns -> True/False
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2024-02-02
'---------------------------------------------------------------------------------------
Function GA_FileExist(ByVal sFile As String) As Boolean
On Error GoTo Error_Handler
Dim iRetVal As Integer
iRetVal = GetAttr(sFile)
GA_FileExist = Not ((iRetVal And vbDirectory) = vbDirectory)
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
'We're here because nothing was found
Resume Error_Handler_Exit
End Function
Usage Example
If GA_FileExist("C:\Temp\Flow-Undo-Dont.jpg") = True Then
'Do something because the file exists
End If
Using the FindFirstFileA API
The following is an approach I came up with while wondering if there were any APIs that could do the job. It has limited testing, but does seem to work nicely. That said there are a number of other APIs that could be used in a similar fashion.
Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileA" (ByVal lpFileName As String, lpFindFileData As WIN32_FIND_DATA) As Long
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Const MAX_PATH As Long = 260
Type WIN32_FIND_DATA
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName As String * MAX_PATH
cAlternate As String * 14
End Type
Private Const INVALID_HANDLE_VALUE As Long = -1
'---------------------------------------------------------------------------------------
' Procedure : API_FileExist
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Test for the existence of a file; Returns True/False
' True => File exists
' False => File does not exist
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd: FindFirstFile API and Type/Const Declarations
' References: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilea
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile : Fully qualified path and filename to check for
'
' Usage:
' ~~~~~~
' API_FileExist("C:\Temp\Flow-Undo-Dont.jpg")
' Returns -> True/False
'
' API_FileExist("C:\Temp\Flow-Undo-Dont.*")
' Returns -> True/False 'careful using wildcards though!
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2024-02-02 Initial Release
'---------------------------------------------------------------------------------------
Function API_FileExist(ByVal sFile As String) As Boolean
On Error GoTo Error_Handler
Dim lHwnd As Long
Dim FileData As WIN32_FIND_DATA
lHwnd = FindFirstFile(sFile, FileData)
If lHwnd <> INVALID_HANDLE_VALUE Then API_FileExist = True
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: API_FileExist" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
Usage Example
If API_FileExist("C:\Temp\Flow-Undo-Dont.jpg") = True Then
'Do something because the file exists
End If
Using the PathFileExistsA API
I also discovered the PathFileExistsA API which can be used to test the existence of both files and/or folders.
Private Declare Function PathFileExists Lib "SHLWAPI.DLL" Alias "PathFileExistsA" (ByVal pszPath As String) As Long
'---------------------------------------------------------------------------------------
' Procedure : API_PathFileExist
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Test for the existence of a file or folder; Returns True/False
' True => File/Folder exists
' False => File/Folder does not exist
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd: FindFirstFile API and Type/Const Declarations
' References: https://learn.microsoft.com/en-us/office/vba/Language/Reference/user-interface-help/getattr-function
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile : Fully qualified path and filename (optional) to check for
'
' Usage:
' ~~~~~~
' API_PathFileExist("C:\Temp\Flow-Undo-Dont.jpg") 'Check for a File
' Returns -> True/False
'
' API_PathFileExist("C:\Temp\") 'Check for a Folder
' Returns -> True/False
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2024-02-02 Initial Release
'---------------------------------------------------------------------------------------
Function API_PathFileExist(ByVal sFile As String) As Boolean
On Error GoTo Error_Handler
API_PathFileExist = PathFileExists(sFile)
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: API_PathFileExist" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
Usage Example – File
If API_PathFileExist("C:\Temp\Flow-Undo-Dont.jpg") = True Then
'Do something because the file exists
End If
Usage Example – Folder
If API_PathFileExist("C:\Temp\") = True Then
'Do something because the folder exists
End If
In reality, I’ve wrapped the function in another function, which truthfully isn’t necessary. Feel free to simply use the API directly! Hind sight is always 20/20!
Using WizHook (Access Only)
This was brought to my attention by a comment on my YouTube channel by @epdmfn3747. I knew of WizHook, but haven’t really played around with it much. I know it has a lot of possibilities, but no documentation. The other drawback is this is Microsoft Access specific. Never the less, thanks to @epdmfn3747 comment it was easy to wrap it up in a reusable function like so:
'---------------------------------------------------------------------------------------
' Procedure : WizHook_FileExist
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Test for the existence of a file; Returns True/False
' True => File exists
' False => File does not exist
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile : Fully qualified path and filename to check for
'
' Usage:
' ~~~~~~
' ? WizHook_FileExist("C:\Temp\Flow-Undo-Dont.jpg")
' Returns -> True/False
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2024-02-02 Initial Release
'---------------------------------------------------------------------------------------
Function WizHook_FileExist(ByVal sFile As String) As Boolean
On Error GoTo Error_Handler
WizHook.Key = 51488399
WizHook_FileExist = WizHook.FileExists(sFile)
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: WizHook_FileExist" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
Usage Example
If WizHook_FileExist("C:\Temp\Flow-Undo-Dont.jpg") = True Then
'Do something because the file exists
End If
Using Windows Management Instrumentation (WMI)
One last approach I thought of was using WMI to query the system. The code would look something like the following.
'---------------------------------------------------------------------------------------
' Procedure : WMI_FileExist
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Test for the existence of a file; Returns True/False
' True => File exists
' False => File does not exist
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile : Fully qualified path and filename to check for
'
' Usage:
' ~~~~~~
' ? WMI_FileExist("C:\Temp\Flow-Undo-Dont.jpg")
' Returns -> True/False
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2024-02-03 Initial Release
'---------------------------------------------------------------------------------------
Public Function WMI_FileExist(ByVal sFile As String) As Boolean
On Error GoTo Error_Handler
#Const WMI_EarlyBind = False 'True => Early Binding / False => Late Binding
#If WMI_EarlyBind = True Then
Dim oWMI As WbemScripting.SWbemServices
Dim oCols As WbemScripting.SWbemObjectSet
#Else
Dim oWMI As Object
Dim oCols As Object
#End If
Dim sWMIQuery As String 'WMI Query
sFile = Replace(sFile, "\", "\\") 'Escape \ character!
Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
sWMIQuery = "SELECT * FROM CIM_Datafile WHERE Name = '" & sFile & "'"
Set oCols = oWMI.ExecQuery(sWMIQuery)
WMI_FileExist = (oCols.Count <> 0)
Error_Handler_Exit:
On Error Resume Next
Set oCols = Nothing
Set oWMI = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: WMI_FileExist" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
Usage Example
If WMI_FileExist("C:\Temp\Flow-Undo-Dont.jpg") = True Then
'Do something because the file exists
End If
Speed Comparisons
Stubborn, stubborn me! I had to know how the various approaches compared to each other (speedwise), so I quickly ran a few test and these were the results. Now this was on limited testing and all local. So I’d love to also run this test over a corporate network to see if there are any surprises.
| Dir | FindFirstFile | FSO | FSO (SHOV) | GetAttr | PathFileExist | WizHook | WMI | |
|---|---|---|---|---|---|---|---|---|
| Average Execution Time | 0.00007125 | 0.00005263 | 0.00059847 | 0.00009273 | 0.00003643 | 0.00004033 | 0.00004915 | 0.01819589 |
| % Diff vs GetAttr | 64.67% | 36.38% | 177.05% | 87.18% | 0.00% | 10.16% | 29.73% | 199.20% |
I’ve rerun the test multiple times (it’s automated, so easy to do) and in some cases GetAttr comes in faster, and other times it’s PathFileExist.
WizHook seems to bounce around and I don’t quite understand why. Sometimes it is fast and other times it is slower for the exact same files???
The only finding that doesn’t seem to change is FSO and WMI are consistently the slowest techniques, by far. Not too much of a surprise at the end of the day. That said, do notice the improvement the SHOV technique brings though.
So my recommendation, based on my testing, would be to use either the GetAttr, PathFileExist or FindFirstFile approach. That said, GetAttr is baked into VBA and doesn’t require any declarations…
One exception perhaps, if you are already using FSO with SHOV in your db, so you’ve already taken the initial CreateObject performance hit, then, and only then does FSO SHOV still hold value as an option.
So now you know and you can make your own educate choice!
Resources on the Subject



Hi Dan –
I’m getting inconsistent response from FileExists particularly when using via VPN, works most of the time in the office, but VPN rarely correctly reports the file even though I’m looking right at it in an Explorer window.
Have tried via Len(Dir(sFile) > 0 and FileSystemObject.FileExists methods plus your code above.
Even when manually debug stepping is inconsistent. Any ideas please?
B-T-W I appreciate the style and craft of your code – nicely done + thank you
quick followup
I was testing for the file before opening with Notepad, so bypassing the test I just tried to open directly with Notepad which reported this (info) message…
The system cannot contact a domain controller to service the authentication request. Please try again later.
I can open an Explorer Window to the folder, locate and open the file via Notepad – suspect Explorer operates through the DC.
Not sure quite what to do – the file is an Access application log file. Obviously I could change it to a database based logger, originally concerned about database availability thinking text logging would be generically safer. Reconsidering that decision.
Thank you,
Tom
Based on the reported error, sounds like networking issues, possibly a speed issue due to VPN.
When I’ve seen VPN being used, it has always been over a WAN connection which is a NO-NO when it comes to anything Access related. If that is your case as well, then you might want to rethink Access as a whole, or look at alternate remote access approaches: CITRIX, Terminal Services, RDP, …
I don’t know if this applies to your situation, but I was just reading a thread and the source (a very reliable source) was explaining that Win 10 V.1903 is causing all sorts of networking issues, including VPN. So if you are running 1903, perhaps all of this is due to another flawed update out of Redmond. You could rollback to a prior version and see if MS ever fixes the issue. I know it’s a bit of a shot in the dark, but thought I’d at least pass along the info in case it was pertinent.
He then explained, and I quote