Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Ensuring a Path Exists

4.00/5 (2 votes)
3 May 2010CPOL2 min read 14.4K  
Ensuring a path exists

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. However, CreateDirectory 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 is reasonably simple and quite short.

Listing one shows my EnsurePathExists routine. It takes a path string, and will determine if it 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.

C++
// 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 component
    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);
}
Listing 1: EnsurePathExists() function

The code processes a single component, or layer, 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.

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)