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);
}