(Back)Slash and Burn
by Miles Forrest - Independent Developer
I think we've all been bitten by App.Path's inconsistencies with backslash over the years.
OK, I concede that it's not actually inconsistent, but it isn't consistent either.
OK, I concede that it's actually consistent to include the trailing backslash for root directories and not for others, because the backslash is the start of the directory name, not the end. So that means it's DOS' fault.
OK, I concede it's not actually DOS' fault per se - I mean, what else could DOS do?
Anyway, if you'd stop arguing with me, the point is that that pesky backslash causes me pain. It annoys me. So I fixed the problem.
This little module is invaluable, even though it only contains five small functions.
Type FILETIME ' 8 Bytes
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Type WIN32_FIND_DATA ' 318 Bytes
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved¯ As Long
dwReserved1 As Long
cFileName As String * 255
cAlternate As String * 14
End Type
Private Const INVALID_HANDLE_VALUE& = -1
Private Declare Function GetLogicalDriveStrings& Lib "kernel32" Alias "GetLogicalDriveStringsA"_
(ByVal nBufferLength As Long, ByVal lpBuffer As String)
Private Declare Function FindFirstFile& Lib "kernel32" Alias "FindFirstFileA"_
(ByVal lpFileName As String, lpFindFileData As WIN32_FIND_DATA)
Private Declare Function FindClose& Lib "kernel32"_
(ByVal hFindFile As Long)
The FileExists Function finds a file or directory, and returns True if it exists. Just throw it a file or directory name (with or without trailing backslash!) and Bob's your uncle.
Public Function FileExists(ByVal zvDirectoryFileName As String) As Boolean
Dim typpFindFileData As WIN32_FIND_DATA
Dim lpHandle As Long
Dim lpSuccess As Long
Dim lpBufferLength As Long
Dim zpLogicalDrives As String * 106 '26 drive letters, 4 characters for each
'and 2 nulls at the end
'Are we dealing with the root directory of a logical drive?
'(Put the 2 spaces in just in case the input string is less
'that 2 characters which will case right$ to blow up)
If Right$(" " & zvDirectoryFileName, 2) = ":\" Then
'Get a list of the logical drives in the format "A:\#c:\#d:\##" where # is NULL
lpSuccess = GetLogicalDriveStrings(106, zpLogicalDrives)
FileExists = Not (InStr(1, zpLogicalDrives, zvDirectoryFileName, vbTextCompare) = 0)
Else
'Find the file
lpHandle = FindFirstFile(zvDirectoryFileName, typpFindFileData)
'If the handle is invalid then the path\file is not found (or errored)
FileExists = Not (INVALID_HANDLE_VALUE = lpHandle)
'Zap the structure
lpSuccess = FindClose(lpHandle)
End If
End Function
The PutBackslashPostfix Function is my all purpose "get that trailing backslash there" workhorse. If there's no trailing backslash, there is when this is finished with it!
Public Function PutBackslashPostfix(ByVal svPath As String) As String PutBackslashPostfix = IIf(Len(svPath) = 0, "\", svPath &_ IIf(Right$(svPath, 1) = "\", "", "\")) End FunctionOf course, it's not only trailing backslashes that need to be attacked. Sometimes you need to insert one at the fornt. PutBackslashPrefix handles this.
Public Function PutBackslashPrefix(ByVal svPath As String) As String
PutBackslashPrefix = IIf(Len(svPath) = 0, "\", _
IIf(Left$(svPath, 1) = "\", "", "\") & svPath)
End Function
Not surprisingly, I also went to town with a similar pair of removal functions.
Public Function RemoveBackslashPostfix(ByVal svPath As String) As String
RemoveBackslashPostfix = IIf(Len(svPath) = 0, "", _
IIf(Right$(svPath, 1) = "\", Left$(svPath, Len(svPath) - 1), svPath))
End Function
Public Function RemoveBackslashPrefix(ByVal svPath As String) As String
RemoveBackslashPrefix = IIf(Len(svPath) = 0, "", _
IIf(Left$(svPath, 1) = "\", Right$(svPath, Len(svPath) - 1), svPath))
End Function
So now, I don't care whose fault it is, or whether it's inconsistent or not.
The important thing is, it's fixed.