Login


Ensuring a Path Exists

By Jonathan Wood on 12/1/2010 (Updated on 12/4/2010)
Language: C++
Technology: MFC
Platform: Windows
License: CPOL
Views: 9,814
Desktop Development » Files & Directories » Directories » Ensuring a Path Exists

Introduction

I was recently working on a C++/MFC application that needed to backup a bunch of files. Copying files is easy enough, but I had to create the target folder if it didn't exist. Creating a folder is also easy: just call CreateDirectory(). However, this function will not create intermediate directories.

That is, if I need to create the directory D:\Documents\Financials, CreateDirectory() will fail if D:\Documents does not already exist. To build this directory, I must first create D:\Documents before I can create D:\Documents\Financials. Clearly, I needed a routine that will ensure a path of any depth exists, and will create any missing folders.

Note that SHCreateDirectory() is documented as doing just this. However, the documentation also states this function "might be altered or unavailable in subsequent versions of Windows." I didn't really care for the sound of that so I decided to write my own routine. Although it required a little thought, the routine I came up with with reasonably simple and quite short.

My EnsurePathExists() Function

Listing one shows my EnsurePathExists() routine. It takes a path string, and will determine whether or not that path exists. If it does not exist, the routine will create it. It returns a Boolean value, which is false if a portion of the directory could not be created. (For example, if there was already a file with the same name.)

Listing 1: EnsurePathExists() function

// Ensures the given path exists, creating it if needed
bool EnsurePathExists(LPCTSTR lpszPath)
{
  CString sPath;

  // Nothing to do if path already exists
  if (DirectoryExists(lpszPath))
    return true;

  // Ignore trailing backslash
  int nLen = _tcslen(lpszPath);
  if (lpszPath[nLen - 1] == '\\')
    nLen--;

  // Skip past drive specifier
  int nCurrLen = 0;
  if (nLen >= 3 && lpszPath[1] == ':' && lpszPath[2] == '\\')
    nCurrLen = 2;

  // We can't create root so skip past any root specifier
  while (lpszPath[nCurrLen] == '\\')
    nCurrLen++;

  // Test each component of this path, creating directories as needed
  while (nCurrLen < nLen)
  {
    // Parse next path compenent
    LPCTSTR psz = _tcschr(lpszPath + nCurrLen, '\\');
    if (psz != NULL)
      nCurrLen = (int)(psz - lpszPath);
    else
      nCurrLen = nLen;

    // Ensure this path exists
    sPath.SetString(lpszPath, nCurrLen);
    if (!DirectoryExists(sPath))
      if (!::CreateDirectory(sPath, NULL))
        return false;

    // Skip over current backslash
    if (lpszPath[nCurrLen] != '\0')
      nCurrLen++;
  }
  return true;
}

// Returns true if the specified path exists and is a directory
bool DirectoryExists(LPCTSTR lpszPath)
{
  DWORD dw = ::GetFileAttributes(lpszPath);
  return (dw != INVALID_FILE_ATTRIBUTES &&
    (dw & FILE_ATTRIBUTE_DIRECTORY) != 0);
}

The code processes a single component, or level, of the path at a time, either ensuring it exists or creating it if it does not. It uses my helper routine DirectoryExists() to determine if each component exists. If it does not, that component is created using CreateDirectory().

To save time, EnsurePathExists() starts by testing for the existence of the entire path. If it already exists, the function simply returns true.

Conclusion

That's about all there is to it. Most of the details are just in making sure each component is properly parsed and tested. The code skips the root folder because you cannot create a root folder. The code also strips multiple leading backslashes, as might be seen in paths that refer to network locations.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.