Imports System.Threading
Public NotInheritable Class ClsScandir
#Region "Attributs privés"
    Private mPath As String          'Chemins à scannés (Path1;Path2)
    Private mExtInclude As String    'Extensions à inclure (*.a*;sd*.exe)
    Private mExtExclude As String    'Extensions à exclure
    Private mSRep As Boolean         'True si on scanne les sous-dossiers

    Private mLstFile As ClsFifoFile   'Liste des noms de fichier
    Private mLstPath As ClsFifoPath   'Liste des répertoires à scanner

    Private mTimerDep As Integer     'Heure de départ (en ms depuis le démarrage de l'ordi)

    Private mFileElapsed As Integer  'Nombre de fichiers déjà retourné au consomateur
    Private mDirElapsed As Integer   'Nombre de dossiers scannés

    Private mActPath As String       'Dossier actuellement traité

    Private mAutoRestart As Boolean  'True si le fait de changer un des 4 premiers attribut redémarre le scan

    'Liste des dossiers à scanner
    Dim mExtInc As String()          'Liste des extensions à inclure
    Dim mExtExc As String()          'Liste des extensions à exclure

    Private CanThreadRun As Boolean  'True si le thread peut continuer de fonctionner
    Private ThreadRun As Boolean     'True si le thread continu de fonctionner

    Private ThreadScandir As Thread  'Objet représentant le thread
#End Region
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Constructeur de la classe
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Sub New()
        mPath = "c:\"
        mExtInclude = "*"
        mExtExclude = ""
        mSRep = True
        mLstFile = New ClsFifoFile
        mLstPath = New ClsFifoPath
        mTimerDep = Environment.TickCount
        mFileElapsed = 0
        mDirElapsed = 0
        mActPath = ""
        mAutoRestart = True
        CanThreadRun = True
        ThreadRun = False
        ThreadScandir = Nothing
    End Sub
#Region "Propriétés"
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne on définit la liste des dossiers à scanner (c:\;d:\dataperso\;\\poste1\c$;...)
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property Path() As String
        Get
            Return mPath
        End Get
        Set(ByVal Value As String)
            mPath = Value
            If mAutoRestart And ThreadRun Then RestartAnalyse()
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne ou définit les extensions à inclure (*.dll;*.e*,kernel32.dll;...)
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property ExtInclude() As String
        Get
            Return mExtInclude
        End Get
        Set(ByVal Value As String)
            mExtInclude = Value
            If mAutoRestart And ThreadRun Then RestartAnalyse()
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne ou définit les fichiers à exclure (*.dll;*.e*,kernel32.dll;...)
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property ExtExclude() As String
        Get
            Return mExtExclude
        End Get
        Set(ByVal Value As String)
            mExtExclude = Value
            If mAutoRestart And ThreadRun Then RestartAnalyse()
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne ou définit si l'analyse s'effectue aussi dans les sous-dossiers
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property SRep() As Boolean
        Get
            Return mSRep
        End Get
        Set(ByVal Value As Boolean)
            mSRep = Value
            If mAutoRestart And ThreadRun Then RestartAnalyse()
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne ou définit si une modification de SRep, ExtInclude, ExtExclude ou Path entraine le redémarrage du scan
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property AutoRestart() As Boolean
        Get
            Return mAutoRestart
        End Get
        Set(ByVal Value As Boolean)
            mAutoRestart = Value
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le dossier actuellement scanné
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property ActPath() As String
        Get
            Return mActPath
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le nombre de fichiers récupérés
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property NbFileElapsed() As Integer
        Get
            Return mFileElapsed
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le nombre total de fichiers restant à récupérer
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property NbFileRemain() As Integer
        Get
            Return mLstFile.Count
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le nombre total de fichiers répertoriés
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property NbFileTotal() As Integer
        Get
            Return mFileElapsed + mLstFile.Count
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le nombre de dossiers scannés
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property NbDirElapsed() As Integer
        Get
            Return mDirElapsed
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le nombre de dossiers restant à scanner
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property NbDirRemain() As Integer
        Get
            Return mLstPath.Count
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le nombre total de dossiers répertoriés
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property NbDirTotal() As Integer
        Get
            Return mDirElapsed + mLstPath.Count
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le temps passé pour la récupération des fichiers
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property TimeFileElapsed() As Integer
        Get
            If Environment.TickCount < mTimerDep Then mTimerDep = Environment.TickCount
            Return CInt((Environment.TickCount - mTimerDep) / 1000)
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le temps restant estimé pour la récupération des fichiers
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property TimeFileRemain() As Integer
        Get
            If mFileElapsed <> 0 Then
                Return CInt(((TimeFileElapsed / CDbl(mFileElapsed)) * mLstFile.Count))
            Else
                Return 0
            End If
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le temps total estimé pour la récupération des fichiers par l'application
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property TimeFileTotal() As Integer
        Get
            Return TimeFileElapsed + TimeFileRemain
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le temps passé à l'analyse des dossiers
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property TimeDirElapsed() As Integer
        Get
            Return TimeFileElapsed
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne le temps restant estimé pour l'analyse des dossiers
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property TimeDirRemain() As Integer
        Get
            If mDirElapsed <> 0 Then
                Return CInt((TimeFileElapsed / CDbl(mDirElapsed)) * mLstPath.Count)
            Else
                Return 0
            End If
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Retourne la durée total prévue pour l'analyse des dossiers
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property TimeDirTotal() As Integer
        Get
            Return TimeDirElapsed + TimeDirRemain
        End Get
    End Property
#End Region
#Region "Thread et sa gestion"
    Private Sub SubScandir()
        Dim lFile As New ClsFifoFile
        Dim lTFile As TFile
        Dim lPath As TPath
        Dim lFiles As New ClsFifoFile

        Dim i As Integer
        Dim j As Integer
        Dim lStr As String
        Try
            If mPath = "" Or mExtInclude = "" Then Exit Try

            ThreadRun = True

            Do While CanThreadRun
                Thread.Sleep(0)
                SyncLock mLstPath
                    lPath = mLstPath.PopData
                End SyncLock

                mActPath = lPath.Path

                If lPath.Path = "" Then Exit Do

                mDirElapsed += 1

                If lPath.IsExist Then
                    If mSRep Then
                        SyncLock mLstPath
                            mLstPath.PushData(lPath.GetSubDirs)
                            mLstPath.Sorted()
                        End SyncLock
                    End If

                    For i = 0 To mExtInc.GetUpperBound(0)
                        lFiles.PushData(lPath.GetFiles(mExtInc(i)))
                    Next
                    Do While lFiles.Count > 0 And CanThreadRun
                        lTFile = lFiles.PopData
                        lStr = lTFile.FileTitle.ToLower
                        For j = 0 To mExtExc.GetUpperBound(0)
                            If lStr Like mExtExc(j) Then
                                lTFile.FileName = ""
                                Exit For
                            End If
                        Next
                        If lTFile.FileName <> "" Then
                            lFile.PushData(lTFile)
                        End If
                    Loop
                    lFile.Sorted()
                    SyncLock mLstFile
                        mLstFile.PushClsFifo(lFile)
                    End SyncLock
                End If
            Loop
        Catch ex As Exception
#If DEBUG Then
            Debug.WriteLine(ex.ToString)
#End If
        Finally
            lFile = Nothing
            If IsNothing(lFile) = False Then lFile.Clear()
            If IsNothing(lFiles) = False Then lFiles.Clear()
            lFile = Nothing
            lFiles = Nothing
            ThreadRun = False
        End Try
    End Sub
    Private Sub StartThread()
        Dim i As Integer
        'Création et nommage du thread
        ThreadScandir = New Thread(AddressOf SubScandir)
        ThreadScandir.Name = "Thread d'analyse des dossiers"
        'Listage des extensions
        mExtInc = mExtInclude.ToLower.Split(";".ToCharArray)
        mExtExc = mExtExclude.ToLower.Split(";".ToCharArray)
        'Listage des dossiers à scanner
        Dim lTmpPath As String()
        lTmpPath = mPath.Split(";".ToCharArray)
        SyncLock mLstPath
            Do While mLstPath.PopData.Path <> "" : Loop
            For i = 0 To lTmpPath.GetUpperBound(0)
                mLstPath.PushData(New TPath(lTmpPath(i)))
            Next
            mLstPath.Sorted()
        End SyncLock
        'Vidage des fichiers récupérés
        SyncLock mLstFile
            Do While mLstFile.PopData.FileName <> "" : Loop
        End SyncLock
        'Mise à zero des compteurs
        mDirElapsed = 0
        mFileElapsed = 0
        'Init du départ
        mTimerDep = Environment.TickCount
        'Démarrage du thread
        CanThreadRun = True
        ThreadScandir.Start()
    End Sub
    Private Sub StopThread()
        'Si le thread tourne encore, on l'arrête
        If ThreadRun Then
            CanThreadRun = False
            If IsNothing(ThreadScandir) = False Then
                If ThreadScandir.Join(10000) = False Then ThreadScandir.Abort()
            End If
        End If
        'On vide les extensions
        ReDim mExtInc(-1)
        ReDim mExtExc(-1)
        'Et on le supprime des objets
        ThreadScandir = Nothing
    End Sub
#End Region
#Region "Gestion de l'analyse publique"
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Démarre l'analyse
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Sub StartAnalyse()
        If ThreadRun Then
            RestartAnalyse()
        Else
            StartThread()
        End If
    End Sub
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Arrête l'analyse
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Sub StopAnalyse()
        StopThread()
        mLstFile.Clear()
        mLstPath.Clear()
    End Sub
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Redémarre l'analyse
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Sub RestartAnalyse()
        StopAnalyse()
        Do While ThreadRun : Thread.Sleep(100) : Loop
        StartAnalyse()
    End Sub
#End Region
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Récupère le fichier en haut de la pile
    ''' </summary>
    ''' <param name="Blocked">Définit si le fonction attend que l'analyse soit finie ou que l'analyse soit finie pour quitter, si false et pas de fichiers en attente, retourne " "</param>
    ''' <param name="DelElement">Définit si l'élément récupéré est retiré de la pile</param>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[nicolas]	25/10/2005	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Function GetNext(Optional ByVal Blocked As Boolean = True, Optional ByVal DelElement As Boolean = True) As TFile
        Dim mObj As TFile
        mObj = mLstFile.PopData(DelElement)
        Do While mObj.FileName = "" And ThreadRun And Blocked
            Thread.Sleep(1000)
            mObj = mLstFile.PopData(DelElement)
        Loop
        If mObj.FileName = "" And ThreadRun Then mObj.FileName = " "
        If DelElement And mObj.FileName.Trim <> "" Then mFileElapsed += 1
        Return mObj
    End Function
    Protected Overrides Sub Finalize()
        StopThread()
        Do While mLstFile.PopData.FileName <> "" : Loop
        Do While mLstPath.PopData.Path <> "" : Loop
        mLstFile = Nothing
        mLstPath = Nothing
        MyBase.Finalize()
    End Sub
End Class
''Scan le prochain répertoire
'Public Sub ScanNextDir()
'    Dim Rep As String
'    Dim FileTitle As String
'    Dim i As Long
'    Dim j As Long
'    Dim Repertoire As DirectoryInfo
'    Dim Files() As FileInfo
'    Dim Dirs() As DirectoryInfo
'    Dim tExtInclude() As String
'    Dim tExtExclude() As String
'    tExtInclude = Split(mExtInclude, ";")
'    tExtExclude = Split(mExtExclude, ";")

'    If LstPath.Count() = 0 Then Exit Sub
'    Rep = LstPath.Item(1)
'    If Right(Rep, 1) <> "\" Then Rep = Rep & "\"
'    LstPath.Remove(1)
'    mDirCount = mDirCount + 1

'    Do While TmpFile.Count() : TmpFile.Remove(1) : Loop
'    Repertoire = New DirectoryInfo(Rep)
'    If Repertoire.Exists = False Then Repertoire = Nothing : Exit Sub
'    Dirs = Repertoire.GetDirectories()
'    Files = Repertoire.GetFiles()
'    Repertoire = Nothing

'    For i = LBound(Dirs) To UBound(Dirs)
'        If mSRep Then LstPath.Add(Dirs(i).FullName)
'        Dirs(i) = Nothing
'    Next
'    Erase Dirs
'    For i = LBound(Files) To UBound(Files)
'        FileTitle = Files(i).Name
'        For j = 0 To UBound(tExtExclude)
'            If (FileTitle Like tExtExclude(j)) Then
'                FileTitle = ""
'                Exit For
'            End If
'        Next
'        If FileTitle <> "" Then
'            For j = 0 To UBound(tExtInclude)
'                If (FileTitle Like tExtInclude(j)) Then
'                    TmpFile.Add(Files(i).FullName)
'                    Exit For
'                End If
'            Next
'        End If
'        Files(i) = Nothing
'    Next
'    Erase Files
'    Sorted(TmpFile)
'    Sorted(LstPath)

'    Do While TmpFile.Count()
'        LstFile.Add(TmpFile.Item(1))
'        TmpFile.Remove(1)
'    Loop
'End Sub
