I have developed a VB.Net code for retrieving data from excel file .I load this data in one form and update it back in excel after making necessary modifications in data. This complete flow works fine but most of the times I have observed that even if I close the form; the already loaded excel process does not get closed properly.
I tried all possible ways to close it but could not be able to resolve the issue. Find below code which I am using for connecting to excel and let me know if any other approach I may need to follow to resolve this issue.
Note: I do not want to kill the excel process as it will kill other instances of the excel
Dim connectionString As String
connectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & ExcelFilePath & ";
Extended Properties=excel 8.0; Persist Security Info=False"
excelSheetConnection = New ADODB.Connection
If excelSheetConnection.State = 1 Then excelSheetConnection.Close()
excelSheetConnection.Open(connectionString)
objRsExcelSheet = New ADODB.Recordset
If objRsExcelSheet.State = 1 Then objRsExcelSheet.Close()
Try
If TestID = "" Then
objRsExcelSheet.Open("Select * from [" & ActiveSheet & "$]", excelSheetConnection, 1, 1)
Else
objRsExcelSheet.Open("Select Test_ID,Test_Description,Expected_Result,Type,UI_Element,Action,Data,Risk from [" & ActiveSheet & "$] WHERE TEST_Id LIKE '" & TestID & ".%'", excelSheetConnection, 1, 1)
End If
getExcelData = objRsExcelSheet
Catch errObj As System.Runtime.InteropServices.COMException
MsgBox(errObj.Message, , errObj.Source)
Return Nothing
End Try
excelSheetConnection = Nothing
objRsExcelSheet = Nothing
You are using the old VB6 COM interfaces. There isn't any evidence in your code snippet of you ever calling Close() on the RecordSet. You can't call Close() on the Connection since you set it to Nothing.
As written, Excel won't exit until the garbage collector runs and the finalizer thread releases the reference counts on the COM interfaces. Which can take a long time if your program doesn't exit or goes dormant after processing the data. You really should consider uplifting this code to use the classes in System.Data.Oledb so you can properly Dispose() everything when you're done with the objects.
The Q&D solution is to force the finalizer thread to run:
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
Run this after you're done using the RecordSet. It isn't otherwise recommended.
Related
So I have a function that I created that will allow the user to gather data from an excel file using a SQL statement. Below is that function:
Public Function query_excel_file(ByVal filepath As String, ByVal sql_statement As String) As DataTable
On Error Resume Next
Dim m_sConn1 As String = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & filepath & ";" & _
"Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"
Dim conn1 As New System.Data.OleDb.OleDbConnection(m_sConn1)
Dim da As New System.Data.OleDb.OleDbDataAdapter(sql_statement, conn1)
Dim dt As DataTable = New DataTable
If da Is Nothing Then
Return Nothing
Exit Function
End If
da.Fill(dt)
conn1.Close()
Return dt
End Function
Now this function works perfectly fine if the sql_statement is a valid sql statement, however if it is not then I get an error like this:
I fully realize that this error comes from a bad sql statement. My question is not how to fix the sql statement but for a way to error handle this so that an error dialog doesn't pop up or crash the program.
What I have tried
I have tried adding the clause shown above:
If da Is Nothing Then
Return Nothing
Exit Function
End If
but the errors still appear. I have tried looking at the properties of my variable da which is a System.Data.OleDb.OleDbDataAdapter to see if there is any property to reference on an error but have had no luck as well.
I have also tried using a simple Try Catch End Try, and On Error Resume Next but the dialog still appears.
That is the "first chance" exception dialog, and it is generated by Visual Studio when an exception occurs, before any other error handling gets to happen.
It's completely separate from your program's error-handling, and it won't happen when running your program outside Visual Studio (since it's generated by Visual Studio).
You can turn off the dialog by unchecking the box. This does not ignore the error, it just stops Visual Studio from pausing when it occurs.
Then, implement proper exception handling, which in this day and age is Try .. Catch (stay far away from On Error Resume Next).
If you put a message box in your Catch block and run your code, you will see that the error is caught properly and not just ignored.
I am having the same issue that many before me have had, and I have found several threads on the issue but my application of their fixes have not yielded any change.
I am reading an excel file and populating a DataGridView with it. Very simply the user can modify it and then save it. The problem, like the other threads, is that the program has such a tight grip on the file I can't overwrite it.
Among the many fixes offered, Garbage Collection was the most frequently mentioned, but my results are unchanged. I have tried disposing of the OLEDB connection, commands, adapters and datasets also without success.
I've been running through several different examples and tutorials to write this program and so if these fixes are required, I've obviously implemented them in the wrong spot.
Here is the reading block which happens on the form load (I've removed all my attempts to fix it to unclutter the code):
Private Sub Inventory_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Using MyConnection = New System.Data.OleDb.OleDbConnection("provider=Microsoft.Jet.OLEDB.4.0;Data Source='c:\AllTrade\Inventory.xlsx';Extended Properties=Excel 8.0;")
Using MyCommand = New System.Data.OleDb.OleDbDataAdapter("select * from [InventorySheet$]", MyConnection)
DtSet = New System.Data.DataSet
MyCommand.Fill(DtSet)
InventoryGridView.DataSource = DtSet.Tables(0)
MyConnection.Close()
End Using
End Using
End Sub
Here is the saving block (This is the code that generates the "File in Use" error):
Private Sub UpdateInventory_Click(sender As Object, e As EventArgs) Handles UpdateInventory.Click
Dim ExcelApp As New Excel.Application()
ExcelApp.Application.Workbooks.Add(Type.Missing)
For i As Integer = 0 To InventoryGridView.Rows.Count - 1
Dim row As DataGridViewRow = InventoryGridView.Rows(i)
For j As Integer = 0 To row.Cells.Count - 1
ExcelApp.Cells(i + 1, j + 1) = row.Cells(j).Value
Next
Next
ExcelApp.ActiveWorkbook.SaveAs("C:\Alltrade\Inventory.xlsx")
ExcelApp.ActiveWorkbook.Saved = True
ExcelApp.Quit()
End Sub
What's the trick to let go of the file? If the GC.Collect() and GC.WaitForPendingFinalizers() are required, where would they go?
I appreciate any help and apologize since I haven't been able to successfully implement the other answers from similar threads.
You need to marshal to release the COM object: ExcelApp...
Private Sub ReleaseObject(ByVal obj As Object)
Try
Dim intRel As Integer = 0
Do
intRel = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
Loop While intRel > 0
Catch ex As Exception
MsgBox("Error releasing object" & ex.ToString)
obj = Nothing
Finally
GC.Collect()
End Try
End Sub
You can call this method like this at the end of your sub.
ReleaseObject(ExcelApp)
You can get more here Excel application not quitting after calling quit
EDIT
I also noticed your connection string is wrong for the newer .xlxs file extention...
Normal ConnectionString : (work for xls files)
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"Excel 8.0;HDR=YES;\""
Office 2007 ConnectionString : (work for xlsx files)
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0;HDR=YES;\""
After much research and frustration, I found a solution.
The connection string of my OleDB connection needs the tidbit added: OLE DB Services = -4 which disables connection pooling. As it happens, the connection is not disposed of (which was the initial hypothesis) when the connection is closed, but instead dumps the connection back into the pool.
Another forum had suggested OLE DB Services = -2 which was supposed to do the same thing, but had no effect in my instance. It's possible it varies between versions but I haven't researched this to conclude that for certain (or someone typoed in one of the forums)
No further code was required in my program to dump the OleDB connection's hold on the file. MyConnection = Nothing and the line that drains the pool followed by a GC.Collect were also recommended but did not resolve my symptoms.
Thanks everyone for the input
I have a program that is going through the subfolders of a folder and running a stored SQL command on each. I am in the process of writing another module that will automatically check the files before being inputted, but I don't like having my company's test data hinging on a program without any way to know if something errored out. Here is the code I currently have:
Sub openConnection()
Set varConnection = New ADODB.Connection
Set varCommand = New ADODB.Command
varConnection.ConnectionString = "PROVIDER=SQLOLEDB;DATA SOURCE=192.168.1.186,1433;INITIAL CATALOG=Test1; INTEGRATED SECURITY=sspi;"
varConnection.Open
varCommand.CommandText = "importFile"
varCommand.CommandType = adCmdStoredProc
varCommand.ActiveConnection = varConnection
Set varParameter = varCommand.CreateParameter("filePath", adChar, adParamInput, Len(varFolderPath), varFolderPath)
varCommand.Parameters.Append varParameter
varCommand.Execute
varConnection.Close
End Sub
On occasion, the command will error out, causing the data to not be uploaded. How can I catch these SQL errors through Excel VBA in order to handle them appropriately without it being treated as though it had gone through like a normal file?
Note: I don't mind rewriting code- this is all still in alpha.
I guess this might be what you are looking for...
MS Access VBA trapping SQL Server Connection Error
Sub OpenConnection()
On Error GoTo ErrorHandler
// Do your stuff here...
ErrorHandler:
Debug.Print Err.Number & " " & Err.Description
' Or else you could log the error in a file, or even display a message box, whatever...'
End Sub
From the answer provided by fionnuala to the above-linked question.
I have a database where data goes through multiple steps, and user can report and 'solve' those errors in the database.
Once an error is added in my tbl_errors, they go on and solve it. Once they have solved the error irl, the 'solve' the error in the database as well, to keep track of time and such.
This all works like a charm, when adding errors I have never encountered any problems. And at first sight, 'solving' the problems goes flawless either. The problem however, is that once I start 'solving' a lot of errors in a row, my code suddenly stops working.
It does not freeze or throw back any errors, and when I step through the code using my breakpoints and f8, all the variables seem to be correct also. Everything goes on just as always, except it just does not do anything anymore. This is only applicable to that specific error. When I add new errors, and try to 'solve' them. It works just as usual.
Important notes:
This ONLY happens when I start fast clicking on my solve button, thus calling the functions real fast behind eachother.
It only freezes for a specific errors. (Can be multiple) All other errors can be solved as usual, indicating that the code is still functioning.
I have stepped through the whole code, while checking all the keys and variables and every variable is correct.
Even though my code goes through the recordset, it does not update anything?
Below is a piece of my relationships screen to give a better understanding of the table structure, as well as the specific parts of code.
Calling the code in the OnClick event:
Private Sub solve_Click()
SolveError getorderid(gvStepDelivery), get_errorID(gvCategory, get_stepsID(gvStepNr))
Me.qry_errors_subform.Requery
Me.Refresh
End Sub
The self-written function SolveError:
Public Function SolveError(Current_order_ID As Long, Category_ID As Long)
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("tbl_errors", dbOpenDynaset)
With rs
.FindFirst "[Error_ID] = " & DLookup("Error_ID", "tbl_errors", "[Current_orders_ID] = " & Current_order_ID & " AND [Category_ID] = " & Category_ID)
.Edit
![Solved_By] = get_user
![Solved_Date] = Date
![Solved_Time] = Time
.update
End With
rs.Close
Set rs = Nothing
End Function
There are other parts of code involved (See the SolveError's parameters), but I don't think they will add some usefull info, since they are just returning the correct values. (They are correct!!)
Ok, it seems I have already found my answer. Since I was probably calling a new iteration before the previous one had completely finished, it simply stopped working. Adding the DoEvents function at the end my SolveErrors function solved it. I have yet to experience the problem again.
Public Function SolveError(Current_order_ID As Long, Category_ID As Long)
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("tbl_errors", dbOpenDynaset)
With rs
.FindFirst "[Error_ID] = " & DLookup("Error_ID", "tbl_errors", "[Current_orders_ID] = " & Current_order_ID & " AND [Category_ID] = " & Category_ID)
.Edit
![Solved_By] = get_user
![Solved_Date] = Date
![Solved_Time] = Time
.update
End With
rs.Close
Set rs = Nothing
DoEvents 'This one did the trick!!
End Function
Info on the DoEvents method can be found here: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
and here: http://office.microsoft.com/en-001/access-help/doevents-function-HA001228827.aspx
I need some help with my program. I want to rewrite the data on my .txt file but an error occurs:
The process cannot access the file 'C:\Users\AARVIII\Documents\Visual Studio 2010\Projects\PROJECT\WindowsApplication3\bin\Debug\ORDERS\aa.txt' because it is being used by another process.
Here is the code:
Sub WRITEDATA()
Dim write As New System.IO.StreamWriter("ORDERS\" & TBFNAME.Text + "" + TBLNAME.Text & ".txt", False)
write.WriteLine(TBFNAME.Text)
write.WriteLine(TBLNAME.Text)
write.WriteLine(TBEADD.Text)
write.WriteLine(TBEADD2.Text)
write.WriteLine(TBADDRESS.Text)
write.WriteLine(TBCONTACT.Text)
write.close()
End Sub
I used a StreamReader to get the data which had already been put in that text file. Please help me figure out how to kill that process so that I can rewrite my data.
It is very possible that your app (on another thread?) is the culprit. First, to make sure you release the resource, make sure to wrap your code in a using block:
Using Dim write As New System.IO.StreamWriter("ORDERS\" & TBFNAME.Text + "" + TBLNAME.Text & ".txt", False)
write.WriteLine(TBFNAME.Text)
write.WriteLine(TBLNAME.Text)
write.WriteLine(TBEADD.Text)
write.WriteLine(TBEADD2.Text)
write.WriteLine(TBADDRESS.Text)
write.WriteLine(TBCONTACT.Text)
End Using
Additionally, you may want to see this thread: .NET Asynchronous stream read/write