Create Multiple Level Subdirectories

While recently working on a project, I needed to create a series of subdirectories from a user supplied string. These could be two, three, four or more deep. Of course the MSVC Help files lived up to their normal level of support. After beating my head aginst the wall I finally realized I could only create a single subdirectory at one time. In other words if none of the sub-directories previously existed then the code CreateDirectory("C:\Data\Test\Monday\Backup\Archive") would continually fail.

I finally realized that the function CreateDirectory is only a wrapper for _mkdir which will only create a single sub-directory so I would need to recursively call CreateDirectory() several times. Since there is really no way around this I created a small subroutine WriteDir() which takes care of all of the work for me.

What are the steps required

  • Set up a CStringArray to hold the Directories in order on creation.
  • Parse the passed CString Object into the CString variable tem one character at a time.
  • When we get to a \ pass it up and add "tem" to the CStringArray.
  • We need to add the \ at the end of the tem variable in order to continue to separate the directories.
  • Continue to do this until the entire string is parsed.

    **Note** Notice we have to add the final value of tem to the CString Array since there is NO \ at the end of the Directory String

    Now we can start creating the directories
  • Skip the first entry since it is the Drive letter
  • Cycle through the CStringArray testing to see if the directory already exists. If it does then go to the next item.
  • If the directory does not exist then use CreateDirectory().
  • When we reach the end of the CStringArray, check to see if the full directory actually was created. If it was then return TRUE, otherwise return FALSE;
  • Don't forget to clear the array (Good Housekeeping)

    From the Example: Original CString = C:\Data\Test\Monday\Backup\Archive
    
    CStringArray members
    	[0] = C:
    	[1] = C:\Data
    	[2] = C:\Data\Test
    	[3] = C:\Data\Test\Monday
    	[4] = C:\Data\Test\Monday\Backup
    	[5] = C:\Data\Test\Monday\Backup\Archive
    
    Remeber we skipped the first entry.  
    Then we cycled through and created each string independently.
    
    

    How do we put this to use

    Add the files WriteDir.h and WriteDir.cpp to your project. Add #include "WriteDir.h" in the cpp file where you are going to call the function. Set up a test variable: BOOL test; CString someString; Set someString to a Directory path you need to create (Do not add a \ at the end of the string) test = WriteDir(someString);

    
    if test == TRUE   SUCCESS
    if test == FALSE  FAILURE
    

    The Source Code

    #include "stdafx.h"
    #include "WriteDir.h"
    

    Setup Internal Variables

    BOOL WriteDirectory(CString dd)
    {
    	HANDLE		fFile;		// File Handle
    	WIN32_FIND_DATA	fileinfo;	// File Information Structure
    	CStringArray	m_arr;		// CString Array to hold Directory Structures
    	BOOL tt;			// BOOL used to test if Create Directory was successful
    	int x1 = 0;			// Counter
    	CString tem = "";		// Temporary CString Object
    
     Before we go to a lot of work. Check to see if the Directory exists
    
    	fFile = FindFirstFile(dd,&fileinfo);
    
    	// if the file exists and it is a directory
    	if(fileinfo.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
    	{
    		//  Directory Exists close file and return
    		FindClose(fFile);
    		return TRUE;
    	}
    	m_arr.RemoveAll();				// Not really necessary - Just habit
    Separate the String into its compounded components
    	for(x1 = 0; x1 < dd.GetLength(); x1++ )		// Parse the supplied CString Directory String
    	{									
    		if(dd.GetAt(x1) != '\\')		// if the Charachter is not a \ 
    			tem += dd.GetAt(x1);		// add the character to the Temp String
    		else
    		{
    			m_arr.Add(tem);			// if the Character is a \ 
    								Add the Temp String to the CString Array
    			tem += "\\";			// Now add the \ to the temp string
    		}
    		if(x1 == dd.GetLength()-1)		// If we reached the end of the String
    								add the remaining string
    			m_arr.Add(tem);
    	}
    
    
    	// Close the file
    	FindClose(fFile);
    
    Create each level of the Directory Structure
    
    	// Now lets cycle through the String Array and create each directory in turn
    	for(x1 = 1; x1 < m_arr.GetSize(); x1++)
    	{
    		tem = m_arr.GetAt(x1);
    		tt = CreateDirectory(tem,NULL);
    
    		// If the Directory exists it will return a false
    		if(tt)
    			SetFileAttributes(tem,FILE_ATTRIBUTE_NORMAL);
    		// If we were successful we set the attributes to normal
    	}
    	//  Now lets see if the directory was successfully created
    	fFile = FindFirstFile(dd,&fileinfo);
    
    Check to see if the Directory was created and it actually is a Directory
    	m_arr.RemoveAll();
    	if(fileinfo.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
    	{
    		//  Directory Exists close file and return
    		FindClose(fFile);
    		return TRUE;
    	}
    	else
    	{
    		// For Some reason the Function Failed  Return FALSE
    		FindClose(fFile);
    		return FALSE;
    	}
    }
    
    

    Download source - 2 KB



  • Comments

    • WriteDirectory() fails sporadically, fix found.

      Posted by Chr on 08/14/2012 08:10am

      WriteDirectory() sporadically failed. My computer is running windows 7/32. I could fix the problem by checking the result of FindFirstFile(). This does not make sense, because FindClose() should also work with invalid handles, however this fix works: fFile = FindFirstFile(cs_directory_to_create,&fileinfo;); if (INVALID_HANDLE_VALUE != fFile) { ...

      Reply
    • Bug Fix

      Posted by rossini on 07/06/2004 03:02pm

      You should change the line
      if(fileinfo.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
       to if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
      
      
      The problem is that if the directory has more than just the
      directory attribute set the check will fail.  I found this
      out by turning off the "Allow Indexing Service to index
      this disk for fast file searching" under properties for the
      drive in Windows XP.  This added the attribute 
      FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
      thus causing the checks for the directory to fail.
      
      John.

      Reply
    • Create folder by current date

      Posted by Legacy on 01/22/2004 12:00am

      Originally posted by: san dinfer

      can any one help me how to create a folder

      Reply
    • please help me in creating folders

      Posted by Legacy on 01/19/2004 12:00am

      Originally posted by: afsheen nasir


      I want to create folder with vc++ code anybody can help me???

      • A Lame Attempt !..

        Posted by prasadptc on 10/13/2004 08:54am

        #include
        #include
        #include
        
        int main()
        {
        char pStrName[64];
        char pStrDirectory[100];
        cout<<"Enter The Name Of Folder To Be Create on C Drive:"<

        Reply
      Reply
    • SHCreateDirectory API

      Posted by Legacy on 12/17/2003 12:00am

      Originally posted by: Josh Hogle

      If your code is for Windows 2000 or later, check out the SHCreateDirectory API call.

      Reply
    • Permissions under "Inetpub/wwwroot/" NEED HELP

      Posted by Legacy on 08/16/2002 12:00am

      Originally posted by: BOBA KOPOBbEB

      2000 ONLY.
      
      Looks fine at first glance, but acctually failed somewhere on the road. Take a look at Ownership/Auditing

      SECURITY_ATTRIBUTES sa;
      PSECURITY_DESCRIPTOR pSD = NULL;
      //SECURITY_DESCRIPTOR_CONTROL sdCtrl;

      // security inits
      memset ( ( VOID *) &sa, 0, sizeof ( SECURITY_ATTRIBUTES) );

      // alloc & init SD
      if ( ! ( pSD = ( PSECURITY_DESCRIPTOR)
      ( malloc ( SECURITY_DESCRIPTOR_MIN_LENGTH)) ) )
      return;

      if ( ! InitializeSecurityDescriptor ( pSD,
      SECURITY_DESCRIPTOR_REVISION) )
      return;

      // specify inheritanse
      UINT res = SetSecurityDescriptorControl(pSD, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
      printf("- ret = %d\n", res);


      // set NULL DACL on the SD
      if ( ! SetSecurityDescriptorDacl ( pSD, TRUE, ( PACL) NULL, FALSE) )
      return;

      // now set up the security attributes
      sa.nLength = sizeof ( SECURITY_ATTRIBUTES);
      sa.bInheritHandle = TRUE;
      sa.lpSecurityDescriptor = pSD;


      BOOL ret = CreateDirectory((LPCTSTR)path, &sa);

      Reply
    • Simple version without any classes, recursion and uneccessary memory allocations

      Posted by Legacy on 07/10/2002 12:00am

      Originally posted by: Eugen

      BOOL CreateDirectoryTree(LPCTSTR pszDirectory)
      {
      char szDir[MAX_PATH];
      char *p, *pNext;

      strcpy (szDir, pszDirectory);
      pNext = strchr(szDir, '\\');
      if (pNext)
      {
      pNext++;
      while ((p=strchr(pNext, '\\')) != 0)
      {
      *p = 0;
      if (GetFileAttributes (szDir) == -1)
      {
      if (CreateDirectory(szDir, 0) == FALSE)
      {
      return FALSE;
      }
      }
      *p = '\\';
      pNext = p+1;
      }
      }
      if (GetFileAttributes (szDir) == -1)
      {
      if (CreateDirectory(szDir, 0) == FALSE)
      {
      return FALSE;
      }
      }
      return TRUE;
      }

      • Potential buffer overflow!

        Posted by inbugable on 09/28/2004 06:59am

        strcpy (szDir, pszDirectory); is bad
        
        use:
        
        char szDir[MAX_PATH+1]; 
        strncpy (szDir, pszDirectory,MAX_PATH);
        szDir[MAX_PATH] = 0;
        
        www.antitux.de!

        Reply
      Reply
    • Try SHPathPrepareForWrite()

      Posted by Legacy on 06/12/2002 12:00am

      Originally posted by: Patrick Walsh

      You will get a kick out of this one.
      It will even prompt the user to format the drive if not formated, mount the network drive etc...

      Reply
    • Problems using recursive logic

      Posted by Legacy on 11/15/2001 12:00am

      Originally posted by: Ashes

      Although recursive logic might be simpler to code, recursive functions use up a lot more stack space. So, the non-recursive original code might be more useful, especially if I am creating very deeply nested paths (4K or greater).

      However, I agree using the ImageHlp API is probably the best.

      Reply
    • WOW !!

      Posted by Legacy on 06/08/2001 12:00am

      Originally posted by: Vikas

      See 'MakeSureDirectoryPathExists()'

      This's really cool !!
      Excellent method to create multiple level of SubDirectories !!

      Reply
    • Loading, Please Wait ...

    Leave a Comment
    • Your email address will not be published. All fields are required.

    Top White Papers and Webcasts

    • The impact of a data loss event can be significant. Real-time data is essential to remaining competitive. Many companies can no longer afford to rely on a truck arriving each day to take backup tapes offsite. For most companies, a cloud backup and recovery solution will eliminate, or significantly reduce, IT resources related to the mundane task of backup and allow your resources to be redeployed to more strategic projects. The cloud - can now be comfortable for you – with 100% recovery from anywhere all …

    • You probably have several goals for your patient portal of choice. Is "community" one of them? With a bevy of vendors offering portal solutions, it can be challenging for a hospital to know where to start. Fortunately, YourCareCommunity helps ease the decision-making process. Read this white paper to learn more. "3 Ways Clinicians can Leverage a Patient Portal to Craft a Healthcare Community" is a published document owned by www.medhost.com

    Most Popular Programming Stories

    More for Developers

    Latest Developer Headlines

    RSS Feeds