Converting a path with short names to a full LFN path
Published on April 20, 2005 By Steve Pitts In Software Development
I recently had the need to convert a Windows path, to be precise the name of an install directory extracted from the registry keys for a piece of game software, from its short name (aka 8.3) format to the long filename form that you'd see in Windows Explorer. I expected to just call a Windows API routine and be done with it, but strangely I couldn't find one just various bits of advice on how to achieve it. Since I ended up writing a routine to achieve the conversion, I thought I'd post it here, mainly for my own reference, but also in the hope that it would be useful to somebody. The code was written for C++ Builder v3, so uses the AnsiString (aka String) type built-in to that product, but it shouldn't be difficult to convert to either an STL string implementation or a simple array of char.

Cheers, Steve (Home Page)



String __fastcall TMainForm::ShortPathToLongPath(String ShortPath)
{
        String LongPath, RestPath, PartPath;
        WIN32_FIND_DATA FindRet;
        HANDLE DirHandle;
        int i_pos;

// strip any blanks and remove a trailing slash if there is one
        RestPath = ShortPath.Trim();
        if (RestPath.SubString(RestPath.Length(), 1) == "\\")
                RestPath.Delete(RestPath.Length(), 1);

// just a drive letter?? Return as is
        if (RestPath.Length() == 2 && RestPath.SubString(2, 1) == ":")
                return RestPath;

// we'd like FindFirst to only return directories
        FindRet.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;

// There is no (apparent) way to convert a short path to a long one, so we
// are forced to resort to converting each element
        if (RestPath.SubString(1, 1) == "\\")
        {
// if the name starts with a slash then we have either a UNC name, which we
// won't try and deal with here, or a folder in the root of the current drive
                if (RestPath.SubString(2, 1) == "\\")
// UNC name, all bets are off
                        return "";
// add the single slash to our output path so that we start at the root
                LongPath = "\\";
        }
        else if (RestPath.SubString(2, 1) == ":")
        {
// we assume that a second character of a colon indicates that this path
// starts with a drive designation
                if (RestPath.SubString(3, 1) == "\\")
                {
// if this is the root of the designated drive then add those three characters
// to the output path and remove them from what remains to be processed
                        LongPath = RestPath.SubString(1, 3);
                        RestPath.Delete(1, 3);
                }
                else
                {
// a drive designation with no following backslash means we'll just copy the
// two character drive details to the output and remove them from what's left
                        LongPath = RestPath.SubString(1, 2);
                        RestPath.Delete(1, 2);
                }
        }
        else
// path is relative to the current directory on the current drive, so our
// output path starts off empty
                LongPath = "";

// loop over each node (ie. folder/directory) in the remainder of the input
// path, converting it to a long filename form
        do
        {
// find the next folder/directory separator in what remains of the path
                i_pos = RestPath.Pos("\\");
                if (i_pos == 1)
                {
// Strip off multiple consecutive backslashes at the start of what's left
                        do
                        {
                                RestPath.Delete(1, 1);
                                i_pos = RestPath.Pos("\\");
                        } while (i_pos == 1);
                }
                if (i_pos == 0)
                {
// just the one folder name left in the path, so add it to the version to be
// passed to FindFirst and clear the remainder to indicate that we're done
                        PartPath = LongPath + RestPath;
                        RestPath = "";
                }
                else
                {
// add the next folder name to what we've already converted and remove it
// (including the next backslash) from what remains to be processed
                        PartPath = LongPath + RestPath.SubString(1, i_pos - 1);
                        RestPath.Delete(1, i_pos);
                }

// our life would be so much simpler if there was some way to convert the
// whole path, but FindFirst only converts the final node of the name,
// removing the rest from the output
                DirHandle = FindFirstFile(PartPath.c_str(), &FindRet);
                if (DirHandle == INVALID_HANDLE_VALUE)
                {
// if we get a problem then simply return a null string to indicate failure
                        return "";
                }
// the filename returned by FindFirst is just the last node of the name,
// converted to an LFN
                LongPath += FindRet.cFileName;
                FindClose(DirHandle);

// unless we've reached the end of the input path we add another backslash to
// the output path, ready for the next node to be appended to it
                if (RestPath != "")
                        LongPath += "\\";
        } while (RestPath != "");

// Phew!! That was a bit long-winded, but we now have a fully LFN path
        return (LongPath);
}


Comments
on Sep 20, 2006
Brought to you by spam resurrection.