0

I created an app that does the following:

  1. Opens an excel spreadsheet and show a hidden sheet
  2. Copy the data in this sheet to a temporary spreadsheet
  3. Run some error checks on the data that's been pasted
  4. Create a unique code in the first column based on customer no. date & time
  5. Save the spreadsheet as a new file
  6. Clear up to be ready for the next spreadsheet

The problem I am having is at step 6 of clearing up and releasing any excel objects in memory that can hold up moving to the next spreadsheet and so on. The current issue I have is that an Excel object remains open and locks the tempfile.xlsx that is created which will then randomly generates the error "The file 'C:\Temp\CustOrders\Input\TempFile.xlsx' already exists." I say randomly because I can run 10 or more files through it without an issue. I could run them all again and it will produce the error after the 1st, 2nd, 3rd or later file. I cannot blame any one file for causing this.

How do I effectively close out all Excel objects ready for the next file to be processed? I have so far tried different ways to do this including trying to kill the process but this seems like a dirty sledgehammer approach.

Here's the code:

Imports Microsoft.Office.Interop.Excel
Imports Microsoft.Office.Interop
Imports System.Runtime.InteropServices
Imports System.IO

Public Class Form1
    Dim xlApp As Excel.Application
    Dim xlNewApp As Excel.Application
    Dim xlWorkBook As Excel.Workbook
    Dim xlWorkSheet As Excel.Worksheet
    Dim range As Excel.Range
    Dim rCnt As Integer
    Dim cCnt As Integer
    Dim Obj As Object
    Dim TempFile() As String
    Dim TempFiledir As String
    Dim filename As String
    Dim xlNewWorkBook As Excel.Workbook
    Dim xlNewWorkSheet As Excel.Worksheet
    Dim misValue As Object = System.Reflection.Missing.Value
    Dim Cust As Object
    Dim pfile As String
    Dim NoProcessed As Integer = 0
    Dim NoFailed As Integer = 0
    Dim filecount As Integer = 0
    Dim fileremaining As Integer = 0
    Dim custFailed As Integer = 0
    Dim files() As String = Directory.GetFiles("C:\Temp\CustOrders\Uploaded")
    Dim di As New DirectoryInfo("C:\Temp\CustOrders\Uploaded")

    Private Sub releaseObject(ByVal obj As Object)
        Try
            Marshal.ReleaseComObject(obj)
            obj = Nothing
        Catch ex As Exception
            obj = Nothing
        Finally
            GC.Collect()
        End Try
    End Sub

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        lblProcessingFile.Visible = False
        CountFiles()
        lblFileCount.Text = "Files to be processed: " & filecount
    End Sub

    Function CountFiles()
        Dim files() As String = Directory.GetFiles("C:\Temp\CustOrders\Uploaded")
        Dim di As New DirectoryInfo("C:\Temp\CustOrders\Uploaded")
        If files.Count > 0 Then
            filecount = di.GetFiles("*.xlsx").Count()
        Else
            filecount = 0
            lblFileCount.Text = "Files to be processed: " & filecount
        End If
    End Function

    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles BTN_gencsv.Click
        TempFiledir = ("C:\Temp\CustOrders\Input\TempFile.xlsx")
        If My.Computer.FileSystem.FileExists(TempFiledir) Then
            My.Computer.FileSystem.DeleteFile(TempFiledir)
        End If
        GetFiles()
    End Sub

    Sub GetFiles()

        '1. Look in the UPLOADED folder for new files
        
        Dim files() As String = Directory.GetFiles("C:\Temp\CustOrders\Uploaded")
        Dim di As New DirectoryInfo("C:\Temp\CustOrders\Uploaded")
        If files.Count > 0 Then
            Dim arrayfi As FileInfo() = di.GetFiles("*.xlsx")
            Dim fi As FileInfo
            For Each fi In arrayfi
                filename = fi.Name
                Start(filename)
            Next
        Else
            MsgBox("No files available in directory")
        End If
    End Sub

    Sub Start(filename)

        With BTN_gencsv
            .BackColor = Color.Red
            .ForeColor = Color.White
            .Text = "Please wait..."
        End With

        '2. Get the file that has been uploaded by the customer, copy and rename as TempFile

        lblProcessingFile.Visible = True
        lblProcessingFile.Text = "Processing file: " & filename
        IO.File.Copy("C:\Temp\CustOrders\Uploaded\" & filename, _
                                                            "C:\Temp\CustOrders\Input\TempFile.xlsx")
        xlApp = New Excel.Application
        xlNewApp = New Excel.Application
        xlWorkBook = xlApp.Workbooks.Open("C:\Temp\CustOrders\Input\TempFile.xlsx")
        xlWorkSheet = xlWorkBook.Worksheets(1)
        xlWorkSheet = xlWorkBook.Worksheets("CSV")
        xlWorkSheet.Visible = XlSheetVisibility.xlSheetVisible
        xlWorkSheet.Unprotect("opencsv")

        '3. Copy rows from the CSV worksheet including headers

        xlWorkSheet.Range("A1:H100").Copy()

        '4. Create new Excel workbook and worksheet so it can have have all rows pasted in
        '   Then perform all prep work

        xlNewWorkBook = xlNewApp.Workbooks.Add(misValue)
        xlNewWorkSheet = xlNewWorkBook.Worksheets(1)
        xlNewWorkSheet.Select()

        ' Paste the rows into the new worksheet
        On Error Resume Next
        xlNewWorkSheet.PasteSpecial(Excel.XlPasteType.xlPasteValues)
        xlApp.CutCopyMode = False

        ' Get current date/time
        Dim dt As DateTime = DateTime.Now
        Dim dt2 As String = dt
        dt2 = dt2.Replace("/", "").Replace(" ", "_").Replace(":", "")

        ' Select customer number from worksheet
        Dim xRng As Excel.Range = CType(xlNewWorkSheet.Cells(2, 5), Excel.Range)
        Cust = xRng.Value().ToString()
        ' If the customer is not found in the spreadsheet lookup it generates "-2146826246" as a value
        ' This saves the cell as "Not found" to make it look friendly
        If Cust.Equals("-2146826246") Then
            custFailed += 1
            Cust = "Cust_Not_Found_" & custFailed
            failedfiles()
        Else
            CustNo()

        End If
        releaseObject(xRng)

        ' This now passes to two error checking subs

    End Sub

    Sub completeform()

        '5. Generate a unique value for Netsuite based on the customer number and current date time
        
        Dim Row As Range
        Dim Index As Long
        Dim Count As Long
        For Index = xlNewWorkSheet.UsedRange.Rows.Count To 1 Step -1
            Row = xlNewWorkSheet.UsedRange.Rows(Index)
            Count = 0
            On Error Resume Next
            Count = Row.SpecialCells(XlCellType.xlCellTypeBlanks).Count
            If Count = Row.Cells.Count Then Row.Delete(Excel.XlDirection.xlUp)
        Next

        Dim dt As DateTime = DateTime.Now
        Dim dt2 As String = dt
        dt2 = dt2.Replace("/", "").Replace(" ", "_").Replace(":", "")
        pfile = (Cust + "_" + dt2)
        Dim rw As Integer = 1
        Do Until xlNewWorkSheet.Cells(rw, 1).Value Is Nothing
            rw += 1
        Loop

        Dim last As String = rw - 1
        With xlNewWorkSheet.Range("A1:A100")
            .Range(.Cells(2, 1), .Cells(last, 1)).Value = (Cust + "_" + dt2)
        End With

        '6. Save the workbook with a unique name based on customer number and date/time

        xlWorkBook.Saved = True
        xlNewWorkBook.SaveAs("C:\Temp\CustOrders\Output\Test_" + pfile + ".csv", Excel.XlFileFormat.xlCSV, misValue, misValue, misValue, misValue, _
                 Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue)

        '7. Close and release all Excel worksheets and workbooks so they dont remain in memory

        xlNewWorkBook.Close(True, misValue, misValue)
        xlWorkBook.Saved = True
        xlNewWorkBook.Saved = True
        releaseObject(xlNewWorkSheet)
        releaseObject(xlNewWorkBook)
        releaseObject(xlNewApp)

        xlWorkBook.Close(False)
        xlApp.Quit()
        releaseObject(range)
        releaseObject(xlWorkSheet)
        releaseObject(xlWorkBook)
        releaseObject(xlApp)
        xlNewApp.Quit()

        System.Threading.Thread.Sleep(2000)

        '8. Move the processed workbook to the Processed folder ready for a new workbook

        IO.File.Move("C:\Temp\CustOrders\Input\TempFile.xlsx", _
                                                "C:\Temp\CustOrders\Processed\Processedfile_" + pfile + ".xlsx")

        '9. Move the spreadsheet from Uploaded to OldUploaded ready for a new file

        IO.File.Move("C:\Temp\CustOrders\Uploaded\" + filename, _
                                                "C:\Temp\CustOrders\OldUploaded\Uploaded_" + filename)
        
        NoProcessed += 1
        lblProcessedCount.Text = "No. Processed..." & NoProcessed
        CountFiles()
        lblFileCount.Text = "Files to be processed: " & filecount
        With BTN_gencsv
            .BackColor = SystemColors.Control
            .ForeColor = SystemColors.ControlText
            .Text = "Generate CSV"
        End With
        lblProcessingFile.Visible = False
    End Sub

    Sub CustNo()
        Dim c As Range
        With xlNewWorkSheet.Range("A1:A100")
            c = .Find("#N/A", LookIn:=XlFindLookIn.xlValues)
            If Not c Is Nothing Then
                custFailed += 1
                Cust = "Cust_Not_Found_" & custFailed
                MsgBox(Cust)
                failedfiles()
            Else
                quantityBlanks()
            End If
        End With
    End Sub

    Sub quantityBlanks()
        Dim rw As Integer = 1
        Do Until xlNewWorkSheet.Cells(rw, 1).Value Is Nothing
            rw += 1
        Loop
        Dim last As String = rw - 1
        Dim rng As Excel.Range
        Dim TotalBlanks As Long
        TotalBlanks = 0
        rng = xlNewWorkSheet.Range(xlNewWorkSheet.Cells(2, 8), xlNewWorkSheet.Cells(last, 8))
        On Error Resume Next
        TotalBlanks = rng.SpecialCells(XlCellType.xlCellTypeBlanks).Count
        If TotalBlanks > 0 Then
            Cust = "Quantity_error_"
            failedfiles()

        Else
            referrors()
        End If
        rng = Nothing
    End Sub

    Sub referrors()
        Dim c As Range
        With xlNewWorkSheet.Range("A1:A100")
            c = .Find("#REF!", LookIn:=XlFindLookIn.xlValues)
            If Not c Is Nothing Then
                Cust = "~REF!_errors_"
                failedfiles()
            Else
                completeform()
            End If
        End With
    End Sub

    Sub failedfiles()
        Dim dt As DateTime = DateTime.Now
        Dim dt2 As String = dt
        dt2 = dt2.Replace("/", "").Replace(" ", "_").Replace(":", "")
        pfile = (Cust + "_" + dt2)
        xlWorkBook.Close(False)
        xlNewWorkBook.Close(False)
        xlNewApp.Quit()
        xlApp.Quit()
        releaseObject(xlApp)
        releaseObject(xlWorkBook)
        releaseObject(xlWorkSheet)
        releaseObject(xlNewApp)
        releaseObject(xlNewWorkBook)
        releaseObject(xlNewWorkSheet)
        'IO.File.Delete("C:\Temp\CustOrders\Input\Newfile.xlsx")
        IO.File.Move("C:\Temp\CustOrders\Input\TempFile.xlsx", _
                                                "C:\Temp\CustOrders\Processed\ProcessedFailedfile_" + pfile + ".xlsx")

        IO.File.Move("C:\Temp\CustOrders\Uploaded\" + filename, _
                                        "C:\Temp\CustOrders\Failed\FailedFile_" + pfile + ".xlsx")

        With BTN_gencsv
            .BackColor = SystemColors.Control
            .ForeColor = SystemColors.ControlText
            .Text = "Generate CSV"
        End With
        NoFailed += 1
        lblFailed.ForeColor = Color.Red
        lblFailed.Text = "No. Failed..." & NoFailed
        CountFiles()
        lblFileCount.Text = "Files to be processed: " & filecount
        lblProcessingFile.Visible = False
    End Sub

    Private Sub PictureBox1_Click(sender As System.Object, e As System.EventArgs) Handles PictureBox1.Click
        CountFiles()
        lblFileCount.Text = "Files to be processed: " & filecount
    End Sub
End Class
  • 1
    Sounds like the "clear up" step isn't being performed accurately.. You'd perhaps be better off using a library like EPPlus rather than opening a copy of Excel in in the background and telling it to manipulate the files. With EPPlus it creates excel files directly, no need for excel to be installed – Caius Jard Mar 04 '21 at 11:20
  • Does this answer your question? [The proper way to dispose Excel com object using VB.NET?](https://stackoverflow.com/questions/10309365/the-proper-way-to-dispose-excel-com-object-using-vb-net) In particular, the [answer by user Govert](https://stackoverflow.com/a/38111107/1115360). – Andrew Morton Mar 04 '21 at 12:54
  • Hi Andrew. It did and it didn't. I tried using the methods mentioned in that article as well as others but it still occasionally fails to remove the Excel objects after each loop randomly – Simon Foxwell Mar 04 '21 at 13:56
  • Hi Caius. That does seem like a better option and I will give it a try. It just bothers me that I'll have to spend more time getting this to work with a different method – Simon Foxwell Mar 04 '21 at 13:57
  • EPPlus is deliberately designed to be a pretty close mimic of how Excel automation works; it should be an easy transition – Caius Jard Mar 04 '21 at 18:50

0 Answers0