0

I am trying to develop a simple bulk copy program that polls a given folder for files at specified intervals.

The code looks perfect. My output gives a great recursive list of files, but when I go to actually move them according to the list, every file I scanned is locked. I have tried garbage collecting, disposing, exiting subs at certain points, debugging at certain points...

Please take a look at my code. When MoveFile is called, everything is locked.

Imports System
Imports System.IO

Public Structure FileStructure

    Dim Enumerator As Integer
    Dim SPath As String
    Dim DPath As String
    Dim Name As String
    Dim FileSize As Long
    Dim IsFile As Short
    Dim SourceFullName As String
    Dim DestFullName As String

End Structure

Public Class StagingDriveCoordinator

    Dim FilesScanned As FileStructure()
    Dim ScanCount As Integer = -1

    Private Sub ScanAll(ByVal sourcePath As String, ByVal destinationPath As String)

        Dim sourceDirectoryInfo As New System.IO.DirectoryInfo(sourcePath)

        ' ---------------------- Create the appropriate directories --------------------------------
        ' Create source path
        If Not System.IO.Directory.Exists(sourcePath) Then
            System.IO.Directory.CreateDirectory(sourcePath)
        End If

        ' If the destination folder doesn't exist then create it
        If Not System.IO.Directory.Exists(destinationPath) Then
            System.IO.Directory.CreateDirectory(destinationPath)
        End If
        ' ------------------------------------------------------------------------------------------

        Dim AddSize As Integer = 0

        'Figure out how much to resize the array this iteration of ScanAll
        If FilesScanned IsNot Nothing Then
            AddSize = FilesScanned.Count + sourceDirectoryInfo.GetFileSystemInfos.Length
        Else
            AddSize = sourceDirectoryInfo.GetFileSystemInfos.Length
        End If

        'Resize the array
        Array.Resize(FilesScanned, AddSize)

        For Each FileSystemInfo In sourceDirectoryInfo.GetFileSystemInfos

            ScanCount += 1

            FilesScanned(ScanCount).Enumerator = ScanCount
            FilesScanned(ScanCount).SPath = sourcePath.ToString
            FilesScanned(ScanCount).DPath = destinationPath.ToString
            FilesScanned(ScanCount).Name = FileSystemInfo.Name.ToString

            If TypeOf FileSystemInfo Is System.IO.FileInfo Then
                FilesScanned(ScanCount).FileSize = DirectCast(FileSystemInfo, FileInfo).Length
                FilesScanned(ScanCount).IsFile = 1
            Else
                FilesScanned(ScanCount).FileSize = 9223372036854775807
                FilesScanned(ScanCount).IsFile = 0
            End If

            FilesScanned(ScanCount).SourceFullName = System.IO.Path.Combine(sourcePath, FileSystemInfo.Name).ToString
            FilesScanned(ScanCount).DestFullName = System.IO.Path.Combine(sourcePath, FileSystemInfo.Name).ToString

            txtOutput.Text += FilesScanned(ScanCount).Enumerator & vbTab & FilesScanned(ScanCount).SourceFullName & vbNewLine

            If FilesScanned(ScanCount).IsFile = 0 Then
                'Debug
                txtOutput.Text += vbNewLine & "Recursively scanning subfolder " + FilesScanned(ScanCount).Name & "..." + vbNewLine + vbNewLine

                'Recursively call the main scanner.
                ScanAll(FilesScanned(ScanCount).SourceFullName, FilesScanned(ScanCount).DestFullName)

            End If

        Next

    End Sub

    Private Sub MoveFile(ByVal Source, ByVal Destination, ByVal filesize)

        Try

            File.Copy(Source, Destination, True)
            txtOutput.Text += "Moving file... Source: " & Source & ". Filesize: " & filesize.ToString & vbNewLine
            txtOutput.Text += "Destination: " & Destination & vbNewLine & vbNewLine
            File.Delete(Source)

        Catch ex As Exception

            txtOutput.Text += "File " & Source & " is locked." & vbNewLine

        End Try

    End Sub

    Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click

        Select Case cmbPollingFrequency.SelectedItem

            Case "5 Seconds"

                Timer1.Interval = 5000

            Case "30 Seconds"

                Timer1.Interval = 30000

            Case "1 Minute"

                Timer1.Interval = 60000

            Case "5 Minutes"

                Timer1.Interval = 300000

            Case "15 Minutes"

                Timer1.Interval = 900000

            Case "30 Minutes"

                Timer1.Interval = 1800000

            Case "1 Hour"

                Timer1.Interval = 3600000

            Case Else

                MsgBox("You must select an interval.")

        End Select

        Timer1.Start()

    End Sub

    Private Sub TimerTick(sender As Object, e As EventArgs) Handles Timer1.Tick

        Timer1.Stop()

        txtOutput.Text += DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") & vbNewLine
        txtOutput.Text += "Scanning Filesystem..." + vbNewLine + vbNewLine

        'Scan the file system.
        ScanAll(cmbStaging.Text, cmbBackup.Text)


        txtOutput.Text += vbNewLine
        txtOutput.Text += DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") & vbNewLine
        txtOutput.Text += " ------------- Scan cycle completed. --------------- " & vbNewLine & vbNewLine

        txtOutput.Text += "Sorting by filesize..." & vbNewLine & vbNewLine

        ' Sort the file list by size.
        FilesScanned = FilesScanned.OrderBy(Function(x) x.FileSize).ToArray


        txtOutput.Text += "Done." & vbNewLine & vbNewLine
        txtOutput.Text += "Moving smallest files first..." & vbNewLine & vbNewLine



        For Each FileElement In FilesScanned

            If FileElement.IsFile > 0 Then

                'file.FileSize only needed to pass size to text output
                MoveFile(FileElement.SourceFullName, FileElement.DestFullName, FileElement.FileSize)


            End If

        Next

        FilesScanned = Nothing
        ScanCount = -1

        Timer1.Start()

    End Sub

    Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click

        Timer1.Stop()

    End Sub

End Class
tinstaafl
  • 6,908
  • 2
  • 15
  • 22
Aaron Rheams
  • 1
  • 1
  • 4
  • You dont really know what the problem is because you are not examining the exception. The "locked" error message is your response to everything – Ňɏssa Pøngjǣrdenlarp Feb 20 '17 at 22:44
  • I think that you can't move the file because the list iterator has it. Typically you can't modify a list while you are looping through it. – leetibbett Feb 20 '17 at 22:45
  • @leetibbett : I'm afraid that's not the case. You're right about that you cannot modify a collection type when in a `For Each` loop, but he never modifies the array itself so there's no problem with what he is currently doing in the loop. – Visual Vincent Feb 21 '17 at 00:55
  • Yes, I'm totally out of the subroutine that polls the IO system. I know this because I get output just before I call MoveFiles. I'm storing the IO results in an instance of the structure I built to hold the files info. It's all strings and numbers. Nothing should be accessing the files at that point. – Aaron Rheams Feb 21 '17 at 03:52
  • @Plutonix : I see how it might appear that I'm throwing a locked exception for every conceivable issue, but I programmed that one based on the error I was receiving without the try-catch block. VB gives me an unhandled exception of locked files. However, this exception occurred before a lot of modifications, so it may be worth revisiting just to verify. – Aaron Rheams Feb 21 '17 at 04:08
  • I am very heavily suspecting System.IO.DirectoryInfo as the culprit here. I have read a million times that this does not lock files, and I also know that it cannot be disposed. Once the ScanAll subroutine has cleared all its iterations, I was thinking surely those resources are released. There really seems to be no reason for the locking. I did also verify that no external causes are doing it. I can move and copy the files in Windows as long as the program isn't accessing them. – Aaron Rheams Feb 21 '17 at 04:14
  • See if the hack involving FileIOPermission in [this post](http://stackoverflow.com/q/603444/2592875) helps your situation. – TnTinMn Feb 21 '17 at 05:06
  • @TnTinMn : No luck. It was worth a shot, thanks. I did go back and verify that removing the try-catch block still results in a lock error. – Aaron Rheams Feb 21 '17 at 14:47
  • I moved everything in the ScanAll() sub to an entirely different class, changed it to a function that returns FilesScanned, stored the value in a private array within the main class, made the scanner class disposable, disposed of it before attempting to move files, and it still fails. – Aaron Rheams Feb 21 '17 at 16:27

1 Answers1

0

I found the problem. The IO system was NOT locking the file. I was trying to copy it to the SAME directory...

FilesScanned(ScanCount).SourceFullName = System.IO.Path.Combine(sourcePath, FileSystemInfo.Name).ToString

FilesScanned(ScanCount).DestFullName = System.IO.Path.Combine(sourcePath, FileSystemInfo.Name).ToString

This should have been:

FilesScanned(ScanCount).SourceFullName = System.IO.Path.Combine(sourcePath, FileSystemInfo.Name).ToString

FilesScanned(ScanCount).DestFullName = System.IO.Path.Combine(destinationPath, FileSystemInfo.Name).ToString

Once I changed it, everything worked perfectly.

Aaron Rheams
  • 1
  • 1
  • 4