Je sollicite l'aide des connaisseurs en threads.
Ci-dessous, un (bout de, expurgé au mieux) code destiné à lister récursivement le contenu de plusieurs dossiers en faisant appel à un thread.
L'objectif du code complet, réalisé pour le compte d'une personne informatiquement bordélique, est :
• Dans un premier temps, de récupérer la liste de tous les dossiers et fichiers non système situés dans divers répertoires et les déplacer vers un dossier temporaire.
• Dans un deuxième temps, idem, lister les répertoires, sous-répertoires et fichiers d'un dossier cible choisi par l'utilisateur
• Dans un troisième temps, rechercher des doublons réels ou potentiels selon le nom, la taille et/ou la date de modif entre les fichiers de ces deux listes
• Et pour finir, permettre à l'utilisateur de supprimer les doublons qu'il souhaite
Pour cela, je souhaite utiliser un thread pour chacune de ces étapes pour permettre, entre autres, à l'utilisateur de pouvoir mettre en pause voire arrêter le processus en cours.
A cette fin, j'utilise une variable structurée créée pour contenir diverses infos sur le thread, son état, la liste des dossiers à scanner, etc...
Mon idée est de lancer, pour les trois premières étapes, des threads l'un après l'autre, en bloquant l'exécution du programme principal entre chacun d'eux par un WaitThred(). Voir procédure Pc_Traitement_BtFPLancer() lignes 340 et 343.
Et c'est là que le bât blesse ! WaitThread() n'a pas le comportement auquel je m'attendais.
En effet, l'instruction bloque tout ! Et le thread et le programme principal
Je m'y prends très vraisemblablement mal mais je ne comprends pas comment et où...
Ici, le bout de code :
Code : Tout sélectionner
EnableExplicit
; ╔═════════════════════════════════════════════════════════════════════════════╗
; ║ STRUCTURES - ENUMERATIONS - CONSTANTES - MACROS - MAPS - VARIABLES GLOBALES ║
; ╚═════════════════════════════════════════════════════════════════════════════╝
;{ ════ STRUCTURES ════
;- ════ STRUCTURES ════
Structure FICHIER
Nom.s
Chemin.s
Taille.q
DateModif.l
Attributs.u
FichierDeplace.a
EndStructure
Structure INFOSTHREAD
IDThread.i
NoThread.i
Signal.i
Pause.i
Fin.i
EndStructure
Structure DONNEESTHREAD Extends INFOSTHREAD
; Data
Fenetre.i
NiveauRecursif.i
CheminOriginel.s
Chemin.s
Filtre.s
Initialisation.a
List Dossiers.s()
EndStructure
;}
;{ ════ ENUMERATIONS ════
;- ════ ENUMERATIONS ════
Runtime Enumeration Fenetres ;{
#FEN_PRINCIPALE
EndEnumeration ;}
Runtime Enumeration Gadgets ;{ Gadgets généraux
#GAD_FP_BG_DOSSIERS
#GAD_FP_LS_DOSSIERSSOURCE
#GAD_FP_LIB_DOSSIERCIBLE
#GAD_FP_LIB_ACTION
#GAD_FP_BG_INFOS
#GAD_FP_LIB_DOSSIER
#GAD_FP_LIB_NOMDOSSIER
#GAD_FP_LIB_FICHIER
#GAD_FP_LIB_NOMFICHIER
#GAD_FP_BT_LANCER
#GAD_FP_BT_QUITTER
EndEnumeration ;}
Enumeration XML ;{
#ARBRE_XML
EndEnumeration ;}
Enumeration DialoguesXML ;{
#XML_FENPRINCIPALE
EndEnumeration ;}
Enumeration #PB_Event_FirstCustomValue
#EVENMTTHREAD_ANALYSETERMINEE
#EVENMTTHREAD_ANALYSEANNULEE
;#MyEvent_ThreadGetFilesString
EndEnumeration
;}
;{ ════ MACROS ════
; -════ MACROS ════
Macro Mc_FormatageXML(ArgChaine)
EscapeString(ArgChaine,#PB_String_EscapeXML)
EndMacro
Macro Mc_DeformatageXML(ArgChaine)
UnescapeString(ArgChaine,#PB_String_EscapeXML)
EndMacro
;}
;{ ════ LISTES ════
;- ════ LISTES ════
Global NewList Fichiers.FICHIER()
;}
;-══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
; ╔═══════════════════╗
; ║ PROCEDURES THREAD ║
; ╚═══════════════════╝
Procedure Pc_Initialisation_ThreadAnalyseDossiers()
Global *DonneesThread.DONNEESTHREAD=AllocateStructure(DONNEESTHREAD)
EndProcedure
Procedure.i Fc_Thread_Demarrage(*ArgThread.INFOSTHREAD,*ArgProcedure) ; ThreadID
With *ArgThread
If Not IsThread(\IDThread)
\Fin=#False
\Pause=#False
\IDThread=CreateThread(*ArgProcedure,*ArgThread)
EndIf
ProcedureReturn \IDThread
EndWith
EndProcedure
Procedure Pc_Thread_Arret(*ArgThread.INFOSTHREAD,ArgAttente=1000)
With *ArgThread
If IsThread(\IDThread)
\Fin=#True
If \Pause
\Pause=#False
SignalSemaphore(\Signal)
EndIf
If ArgAttente
If WaitThread(\IDThread,ArgAttente)=0
KillThread(\IDThread)
EndIf
\IDThread=0
\Pause=#False
\Fin=#False
If \Signal
FreeSemaphore(\Signal)
\Signal=0
EndIf
EndIf
EndIf
EndWith
EndProcedure
Procedure.a Fc_Thread_Liberation(*ArgThread.INFOSTHREAD,ArgArret.a=#True,ArgAttente=1000) ; True or False
With *ArgThread
If IsThread(\IDThread)
If ArgArret
Pc_Thread_Arret(*ArgThread,ArgAttente)
FreeStructure(*ArgThread)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
Else
If \Signal
FreeSemaphore(\Signal)
EndIf
FreeStructure(*ArgThread)
ProcedureReturn #True
EndIf
EndWith
EndProcedure
Procedure Pc_Thread_Pause(*ArgThread.INFOSTHREAD)
With *ArgThread
If IsThread(\IDThread)
If Not \Signal
\Signal=CreateSemaphore()
EndIf
If Not \Pause
\Pause=#True
EndIf
EndIf
EndWith
EndProcedure
Procedure Pc_Thread_Reprise(*ArgThread.INFOSTHREAD)
With *ArgThread
If IsThread(\IDThread)
If \Pause
\Pause=#False
SignalSemaphore(\Signal)
EndIf
EndIf
EndWith
EndProcedure
;-══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
; ╔══════════════╗
; ║ FENETRES XML ║
; ╚══════════════╝
Procedure.a Fc_Fenetres_OuvertureDialogueXML(ArgArbreXML.u,ArgDialogueXML.u,ArgChaineXML.s,ArgNomFenetre.s,ArgIdFenetreParente.i=0)
Protected.i NoDialogue
Protected.a TypeErreur
Protected.s TitreMSG="Application",MsgErreur
If ArgChaineXML
If ParseXML(ArgArbreXML,ArgChaineXML)
If XMLStatus(ArgArbreXML)=#PB_XML_Success
If CreateDialog(ArgDialogueXML)
If ArgIdFenetreParente<>0
NoDialogue=OpenXMLDialog(ArgDialogueXML,ArgArbreXML,ArgNomFenetre,0,0,0,0,ArgIdFenetreParente)
Else
NoDialogue=OpenXMLDialog(ArgDialogueXML,ArgArbreXML,ArgNomFenetre,0,0,0,0)
EndIf
If NoDialogue
FreeXML(ArgArbreXML)
ProcedureReturn #True
Else
TypeErreur=3
EndIf
FreeDialog(ArgDialogueXML)
Else
TypeErreur=2
EndIf
FreeXML(ArgArbreXML)
Else
TypeErreur=1
MsgErreur="Erreur durant l'interprétation du dialogue XML !"
EndIf
Else
TypeErreur=1
MsgErreur="Erreur durant la lecture des données du dialogue XML !"
EndIf
EndIf
Select TypeErreur
Case 1:MsgErreur+"\n\n No erreur : "+XMLStatus(ArgArbreXML)+" - "+XMLError(ArgArbreXML)+"\n Ligne : "+Str(XMLErrorLine(ArgArbreXML))+" - Colonne : "+Str(XMLErrorPosition(ArgArbreXML))
Case 2:MsgErreur="Erreur durant la création de la fenêtre de dialogue !"
Case 3:MsgErreur="Erreur durant l'ouverture de la fenêtre de dialogue !\n\nErreur : "+DialogError(ArgDialogueXML)
EndSelect
MessageRequester(TitreMSG,UnescapeString(MsgErreur),#PB_MessageRequester_Error)
EndProcedure
; ╔═══════════════════════════════╗
; ║ PROCEDURES FENETRE PRINCIPALE ║
; ╚═══════════════════════════════╝
Procedure Pc_Gestion_BtFPLancer(ArgEtat.a)
Protected.s Chaine
If GetGadgetData(#GAD_FP_BT_LANCER)<>ArgEtat
Select ArgEtat
Case 0:Chaine="&Lancer"
Case 1:Chaine="&Pause"
Case 2:Chaine="&Reprise"
EndSelect
SetGadgetText(#GAD_FP_BT_LANCER,Chaine)
SetGadgetData(#GAD_FP_BT_LANCER,ArgEtat)
EndIf
EndProcedure
Procedure Pc_Gestion_BtFPQuitter(ArgEtat.a)
Protected.s Chaine
If GetGadgetData(#GAD_FP_BT_QUITTER)<>ArgEtat
Select ArgEtat
Case 0:Chaine="&Quitter"
Case 1:Chaine="&Arrêter"
EndSelect
SetGadgetText(#GAD_FP_BT_QUITTER,Chaine)
SetGadgetData(#GAD_FP_BT_QUITTER,ArgEtat)
EndIf
EndProcedure
Procedure Pc_Analyse_Dossier(*ArgDonneesThread.DONNEESTHREAD)
Protected.FICHIER Fichier
Protected.i NoDossier
Protected.s Chemin,Extension
;Debug "Pc_Analyse_Dossier"
With *ArgDonneesThread
If \NiveauRecursif>0
Chemin=\Chemin
Else
NextElement(\Dossiers())
Chemin=\CheminOriginel
Chemin=\Dossiers()
EndIf
\NiveauRecursif+1
EndWith
NoDossier=ExamineDirectory(#PB_Any,Chemin,"*.*")
If NoDossier
Debug " Analyse : "+Chemin
;Debug " NoDossier "+NoDossier
While NextDirectoryEntry(NoDossier)
If *ArgDonneesThread\Pause
;PostEvent(#MyEvent_ThreadGetFilesString,\Fenetre,0,0,;Fc_Affectation_Chaine(ChaineDonnees))
WaitSemaphore(*ArgDonneesThread\Signal)
;PostEvent(#MyEvent_ThreadGetFilesString,\Fenetre,0,0,Fc_Affectation_Chaine(ChaineDonnees))
EndIf
If *ArgDonneesThread\Fin
Break
EndIf
With Fichier
\Nom=DirectoryEntryName(NoDossier)
;Debug " Nom : "+\nom
\Chemin=Chemin
\Attributs=GetFileAttributes(\Chemin+\Nom)
If DirectoryEntryType(NoDossier)=#PB_DirectoryEntry_Directory
If \Nom<>"." And \Nom<>".."
If Not (\Attributs&#PB_FileSystem_Hidden And \Attributs&#PB_FileSystem_System)
Delay(10)
*ArgDonneesThread\Chemin=\Chemin+\Nom+"\"
Pc_Analyse_Dossier(*ArgDonneesThread)
EndIf
EndIf
Else
If GetGadgetText(#GAD_FP_LIB_NOMDOSSIER)<>\Chemin
SetGadgetText(#GAD_FP_LIB_NOMDOSSIER,\Chemin)
EndIf
If Not (\Attributs&#PB_FileSystem_Hidden And \Attributs&#PB_FileSystem_System)
SetGadgetText(#GAD_FP_LIB_NOMFICHIER,\Nom)
\Taille=FileSize(\Chemin+\Nom)
\DateModif=GetFileDate(\Chemin+\Nom,#PB_Date_Modified)
;Debug " Fichier retenu : "+Fichier\Nom
AddElement(Fichiers())
Fichiers()=Fichier
EndIf
EndIf
EndWith
ClearStructure(@Fichier,FICHIER)
Wend
FinishDirectory(NoDossier)
EndIf
With *ArgDonneesThread
\NiveauRecursif-1
If \NiveauRecursif=0
If \Fin
PostEvent(#EVENMTTHREAD_ANALYSEANNULEE,\Fenetre,0,0,*ArgDonneesThread)
\IDThread=0
Else
If ListIndex(*ArgDonneesThread\Dossiers())=ListSize(*ArgDonneesThread\Dossiers())-1
PostEvent(#EVENMTTHREAD_ANALYSETERMINEE,\Fenetre,0,0,*ArgDonneesThread)
\IDThread=0
Else
Pc_Analyse_Dossier(*ArgDonneesThread)
EndIf
EndIf
EndIf
EndWith
EndProcedure
; Procedures Boutons & Raccourcis
Procedure Pc_Traitement_BtFPLancer()
Protected.u NbDossiers,Compteur
Protected.a EtatGadget=GetGadgetData(#GAD_FP_BT_LANCER)
Protected.s Dossier
Select EtatGadget
Case 0 ; Lancer
If Not IsThread(*DonneesThread\IDThread)
NbDossiers=CountGadgetItems(#GAD_FP_LS_DOSSIERSSOURCE)
If NbDossiers
SetGadgetText(#GAD_FP_LIB_ACTION,"Inventaire des dossiers et fichiers à déplacer : ")
ClearList(Fichiers())
ClearList(*DonneesThread\Dossiers())
NbDossiers-1
For Compteur=0 To NbDossiers
Dossier=GetGadgetItemText(#GAD_FP_LS_DOSSIERSSOURCE,Compteur)
Select UCase(Dossier)
Case "[BUREAU]":Dossier=GetUserDirectory(#PB_Directory_Desktop)
Case "[DOCUMENTS]":Dossier=GetUserDirectory(#PB_Directory_Documents)
Case "[TÉLÉCHARGEMENTS]":Dossier=GetUserDirectory(#PB_Directory_Downloads)
EndSelect
With *DonneesThread
\NiveauRecursif=0
\Fenetre=#FEN_PRINCIPALE
AddElement(*DonneesThread\Dossiers())
*DonneesThread\Dossiers()=Dossier
;\Filtre=LCase(ArgFiltre)
EndWith
Next
Pc_Gestion_BtFPLancer(1):Pc_Gestion_BtFPQuitter(1)
ResetList(*DonneesThread\Dossiers())
Fc_Thread_Demarrage(*DonneesThread,@Pc_Analyse_Dossier())
Debug *DonneesThread\IDThread
If *DonneesThread\IDThread
WaitThread(*DonneesThread\IDThread)
EndIf
EndIf
Else
EndIf
Case 1 ; Pause
If IsThread(*DonneesThread\IDThread) And Not *DonneesThread\Pause
Pc_Thread_Pause(*DonneesThread)
Pc_Gestion_BtFPLancer(2)
EndIf
Case 2 ; Reprendre
If IsThread(*DonneesThread\IDThread) And *DonneesThread\Pause
Pc_Thread_Reprise(*DonneesThread)
Pc_Gestion_BtFPLancer(1)
EndIf
EndSelect
EndProcedure
Procedure Pc_Traitement_BtFPQuitter()
Protected.a EtatGadget=GetGadgetData(#GAD_FP_BT_Quitter)
Select EtatGadget
Case 0 ; Quitter
Fc_Thread_Liberation(*DonneesThread)
End
Case 1 ; Arrêter
If IsThread(*DonneesThread\IDThread)
Pc_Thread_Pause(*DonneesThread)
If MessageRequester("Analyse de dossiers","Confirmer l'arrêt de l'analyse des dossiers ?",#PB_MessageRequester_YesNo|#MB_ICONQUESTION)=#PB_MessageRequester_Yes
Pc_Thread_Arret(*DonneesThread)
ClearList(Fichiers())
Else
Pc_Thread_Reprise(*DonneesThread)
EndIf
EndIf
EndSelect
EndProcedure
Procedure Pc_Reinitialisation_GadgetsFP()
SetGadgetText(#GAD_FP_LIB_ACTION,"")
SetGadgetText(#GAD_FP_LIB_NOMDOSSIER,"")
SetGadgetText(#GAD_FP_LIB_NOMFICHIER,"")
Pc_Gestion_BtFPLancer(0)
Pc_Gestion_BtFPQuitter(0)
EndProcedure
;
Procedure Pc_FenPrincipale_GestionEvenements()
Define.l Evenmt,EvenmtMenu,TypeEvenmt,NoGadget
Repeat
Evenmt=WaitWindowEvent()
TypeEvenmt=EventType()
Select Evenmt
Case #PB_Event_Gadget
NoGadget=EventGadget()
Select TypeEvenmt
Case #PB_EventType_LeftClick
Select NoGadget
Case #GAD_FP_BT_LANCER:Pc_Traitement_BtFPLancer()
Case #GAD_FP_BT_QUITTER:Pc_Traitement_BtFPQuitter()
EndSelect
EndSelect
Case #PB_Event_CloseWindow
End
Case #EVENMTTHREAD_ANALYSETERMINEE
;Debug "Analyse dossiers terminée !"
Pc_Gestion_BtFPLancer(0):Pc_Gestion_BtFPQuitter(0)
Case #EVENMTTHREAD_ANALYSEANNULEE
;Debug "Annulation analyse dossiers !"
Pc_Reinitialisation_GadgetsFP()
EndSelect
ForEver
EndProcedure
Procedure Pc_FenPrincipale_Affichage()
Protected.a ValeurRetour
Protected.s ChaineXML
ChaineXML="<?xml version='1.0' encoding='UTF-16'?>"+Chr(10)+ ;{
"<dialogs>"+Chr(10)+
" <window id='"+Str(#FEN_PRINCIPALE)+"' name='FEN_PRINCIPALE' text='"+Mc_FormatageXML("Synchronisation de fichiers")+"' xpos='106' ypos='102' width='800' minwidth='auto' minheight='auto' margin='10' flags='#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_SizeGadget'>"+Chr(10)+
" <vbox expand='item:1' spacing='8'>"+Chr(10)+
" <gridbox id='"+Str(#GAD_FP_BG_DOSSIERS)+"' name='GAD_FP_BG_DOSSIERS' columns='1' colspacing='4' colexpand='item:1' rowexpand='item:1'>"+Chr(10)+
" <listview id='"+Str(#GAD_FP_LS_DOSSIERSSOURCE)+"' name='GAD_FP_LS_DOSSIERSSOURCE' height='160' flags='#PB_ListView_Multiselect'/>"+Chr(10)+
" <text id='"+Str(#GAD_FP_LIB_DOSSIERCIBLE)+"' name='GAD_FP_LIB_DOSSIERCIBLE' text='"+Mc_FormatageXML(" ")+"'/>"+Chr(10)+
" </gridbox>"+Chr(10)+
" <singlebox expand='no' align='top,left' height='16' margin='0'/>"+Chr(10)+
" <text id='"+Str(#GAD_FP_LIB_ACTION)+"' name='GAD_FP_LIB_ACTION' text='"+Mc_FormatageXML(" ")+"'/>"+Chr(10)+
" <gridbox id='"+Str(#GAD_FP_BG_INFOS)+"' name='GAD_FP_BG_INFOS' invisible='yes' columns='3' colspacing='4' colexpand='item:3'>"+Chr(10)+
" <singlebox expand='no' width='0'/>"+Chr(10)+
" <text id='"+Str(#GAD_FP_LIB_DOSSIER)+"' name='GAD_FP_LIB_DOSSIER' text='"+Mc_FormatageXML("Dossier : ")+"'/>"+Chr(10)+
" <text id='"+Str(#GAD_FP_LIB_NOMDOSSIER)+"' name='GAD_FP_LIB_NOMDOSSIER' flags='#PB_Text_Border'/>"+Chr(10)+
" <singlebox expand='no' width='0'/>"+Chr(10)+
" <text id='"+Str(#GAD_FP_LIB_FICHIER)+"' name='GAD_FP_LIB_FICHIER' text='"+Mc_FormatageXML("Fichier : ")+"'/>"+Chr(10)+
" <text id='"+Str(#GAD_FP_LIB_NOMFICHIER)+"' name='GAD_FP_LIB_NOMFICHIER' flags='#PB_Text_Border'/>"+Chr(10)+
" </gridbox>"+Chr(10)+
" <singlebox expand='no' align='top,left' width='0' height='8' margin='0'/>"+Chr(10)+
" <hbox expand='item:1' spacing='8'>"+Chr(10)+
" <singlebox/>"+Chr(10)+
" <button id='"+Str(#GAD_FP_BT_LANCER)+"' name='GAD_FP_BT_LANCER' text='"+Mc_FormatageXML("&Lancer")+"' width='120'/>"+Chr(10)+
" <button id='"+Str(#GAD_FP_BT_QUITTER)+"' name='GAD_FP_BT_QUITTER' text='"+Mc_FormatageXML("&Quitter")+"' width='120'/> "+Chr(10)+
" </hbox>"+Chr(10)+
" <singlebox width='22'/>"+Chr(10)+
" </vbox>"+Chr(10)+
" </window>"+Chr(10)+
"</dialogs>" ;}
ValeurRetour=Fc_Fenetres_OuvertureDialogueXML(#ARBRE_XML,#XML_FENPRINCIPALE,ChaineXML,"FEN_PRINCIPALE")
If ValeurRetour
; Ajout Dossiers sources par défaut
AddGadgetItem(#GAD_FP_LS_DOSSIERSSOURCE,-1,"[Bureau]")
AddGadgetItem(#GAD_FP_LS_DOSSIERSSOURCE,-1,"[Documents]")
AddGadgetItem(#GAD_FP_LS_DOSSIERSSOURCE,-1,"[Téléchargements]")
SetGadgetItemData(#GAD_FP_LS_DOSSIERSSOURCE,0,0)
SetGadgetItemData(#GAD_FP_LS_DOSSIERSSOURCE,1,0)
SetGadgetItemData(#GAD_FP_LS_DOSSIERSSOURCE,2,0)
;
SetGadgetText(#GAD_FP_LIB_DOSSIERCIBLE,GetUserDirectory(#PB_Directory_ProgramData))
SetGadgetText(#GAD_FP_LIB_DOSSIERCIBLE,GetUserDirectory(#PB_Directory_Documents))
Pc_Initialisation_ThreadAnalyseDossiers()
Pc_FenPrincipale_GestionEvenements()
EndIf
EndProcedure
;-══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
; ╔══════════════════╗
; ║ MODULE PRINCIPAL ║
; ╚══════════════════╝
Pc_FenPrincipale_Affichage()
End
; IDE Options = PureBasic 6.02 LTS (Windows - x64)
; CursorPosition = 467
; FirstLine = 434
; EnableXP
; DPIAware
; CompileSourceDirectory
; EnablePurifier
Note : En dehors de la problématique de la gestion du thread, ne tenez pas compte de bizarreries que vous pourriez constater sur le reste du code car, je le rappelle, j'ai tenté de l'expurger au mieux de bouts inutiles pour la démonstration...