GetFullPathName


When you are given a filename or a relative path, you’ll often need to convert it to a full pathname. You could write the code to find the pathname in Basic, but the operating system can provide the information more reliably. That way, your code will work even if the rules defining paths and filenames change (as they did for Windows NT and Windows 95). Win32 provides the GetFullPath­Name function.


Use GetFullPathName as shown here:

Dim sBase As String, pBase As Long
sFullName = String$(cMaxPath, 0)
c = GetFullPathName(sName, cMaxPath, sFullName, pBase)
sFullName = Left$(sFullName, c)
If c Then s = s & “Full name: “ & sFullName & sCrLf

Notice the last argument passed to GetFullPathName. The function returns a pointer to the filename portion of the full pathname. That’s handy in C, but it’s not much use in Basic. The Basic Way is to return the 1-based position of the filename string within the full pathname string. Then you can use Mid$ to cut out the filename.


The only way to convert that filename pointer to a position index is to write a wrapper function in C that calculates the index by comparing the pointer to the start of the full pathname to the filename pointer. You can’t do the pointer arithmetic in Basic because the pBase pointer returned by GetFullPathName is a pointer to the temporary ANSI string used during Unicode conversion. That temporary string is gone, so a pointer to it is meaningless.


But let’s put that aside for the moment. Instead, let’s take another look at GetFull­PathName. Do you notice anything missing? It returns only the pathname but no other useful information, such as the extension or the directory. Why not? Many languages provide a function to split a full pathname into its parts. For example, Microsoft Visual C++ provides _splitpath. A language that lacks this invaluable tool is unthinkable. One way or another we must add it to Visual Basic, and the Win32 GetFullPathName function provides part of the solution. But to make it fully functional, you need to parse the rest of the full path­name to provide the directory and extension parts. If you’re going that far, it’s easy enough to ignore the filename pointer provided by GetFullPathName and just parse for the basename position in Basic.


That’s exactly what my GetFullPath function does. It returns indexes to the pathname parts through optional reference parameters, and it returns the full pathname in the return value. You can use the whole works like this:

s = s & sCrLf & “Test GetFullPath with all arguments” & sCrLf & sCrLf
sFullName = GetFullPath(sName, iBase, iExt, iDir)
If sFullName <> sEmpty Then
s = s & “Relative file: “ & sName & sCrLf
s = s & “Full name: “ & sFullName & sCrLf
s = s & “File: “ & Mid$(sFullName, iBase) & sCrLf
s = s & “Extension: “ & Mid$(sFullName, iExt) & sCrLf
s = s & “Base name: “ & Mid$(sFullName, iBase, _
iExt - iBase) & sCrLf
s = s & “Drive: “ & Left$(sFullName, iDir - 1) & sCrLf
s = s & “Directory: “ & Mid$(sFullName, iDir, _
iBase - iDir) & sCrLf
s = s & “Path: “ & Left$(sFullName, iBase - 1) & sCrLf
Else
s = s & “Invalid name: “ & sName
End If

It would be annoying to have to provide all those index arguments in cases where you didn’t need them, but fortunately they’re optional. The following list shows just about all the possible permutations, although some of these make more sense than others.

sFullName = GetFullPath(sName, iBase, iExt, iDir)
sFullName = GetFullPath(sName, iBase, iExt)
sFullName = GetFullPath(sName, iBase)
sFullName = GetFullPath(sName)
sFullName = GetFullPath(sName, , iExt)
sFullName = GetFullPath(sName, , iExt, iDir)
sFullName = GetFullPath(sName, , , iDir)
sFullName = GetFullPath(sName, iBase, , iDir)

Although these cover the bases, they’re not particularly convenient, so UTIL­ITY.BAS (and UTILITY.CLS through delegation) also provides wrappers for some of the most common operations:

sName = “Hardcore.frm”
sPart = GetFullPath(sName) ‘ C:\Hardcore\Hardcore.frm
sPart = GetFileBase(sName) ‘ Hardcore
sPart = GetFileBaseExt(sName) ‘ Hardcore.frm
sPart = GetFileExt(sName) ‘ .frm
sPart = GetFileDir(sName) ‘ C:\Hardcore\

Now that we’ve seen how GetFullPath works, let’s take a look at how it’s implemented in Basic, as shown on the following page.

Function GetFullPath(sFileName As String, _
Optional FilePart As Long, _
Optional ExtPart As Long, _
Optional DirPart As Long) As String

Dim c As Long, p As Long, sRet As String
If sFileName = sEmpty Then ApiRaise ERROR_INVALID_PARAMETER

‘ Get the path size, then create string of that size
sRet = String(cMaxPath, 0)
c = GetFullPathName(sFileName, cMaxPath, sRet, p)
If c = 0 Then ApiRaise Err.LastDllError
BugAssert c <= cMaxPath
sRet = Left$(sRet, c)

‘ Get the directory, file, and extension positions
GetDirExt sRet, FilePart, DirPart, ExtPart
GetFullPath = sRet

End Function

Most of the function is straightforward API calling as discussed in Chapter 2. The error-handling functions ApiRaise and ApiRaiseIf were discussed in “What’s Wrong with That?” earlier in this chapter. Actually, most of the code is hidden in the call to GetDirExt, which contains big, ugly chunks of inconsistent parsing code for finding the different file parts. I’ll spare you the details of how I parse from the front to find the directory position and from the back to find the extension and base positions. The code (like that for all the procedures in this section) is in UTILITY.BAS.