web-dev-qa-db-fra.com

Comment lister des fichiers dans un répertoire en utilisant l'API Windows?

J'ai ce code et il affiche le dossier avec le répertoire lui-même et non son contenu. Je veux afficher son contenu. Je ne veux pas utiliser boost :: système de fichiers. 

Comment puis-je résoudre ça?

Code:

#include <windows.h>
#include <iostream>

int main()
{
    WIN32_FIND_DATA data;
    HANDLE hFind = FindFirstFile("C:\\semester2", &data);      // DIRECTORY

    if ( hFind != INVALID_HANDLE_VALUE ) {
        do {
            std::cout << data.cFileName << std::endl;
        } while (FindNextFile(hFind, &data));
        FindClose(hFind);
    }
}

Sortie:

semester2
8
John Escobia
HANDLE hFind = FindFirstFile("C:\\semester2", &data);       // DIRECTORY

Vous avez le répertoire parce que c'est ce que vous avez demandé. Si vous voulez les fichiers, demandez-les:

HANDLE hFind = FindFirstFile("C:\\semester2\\*", &data);  // FILES

(Vous pouvez plutôt utiliser *.* si vous préférez, mais apparemment, cela ne fonctionne que pour des raisons de compatibilité ascendante et devrait donc probablement être évité. Voir commentaires et réponse de RbMm.)

12
Harry Johnston

Permettez-moi de prendre des notes sur "*.*" vs "*". Ces déclarants ne sont pas égaux.

Deux fichiers différents peuvent exister dans notre dossier: somefile et somefile..

Si nous utilisions l'api de faible niveau ZwQueryDirectoryFile avec "*.*" comme expression de recherche (il s'agit du dixième paramètre - FileName [in, optional]) - nous obtiendrions uniquement somefile.. Mais si nous utilisions "*", nous obtiendrions les deux fichiers - somefile et somefile. 

Si nous essayons FindFirstFile("C:\\semester2\\*.*", &data);, nous pouvons noter que les deux fichiers somefile et somefile. sont renvoyés. Donc, ici "*.*" vs "*" ont le même effet - aucune différence d’utilisation.

Pourquoi cela arrive-t-il? Parce que dans FindFirstFileEx dans kernelbase (kernel32), faites une vérification spéciale du masque "*.*" et si c'est le cas, remplacez-le par "" (nom vide ayant le même effet que "*"). 

Je pense que cela est fait pour corriger une erreur très courante lorsque les utilisateurs passent "*.*" au lieu du "*" correct et pour assurer la compatibilité avec le code hérité.

. et .. ne font pas réellement partie du répertoire car il est stocké sur disque, mais sont ajoutés par l'API Win32.

Ce n'est pas vrai. 

  • pour le système de fichiers de style FAT-, cela est réellement stocké dans le répertoire FAT en tant que 2 première entrée. 
  • dans NTFS il n'y a pas de telles entrées, mais NTFS.sys ajoute artificiellement ces 2 entrées si elles sont masquées. 

Cela se fait donc non pas au niveau de l'API Win32, mais au niveau du noyau - du pilote.

En conclusion, "*.*" fonctionnera correctement avec l’API Win32 au minimum, mais la méthode correcte et propre consiste à utiliser "*" ici.
"*.*" sera une erreur avec ZwQueryDirectoryFile api.

6
RbMm

La réponse de Harry produira en fait des fichiers et des dossiers ayant une extension dans votre dossier souhaité "C:\\semester2".

Ainsi, par exemple, si vous avez un dossier nommé "C:\\semester2\\math.course", l'exemple ci-dessus le trouvera également. De plus, si vous avez un fichier nommé "C:\\semester2\\math_scores" (notez qu'il n'a pas d'extension), il ne sera pas trouvé.

Compte tenu de ce qui précède, je suggérerais la solution suivante:

HANDLE hFind = FindFirstFile("C:\\semester2\\*", &data); 

Cela listera la liste complète des éléments dans le répertoire. Le filtrage des répertoires peut être effectué de la manière suivante:

if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// directory
}
else
{
// file
}

Les éléments suivants peuvent être utilisés pour les références: Constantes FileAttributes , FIND_DATA struct , API FindFirstFile

0
papadp