API Windows / Gestion de fichiers [Résolu]

Signaler
-
 Telnaz -
Bonjour, je créé un de mes premiers programmes sur Visual Studio pour un projet. Je dois coder celui-ci via le langage C et l'API Windows uniquement. J'ai déjà bien avancé, avec différentes recherches / aides.
A présent, je bloque sur quelque chose ...

J'essaie de coder une recherche de codes dans plusieurs fichiers, ce code est compris entre deux balises que j'appelle <A> et </A>.

Pour l'instant je bloque sur le fait de rechercher et lister tous les fichiers, qui sont contenus dans plusieurs sous-dossiers, différentes arborescences...

Est-ce que vous connaissez des fonctions ou autre méthode permettant cela ? Via l'API Windows ...

Merci

12 réponses

Messages postés
5550
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
12 mai 2021
933
Salut Telnaz,

L'API Windows comporte la fonction FindFirstFile() qui permet de retourner le premier fichier matchant l'argument de recherche et FindNextFile() qui permet de retourner le reste.

https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilea
https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextfilea
https://docs.microsoft.com/en-us/windows/win32/fileio/listing-the-files-in-a-directory

Dal
Salut, merci pour ta réponse.

En effet, j'ai déjà utilisé ces fonctions ... D'ailleurs, j'arrive bien à lister les fichiers et sous-dossier du dossier courant. Par contre, je ne sais pas comment effectuer cette analyse de façon récursive, c'est-à-dire également lister tous les fichiers, compris dans les sous-dossiers ...

bufferCheminSousDossier : chemin absolu du dossier courant (ou j'effectue la recherche de fichiers)

Voici mon code :

 


//hedit sont les formulaires remplis si les boutons correspondants sont actionnés

if (hEditJ !=0 && hEditI !=0)

{


strcat(bufferCheminSousDossier,"\\*");
hFind2 = FindFirstFile(bufferCheminSousDossier, &ffd);


if (INVALID_HANDLE_VALUE == hFind2)

{

DisplayErrorBox(TEXT("FindFirstFile"));
return dwError;

}


do {


if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)

{

Trouve =1;
MessageBox(NULL, ffd.cFileName , "test", MB_ICONWARNING | MB_OK);


}

else

{

filesize.LowPart = ffd.nFileSizeLow;
filesize.HighPart = ffd.nFileSizeHigh;
MessageBox(NULL, ffd.cFileName , "test", MB_ICONWARNING | MB_OK);
SendMessage(hEditK, WM_SETTEXT, 0, (long)filesize.QuadPart);

}


} while (FindNextFile(hFind2, &ffd) !=0);


if (Trouve == 0)

{

MessageBox(NULL, "Il n'existe aucun fichier qui correspond à la demande", "Erreur", MB_ICONWARNING | MB_OK);

}


dwError = GetLastError();



if (dwError != ERROR_NO_MORE_FILES)
{
DisplayErrorBox(TEXT("FindFirstFile"));
msgboxID = MessageBox(NULL, "Erreur", "Erreur", MB_ICONWARNING | MB_OK);

}

FindClose(hFind);
return dwError;

}
Messages postés
5550
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
12 mai 2021
933
J'ai repris ce même code, cela me fait pareil ... il n'y a pas de procédure récursive :

main.c :

 

// Code présent au-dessus

if (hEditJ !=0 && hEditI !=0)


{

FindFilesRecursively(_T(bufferCheminSousDossier), _T("*."));

}

// ..........


fonction.c :

HANDLE hFind3 = INVALID_HANDLE_VALUE;
TCHAR szFullPattern[MAX_PATH];



void FindFilesRecursively(LPCTSTR lpFolder, LPCTSTR lpFilePattern)
{

PathCombine(szFullPattern, lpFolder, _T("*"));
hFind3= FindFirstFile(szFullPattern, &ffd);

if(hFind3 != INVALID_HANDLE_VALUE)
{
do
{
if(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{

PathCombine(szFullPattern, lpFolder, ffd.cFileName);
FindFilesRecursively(szFullPattern, lpFilePattern);

}

} while(FindNextFile(hFind3, &ffd));
FindClose(hFind3);
}



PathCombine(szFullPattern, lpFolder, lpFilePattern);
hFind3 = FindFirstFile(szFullPattern, &ffd);

if(hFind3 != INVALID_HANDLE_VALUE)
{
do
{
if(!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{

PathCombine(szFullPattern, lpFolder, ffd.cFileName);
MessageBox(NULL, szFullPattern , "Liste", MB_OK);
}
} while(FindNextFile(hFind3, &ffd));
FindClose(hFind3);
}
}
Au niveau de la recherche de dossiers, j'obtiens des chemins du type \*\*\*\*
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
Bonjour Telnaz,

Et comme ça ? (scan_dir s'appelle elle-même recursivement) :

#include <windows.h>

void scan_dir(char *path)
{
 WIN32_FIND_DATA fd;
 void *dir;
 char newp[80], search_path[80], *filename;

 strlwr(path);

 strcpy(search_path, path);
 strcat(search_path, "*.*");

 dir = FindFirstFile(search_path, &fd); // . DOS
 FindNextFile(dir, &fd);   // .. DOS

 while(FindNextFile(dir, &fd))
 {
  if((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) // file
  {
   filename = fd.cFileName;
  }
  else              //directory
  {
   strcpy(newp, path);
   strcat(newp, fd.cFileName);
   strcat(newp, "\\");
   scan_dir(newp);
  }
 }

 FindClose(dir);
}
Non cela fonctionne mal avec mon code, j'ai beaucoup d'erreur ...

Pour revenir au code précédent, j'ai remarqué que le valeur du szFullPattern, lorsque l'on cherche les fichiers correspondants, ne changeait pas. C'est-à-dire, il ne suit pas la procédure récursive qui est faite lors de la recherche des dossiers ...

Je pense donc qu'il manque une boucle à ce niveau là ... Mais je n'ai pas réussi à améliorer la chose
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
Ah OK,

Je proposais cette fonction récursive car je m'en servais souvent dans divers projets pour explorer une arborescence de répertoires (en profondeur d'abord) et elle fonctionnait très bien

Evidemment, j'ai simplifié, dans le if où on détecte que c'est un fichier, je faisais quelque chose de filename ......

J'y avais même inclus les fonctions setjump() et longjump() pour re sauter à mon programme principal dès que j'avais trouvé le fichier recherché, et éviter tous les dépilages des appels successifs de la fonction
J'ai corrigé les nombreux soucis de compatibilité ...

Mais, après compilation et lancement de l'interface, je n'ai rien qui est renvoyé lors de l'exécution de la fonction.

J'ai, un doute, quand j'appelle la fonction dans le main.c, j'écris :

scan_dir(bufferCheminSousDossier);

?

bufferCheminSousDossier est le chemin absolu du sous dossier que je dois analyser
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
Comme je te l'expliquais, j'ai simplifié pour montrer la structure, donc il ne se passe pas grand chose si tu à copié ma fonction telle quelle

Par exemple, dans un projet, je stockais le nom du fichier trouvé dans un fichier texte:

if((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) // file
{
    ................
    fprintf(fp, "%s%s\n",path, filename);
} else {
    //répertoire
    ........
}


l'appel était comme ceci:

scan_dir('C:\\phil\\resultat\\');
Oui bien sûr, j'avais écris cela :

if((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) // file
{
    filename = ffd.cFileName;
    fichier=fopen("donnees.txt", "w+");
    fprintf(fichier, "%s%s\n",path, filename);
    fclose(fichier);

} else {
  .....
}   


Mais rien ne s'affiche dans le fichier texte créé ...
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
C'est

filename = fd.cFileName;


et non pas

filename = ffd.cFileName;
J'ai défini cette structure avec "ffd" ...dans le fichier ressource

(j'ai corrigé, erreur de frappe non faite sur le code original)
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
ok, mais dans le test if tu as écris:

if((fd.dwFileAttributes ....


avec fd
Oui c'est pour cela que j'ai dis avoir corrigé. Désolé

Au niveau du main.c, j'ai essayé scan_dir('D:\\MA_atp\\');

Cela me dit : too many characters in character constant

De base ce n'est pas ce chemin que je voulais analyser, mais j'ai voulu tester si le problème venait de là ...
Après test, j'ai même remarqué que la fonction ne passait pas la boucle while ...
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
ah!

attend voir, ce ne serait pas plutôt:

scan_dir("D:\\MA_atp\\");


avec double quotes

(je suis en train de faire du Python, et là c'est la même chose, mais pas en C)
Non finalement le problème ne vient pas de là, mais du code ... Comme je t'ai dis, la boucle while n'est pas effectuée..bizarre

J'ai remarqué cela avec des printf ...
Messages postés
1047
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
11 mai 2021
112
ok, mais tu as bien mis des double quotes pour re tester ??

too many characters in character constant

ça venait bien de l'emploi de simple quotes

Sinon, bizarre .... si il n'entre pas dans la boucle, c'est que FindNextFile(dir, &fd) renvoi false, donc qu'il ne trouve

rien comme fichiers ou répertoires dans dir
Oui oui ne t'inquiète pas ... désolé j'oublie de tout te préciser mais c'est que j'ai fais pas mal de changements...

Aux dernières nouvelles, j'essaie de comprendre pourquoi il n'analyse pas les fichiers...

J'ai vérifié que la variable path comprenait bien le chemin, mais ensuite ça n'a pas l'air de fonctionner
Messages postés
5550
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
12 mai 2021
933
Salut Telnaz,

Je suis sous Linux habituellement, alors il m'est difficile de tester les codes Win32.

Le code posté sur SO boucle sur le même répertoire racine et j'ai la flemme de le déboguer tellement il comporte de code à la sauce MS.

Alors en voilà un autre, qui est fortement inspiré de celui posté dans ce fil https://arstechnica.com/civis/viewtopic.php?t=817827 par earl.

#include <stdio.h>
#include <windows.h>

void recursively_list_files(char * dir) {
	HANDLE hFind;
	WIN32_FIND_DATA fd;
	char path[MAX_PATH + 1];

	sprintf(path, "%s\\*", dir);

	printf("---- dans le repertoire %s ----\n\n", dir);

 	if ((hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE) {
		printf("Erreur FindFirstFIle sur %s\n", path);
		exit(1);
	}

	while(1) {
		if ((strcmp(".", fd.cFileName) !=0) && (strcmp("..", fd.cFileName) != 0)) {
			if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                /* c'est un dossier */
				sprintf(path, "%s\\%s", dir, fd.cFileName);
				recursively_list_files(path);
			} else {
				/* c'est un fichier */
				printf("fichier : %s\n", fd.cFileName);
			}
		}
		if (!FindNextFile(hFind, &fd))
            break;
	}
	if (GetLastError() != ERROR_NO_MORE_FILES) {
		printf("Erreur FindNextFile - dans %s\n", path);
		exit(1);
	}
	if (FindClose(hFind) == FALSE) {
		printf("Erreur FindClose\n");
		exit(1);
	}
}


int main(void) {
    /* sur mon disque C: j'ai un répertoire Drivers avec 58 Fichiers dans 26 Dossiers */
    recursively_list_files("C:\\Drivers");

    return 0;
}

Ce code parcourt bien les répertoires. Il évite les entrées "." qui correspondent au répertoire courant et ".." qui correspond au répertoire parent et chez moi, il liste bien les 58 Fichiers contenus dans 26 Dossiers situés dans un répertoire qui se trouve à la racine de C:\ et qui s'appelle "Drivers".

---- dans le repertoire C:\Drivers ----

---- dans le repertoire C:\Drivers\input ----

---- dans le repertoire C:\Drivers\input\9FT4R ----

---- dans le repertoire C:\Drivers\input\9FT4R\Bin ----

---- dans le repertoire C:\Drivers\input\9FT4R\Bin\production ----

---- dans le repertoire C:\Drivers\input\9FT4R\Bin\production\Windows10-x64 ----

fichier : HidEventFilter.cat
fichier : HidEventFilter.inf
fichier : HidEventFilter.sys
---- dans le repertoire C:\Drivers\network ----

---- dans le repertoire C:\Drivers\network\6YDJP ----

---- dans le repertoire C:\Drivers\network\6YDJP\Windows ----

---- dans le repertoire C:\Drivers\network\6YDJP\Windows\WIN10 ----

---- dans le repertoire C:\Drivers\network\6YDJP\Windows\WIN10\32 ----

fichier : rtux86w10.cat
fichier : rtux86w10.INF
fichier : rtux86w10.sys
---- dans le repertoire C:\Drivers\network\6YDJP\Windows\WIN10\64 ----

fichier : rtux64w10.cat
fichier : rtux64w10.INF
fichier : rtux64w10.sys
---- dans le repertoire C:\Drivers\storage ----

---- dans le repertoire C:\Drivers\storage\1NN1D ----

---- dans le repertoire C:\Drivers\storage\1NN1D\Drivers ----

---- dans le repertoire C:\Drivers\storage\1NN1D\Drivers\Production ----

---- dans le repertoire C:\Drivers\storage\1NN1D\Drivers\Production\Windows10-x64 ----

fichier : dpinst.exe
fichier : HfcDisableService.exe
fichier : iaAHCIC.cat
fichier : iaAHCIC.inf
fichier : iaStorAC.cat
fichier : iaStorAC.inf
fichier : iaStorAC.sys
fichier : iaStorAfs.sys
fichier : iaStorAfsNative.exe
fichier : iaStorAfsService.exe
fichier : iaStorSW.cat
fichier : iaStorSW.inf
fichier : iaStorSwExt.cat
fichier : iaStorSwExt.inf
fichier : Optane.dll
fichier : RstMwService.exe
fichier : ShellPackage.msi
---- dans le repertoire C:\Drivers\storage\M31NN ----

---- dans le repertoire C:\Drivers\storage\M31NN\Drivers ----

---- dans le repertoire C:\Drivers\storage\M31NN\Drivers\x64 ----

fichier : iaLPSS2_GPIO2.sys
fichier : ialpss2_gpio2_cnl.cat
fichier : iaLPSS2_GPIO2_CNL.inf
fichier : iaLPSS2_I2C.sys
fichier : ialpss2_i2c_cnl.cat
fichier : iaLPSS2_I2C_CNL.inf
fichier : iaLPSS2_SPI.sys
fichier : ialpss2_spi_cnl.cat
fichier : iaLPSS2_SPI_CNL.inf
fichier : iaLPSS2_UART2.sys
fichier : ialpss2_uart2_cnl.cat
fichier : iaLPSS2_UART2_CNL.inf
---- dans le repertoire C:\Drivers\storage\R16KJ ----

---- dans le repertoire C:\Drivers\storage\R16KJ\Install ----

---- dans le repertoire C:\Drivers\storage\R16KJ\Install\DrvBin64 ----

---- dans le repertoire C:\Drivers\storage\R16KJ\Install\DrvBin64\ExtInf ----

fichier : RtsCrExt.inf
fichier : rtscrext64.cat
fichier : SDRTCPRM.dll
fichier : RsCRIcon.dll
fichier : RtsPer.sys
fichier : rtsper64.cat
fichier : RtsperDLnQS.inf
---- dans le repertoire C:\Drivers\storage\TBT51 ----

---- dans le repertoire C:\Drivers\storage\TBT51\production ----

---- dans le repertoire C:\Drivers\storage\TBT51\production\Windows10-x64 ----

fichier : TbtBusDrv.sys
fichier : TbtControlCenterToastLauncher.exe
fichier : TbtFilterDrv.dll
fichier : tbthostcontroller.cat
fichier : TbtHostController.inf
fichier : tbthostcontrollerextension.cat
fichier : TbtHostControllerExtension.inf
fichier : tbthostcontrollerhsacomponent.cat
fichier : TbtHostControllerHsaComponent.inf
fichier : tbtp2pndisdrv.cat
fichier : TbtP2pNdisDrv.inf
fichier : TbtP2pNdisDrv.sys
fichier : ThunderboltService.exe


Dal
Cela fonctionne très bien, merci beaucoup. Il me reste tout de même un problème d'affichage à régler.
Je n'ai pas de console, uniquement une interface graphique ... Pour vérifier si cela fonctionnait, j'ai ajouté un "MessageBox" pour bien afficher tous les fichiers trouvés.

Cependant, l'objectif est de lister les dossiers et fichiers comme tu l'as montré.

J'ai ajouté ceci :

...
char path[MAX_PATH +1]
FILE* fichier;
fichier = fopen("donnees.txt", "w+");

sprintf(path, "%s\\*", dir);

fprintf(fichier, "---- dans le repertoire %s ----\n\n", dir);

if ....

} else {
    /* c'est un fichier */
    fprintf(fichier, "fichier : %s\n", fd.cFileName);

....

fclose(fichier);

}


Cependant, lors de l’exécution, je n'ai pas tous les fichiers d'afficher, seulement quelques uns et le "dans le repertoire " n'est affiché qu'une seule fois ...

PS : je n'ai pas utilisé la fonction main(void), j'ai directement écris recursively_list_files(chemin); dans une boucle if dans mon main.c
Messages postés
5550
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
12 mai 2021
933 > Telnaz
Comme la fonction est appelée récursivement, à chaque lancement de la fonction, tu crées un nouveau fichier donnees.txt qui écrase l'ancien (et qui fait d'autres choses horribles au retour de la fonction, car le pointeur sur FILE n'est pas le même).

Le plus simple dans ton cas est de faire :

FILE * fichier;
fichier = fopen("donnees.txt", "w+");


dans la fonction qui appelle recursively_list_files() et de passer le pointeur sur FILE à cette fonction, dont le prototype serait alors
void recursively_list_files(FILE * fichier, char * dir)
.

Ainsi, à chaque appel récursif de recursively_list_files() c'est le même pointeur sur FILE qui est passé et utilisé pour écrire dans le fichier ouvert en écriture.

Le
fclose(fichier);
devrait aussi être dans la fonction qui appelle recursively_list_files() pour des raisons similaires.
>
Messages postés
5550
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
12 mai 2021

Merci infiniment, tu es très fort !!