web-dev-qa-db-fra.com

Comment créer récursivement un dossier dans Win32?

J'essaie de créer une fonction qui prend le nom d'un répertoire (C:\foo\bar, ou ..\foo\bar\..\baz, ou \\someserver\foo\bar), et crée des répertoires si nécessaire afin que le chemin d'accès complet soit créé.

J'essaie moi-même une implémentation assez naïve et cela semble être un cauchemar pour le traitement des chaînes. Il y a / contre \, il y a le cas particulier des partages réseau qui commencent par \\ (vous ne pouvez pas non plus tenter de mkdir () les deux premiers niveaux du chemin qui sont le nom de la machine et le nom du partage), et il y a \.\ saisit un non-sens qui peut exister dans un chemin.

Existe-t-il un moyen simple de le faire en C++?

33
pauldoo

Si vous n'avez pas besoin de prendre en charge les versions de Windows antérieures à Windows 2000, vous pouvez utiliser la fonction SHCreateDirectoryEx pour cela. Considère ceci:

int createDirectoryRecursively( LPCTSTR path )
{
    return SHCreateDirectoryEx( NULL, path, NULL );
}

// ...
if ( createDirectoryRecursively( T("C:\\Foo\\Bar\\Baz") ) == ERROR_SUCCESS ) {
   // Bingo!
} 

Dans le cas où l'utilisation d'une telle API Shell32.dll devient un problème, vous pouvez toujours réimplémenter la fonction createDirectoryRecursively ci-dessus avec autre chose (éventuellement une boucle câblée à la main).

47
Frerich Raabe

Voici une version qui fonctionne sans bibliothèque externe, donc Win32 uniquement, et qui fonctionne dans toutes les versions de Windows (y compris Windows CE, où j'en avais besoin):

wchar_t *path = GetYourPathFromWherever();

wchar_t folder[MAX_PATH];
wchar_t *end;
ZeroMemory(folder, MAX_PATH * sizeof(wchar_t));

end = wcschr(path, L'\\');

while(end != NULL)
{
    wcsncpy(folder, path, end - path + 1);
    if(!CreateDirectory(folder, NULL))
    {
        DWORD err = GetLastError();

        if(err != ERROR_ALREADY_EXISTS)
        {
            // do whatever handling you'd like
        }
    }
    end = wcschr(++end, L'\\');
}
10
ctacke

Voici une fonction que j'ai écrite qui crée de manière itérative une arborescence de dossiers. Voici la fonction principale:

#include <io.h>
#include <string>
#include <direct.h>
#include <list>

// Returns false on success, true on error
bool createFolder(std::string folderName) {
    list<std::string> folderLevels;
    char* c_str = (char*)folderName.c_str();

    // Point to end of the string
    char* strPtr = &c_str[strlen(c_str) - 1];

    // Create a list of the folders which do not currently exist
    do {
        if (folderExists(c_str)) {
            break;
        }
        // Break off the last folder name, store in folderLevels list
        do {
            strPtr--;
        } while ((*strPtr != '\\') && (*strPtr != '/') && (strPtr >= c_str));
        folderLevels.Push_front(string(strPtr + 1));
        strPtr[1] = 0;
    } while (strPtr >= c_str);

    if (_chdir(c_str)) {
        return true;
    }

    // Create the folders iteratively
    for (list<std::string>::iterator it = folderLevels.begin(); it != folderLevels.end(); it++) {
        if (CreateDirectory(it->c_str(), NULL) == 0) {
            return true;
        }
        _chdir(it->c_str());
    }

    return false;
}

La routine folderExists est la suivante:

// Return true if the folder exists, false otherwise
bool folderExists(const char* folderName) {
    if (_access(folderName, 0) == -1) {
        //File not found
        return false;
    }

    DWORD attr = GetFileAttributes((LPCSTR)folderName);
    if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
        // File is not a directory
        return false;
    }

    return true;
}

Un exemple d'appel avec lequel j'ai testé les fonctions ci-dessus est le suivant (et cela fonctionne):

createFolder("C:\\a\\b\\c\\d\\e\\f\\g\\h\\i\\j\\k\\l\\m\\n\\o\\p\\q\\r\\s\\t\\u\\v\\w\\x\\y\\z");

Cette fonction n'a pas subi de tests très approfondis, et je ne suis pas sûr qu'elle fonctionne encore avec d'autres systèmes d'exploitation (mais est probablement compatible avec quelques modifications). J'utilise actuellement Visual Studio 2010 avec Windows 7.

7
Jonathan Feucht

SHCreateDirectory la fonction peut le faire. Mais le document indique qu'il peut devenir obsolète dans une version ultérieure de Windows.

Depuis MSDN

Remarque Cette fonction est disponible via Windows XP Service Pack 2 (SP2) et Microsoft Windows Server 2003. Elle peut être modifiée ou indisponible dans les versions ultérieures de Windows.

6
Aamir

version de travail pratique et simple de la mienne:

BOOL DirectoryExists(LPCTSTR szPath)
{
  DWORD dwAttrib = GetFileAttributes(szPath);

  return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
    (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

void createDirectoryRecursively(std::wstring path)
{
  unsigned int pos = 0;
  do
  {
    pos = path.find_first_of(L"\\/", pos + 1);
    CreateDirectory(path.substr(0, pos).c_str(), NULL);
  } while (pos != std::string::npos);
}

//in application
int main()
{
  std::wstring directory = L"../temp/dir";
  if (DirectoryExists(directory.c_str()) == FALSE)
    createDirectoryRecursively(directory);
  return 0;
}
3
sailfish009

Pour Windows XP et plus. Attend la chaîne terminée par widechar null et la quantité d'actions récursives en tant que paramètres. N'a pas encore été testé avec plus d'un niveau.

Remarque: les séparateurs de chemin doivent être '\'

bool CreateRecursiveDirectoryW(const wchar_t* filepath, const int max_level)
{
    bool result = false;
    wchar_t path_copy[MAX_PATH] = {0};
    wcscat_s(path_copy, MAX_PATH, filepath);
    std::vector<std::wstring> path_collection;

    for(int level=0; PathRemoveFileSpecW(path_copy) && level < max_level; level++)
    {
        path_collection.Push_back(path_copy);
    }
    for(int i=path_collection.size()-1; i >= 0; i--)
    {
        if(!PathIsDirectoryW(path_collection[i].c_str()))
            if(CreateDirectoryW(path_collection[i].c_str(), NULL))
                result = true;
    }
    return result;
};
2
xezon

Je modifie une ancienne application Windows CE, et c'est ce que je prévois d'utiliser. Devrait également fonctionner dans Windows CE. C'est aussi récursif:

static void createPath(const CString& p)
{
   // only create directories that don't exist
   if (::GetFileAttributes(p) == INVALID_FILE_ATTRIBUTES)
   {
      // check if our parent needs to be created, too...
      int i = p.ReverseFind('\\');
      if (i > 0)
      {
         // ...yes, create the parent (recursively)
         createPath(p.Left(i));
      }

      // finally, actually create the directory in p
      ::CreateDirectory(p, NULL);
   }
}
1
Tommi

ctacke Vous avez oublié le dernier segment. par exemple. '\ aa\bb\"cc"' Voici la modification pour ctacke:

//---------------------------------------------------------------------
int isfexist(char *fn)
{
    struct stat stbuf;
    extern int errno;

    if (stat(fn, &stbuf)) {
        if (errno == ENOENT) return(0);
        else {
            printf("isfexist: stat");
            return(0);
        }
    } else {
        if (stbuf.st_mode & S_IFDIR) return(2);
        else return(1);
    }
}
//---------------------------------------------------------------------
int MakeDirTree(char *path)
{
    char *end1, *end2;

    if (path[0] == '\\') end1 = path + 1;       // Case '\aa\bb'
    else if (path[1] == ':' && path[2] == '\\') end1 = path + 3;    // Case 'C:\\aa\\bb'
    else end1 = path;

    for(;;) {
        end2 = strchr(end1, '\\');
        if (end2 == NULL) {
            // Case '\aa\bb\'
            if (*end1 == 0) break;
            // Last segment '\aa\bb\"cc"' not yet proceed
        } else *end2 = 0;
        if (isfexist(path) <= 0) mkdir(path);
        if (end2 == NULL) break;    // Last segment finished
        else {
            *end2 = '\\';
            end1 = end2 + 1;
        }
    }
}
1
steel17
UnicodeString path = "C:\\Test\\Test\\Test\\";
TStringList *list = new TStringList();

try
{
    list->Delimiter = '\\';
    list->StrictDelimiter = true;
    list->DelimitedText = path;
    path = list->Strings[0]; \\drive letter
    for(int i = 1; i < list->Count - 1; i++)
    {
        try
        {
            path += "\\" + list->Strings[i];
            CreateDirectory(path.w_str(), NULL);
        }
        catch(...) { }
    }
}
catch(...) { }
delete list;
0
rebeldevil14