How to properly quit Excel - vb.net

I am trying to print a receipt from Excel but unable to do so. I have tried release in reverse but I seems can't find what is missing. Kindly help! Thank you!
This is what i have done so far :
Imports Excel = Microsoft.Office.Interop.Excel
Public Class Form1
#Region "dim"
Dim exeDir As New IO.FileInfo(Reflection.Assembly.GetExecutingAssembly.FullName)
Dim xlPath = IO.Path.Combine(exeDir.DirectoryName, "SampleReceipt.xls")
Dim app As Excel.Application = Nothing
Dim books As Excel.Workbooks = Nothing
Dim book As Excel.Workbook = Nothing
Dim sheets As Excel.Sheets = Nothing
Dim sheet As Excel.Worksheet = Nothing
Dim cell As Excel.Range = Nothing
#End Region
Private Sub NAR(ByVal o As Object)
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
Catch ex As Exception
o = Nothing
Finally
End Try
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
app = New Excel.Application()
books = app.Workbooks
book = books.Open(xlPath)
sheets = book.Sheets
sheet = book.Sheets(1)
cell = sheet.Range("A1")
cell.Value = "Lorem Ipsum"
book.SaveAs("C:\Temp\ExcelBook.xls")
book.Close()
app.Quit()
Finally
NAR(cell)
NAR(sheet)
NAR(sheets)
NAR(book)
NAR(books)
NAR(app)
End Try
End Sub
End Class

This is a bit of a guess, but I don't think your NAR method is doing the job perfectly well.
You have this:
Private Sub NAR(ByVal o As Object)
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
Catch ex As Exception
o = Nothing
Finally
End Try
End Sub
Now, if you have an Exception raised by ReleaseComObject then you're just swallowing it - I would think that you want to see what the exception is. You really should only handle exceptions that you can recover from. Handling the top-level Exception really is an anti-pattern. So if ReleaseComObject is raising an exception then find out what it is and deal with that specifically.
Next, if you do have an exception you are then trying to set the reference to Nothing, but you're only setting a copy of the reference to Nothing. You're passing in o using ByVal. The original reference is untouched. You want to pass it in ByRef. Try that.
Also, It might be required that you set the reference to Nothing after releasing the component - so move it out of the Catch.
And finally, the help docs on ReleaseComObject says this:
If you want to call this method to ensure that a COM component is released at a determined time, consider using the FinalReleaseComObject method instead. FinalReleaseComObject will release the underlying COM component regardless of how many times it has re-entered the CLR. The internal reference count of the RCW is incremented by one every time the COM component re-enters the CLR. Therefore, you could call the ReleaseComObject method in a loop until the value returned is zero. This achieves the same result as the FinalReleaseComObject method.
I'd try those three things.
Try this code:
Private Sub NAR(ByRef o As Object)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(o)
o = Nothing
End Sub
And, only put in exception handling if you have a specific exception to handle, but keep the o = Nothing out of the handling code.

Related

Getting error in Access database connection

I'm trying to connect to my database and I'm getting this error message (ex):
I've another function which opens DB and load some data, it works fine without any error... But when I try to use this, it returns me this error, the line 175 is "cn.Open()".
My connection string and Local_DB is both same as other functions which are working without errors.
Private Sub AtualizaClientes(ID As Integer)
' Local da DataBase
Dim Local_DB As String
Local_DB = "C:\Users\Heitor BASAM\Desktop\Sistema\DataBase_Sistema.accdb"
Try
Dim cn As New OleDb.OleDbConnection
cn.ConnectionString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={Local_DB}"
cn.Open()
cn.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Glad you fixes your problem! Just a few bits to tidy up the code.
Always use Using...End Using with database objects that expose a Dispose method. They may be using unmanaged objects and their dispose methods must run to release these resources. The End Using takes care of this for you even if there is an error.
You can let the error bubble up to the calling code, user interface code. Wrap the call to AtualizaClientes in a Try...Catch...End Try
Private Sub AtualizaClientes(ID As Integer)
Dim Local_DB = "C:\Users\Heitor BASAM\Desktop\Sistema\DataBase_Sistema.accdb"
Using cn As New OleDb.OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={Local_DB}")
cn.Open()
End Using
End Sub
EDIT
You would add the Try...End Try to the UI code. Example...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim id As Integer = 7
Try
AtualizaClientes(id)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
I figured out the problem! I've a function when a especify textbox is changed, it calls this "AtualizaClientes" function. The problem is, when you load the form, it runs a "TextChanged" event in every textbox and trys to run the database before loading it. This was causing the error message.
To solve this problem, I made a boolean variable which returns true after loading the database. So, I changed my textchanged event to run only if this variable is true.

I am trying to Open a Form Using data passed from a linklabel

I created a link label with the lnk.name = ItemCard
I have a Form called frmItemCard
When you click the linklabel I want it to open the form programmatically.
I am doing this because I am generating the linklabels from a list in an SQL table.
The Code I am using to Open the form is:
Private Sub lnk_LinkClicked(ByVal sender As System.Object, ByVal e As LinkLabelLinkClickedEventArgs)
Dim lnk As LinkLabel = CType(sender, LinkLabel)
Try
Dim vForm As String
vForm = "frm" + lnk.Name
Call showFormDynamically(vForm)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Public Sub showFormDynamically(frmForm As String)
Dim obj As Object = Activator.CreateInstance(Type.GetType(frmForm))
obj.MdiParent = ParentForm
obj.Dock = DockStyle.Fill
obj.show()
End Sub
The Error I get is: Value cannot be Null. Parameter name: type
Any Ideas what I am doing wrong?
The thing is, that Type.GetType() will return null if it cannot resolve the type at all.
So you have no type to create an instance from, now if you call Activator.CreateInstance(null) the exception is thrown, because that method does not allow the argument passed in to be null.
This has nothing to do with VB.NET at all, that's just how the .NET framework operates. Anyways, try it: call Type.GetType("anything") it will just return null.
So I'd say your type name is just wrong. It seems that you just use something like "frmItemCard" but you need to pass the full qualified one which could probably look like: "AnyApplication.AnyNamespace.frmItemCard".
There are several ways to find your type name. You might start here:
How do I find the fully qualified name of an assembly?.

Null reference exception when trying to open a workbook, vb.net

I've got an openfiledialog reading a spreadsheet file name from a textbox, then performing some formatting and spitting out a text file. The code works fine one time through; my next task is to get it so that I can open successive spreadsheets (one at a time) without closing the program.
When I try to open a second excel file, I get a null reference exception (object ref not set to an instance of an object) on the line where I'm opening the workbook.
Public Class Form1
Dim xlApp As New Microsoft.Office.Interop.Excel.Application
Dim xlWorkbook, xlWorkbook2 As Microsoft.Office.Interop.Excel.Workbook
Dim xlWsheet, xlWsheet2 As Microsoft.Office.Interop.Excel.Worksheet
Dim strm As System.IO.Stream
Dim FormFile As String = "C:\nitemp.tmp\QuantData.xls"
Private Sub Open_Click(sender As Object, e As EventArgs) Handles Open.Click
'Open button code'
OpenFileDialog1.Title = "Select a File"
OpenFileDialog1.InitialDirectory = directory.Text 'uppermost text box, change to open a different default directory with OPEN button'
OpenFileDialog1.RestoreDirectory = True
OpenFileDialog1.ShowDialog()
End Sub
Private Sub OpenFileDialog1_FileOk(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles OpenFileDialog1.FileOk
Dim lrow As Integer = 0
Try
strm = OpenFileDialog1.OpenFile()
TextBox1.Text = OpenFileDialog1.FileName.ToString()
xlWorkbook = xlApp.Workbooks.Open(TextBox1.Text) 'opens excel file'
xlApp.Visible = False
strm.Close()
I see two possible null references here: 1) the .ToString on the previous line may be empty. WI run the code, the textbox isn't being populated by the correct file path. Only after the error box pops up and I hit 'continue' does the textbox show the correct path.
2) an issue with the platform, 64x vs 32x? This came up in a search, so I tried "New Excel.App" and "New Excel.Workbook", then changed to x86 platform, yet this got me the infamous COM exception 80040154, which makes me think this is not really the issue, but I'm still pretty new to coding..
Can someone find the null?
First things first, you don't need to call OpenFileDialog1.OpenFile(). In fact you don't need to get the filestream at all as you aren't manipulating the file directly (only Excel is).
Secondly, you need to retrieve and dispose of the xlApp.Workbooks collection independently, otherwise you are going to leak some COM wrappers. The null reference exception could be from either the Workbooks collection being null, or the open filename being null. Some error handling will solve your problem.
...
Dim xlWorkbooks as Excel.Workbooks
Dim xlWorkbookOpened as Excel.Workbook
Try
TextBox1.Text = OpenFileDialog1.FileName.ToString()
If (TextBox1.Text IsNot Nothing) Then
xlWorkbooks = xlApp.Workbooks
If (xlWorkbooks IsNot Nothing) Then
xlWorkbookOpened = xlWorkbooks.Open(TextBox1.Text) 'opens excel file'
If (xlWorkbookOpened IsNot Nothing) Then
' DO WHATEVER YOU NEED TO...
Marshal.ReleaseComObject(xlWorkbookOpened)
xlWorkbookOpened = Nothing
End If
Marshal.ReleaseComObject(xlWorkbooks)
xlWorkbooks = Nothing
End If
End If
Catch ex As Exception
' Log error to start with...
Trace.WriteLine(ex.Message)
End Try
Note that I've explicitly released every COM object after use and set the value to Nothing. This is necessary to ensure proper cleanup.

Excel VSTO Workbooks.Open only working when another action is taken first

I am working on a VSTO add-in. I have a customized ribbon, and on that ribbon a button called TemplateCallButton. I also have several other functions and buttons, one of which just opens a folder with templates (included as example). The TemplateCallButton only works and adds in a template file if one of the other actions has been completed (seemingly doesn't matter which one). After any other action has run then it works as expected.
What's more frustrating is that this behavior only seems to happen on machines I deploy on, and not the one I'm developing on. Here is the TemplateCallButton code:
Public Class InsightLabProcessor
Dim MainTemplatePath As String = "C:\Insight\Insight.xltm"
....
Private Sub TemplateCallButton_Click(sender As Object, e As RibbonControlEventArgs) Handles TemplateCallButton.Click
Dim objApp As Excel.Application
objApp = Marshal.GetActiveObject("Excel.Application")
objApp.Visible = True
Dim objWorkbook As Excel.Workbook = objApp.Workbooks.Open(MainTemplatePath)
objWorkbook.Worksheets(4).Activate()
End Sub
and here is the code for the button that just opens a folder:
Private Sub PhaseCodeFolderOpenButton_Click(sender As Object, e As RibbonControlEventArgs) Handles PhaseCodeFolderOpenButton.Click
Process.Start("explorer.exe", "C:\Insight\Phase Codes")
End Sub
or one that opens the control form:
Private Sub ControlPannel_Click(sender As Object, e As RibbonControlEventArgs) Handles ControlPannel.Click
Dim controlpanel As New ControlPanel
controlpanel.Show()
controlpanel = Nothing
End Sub
I feel like I must be missing something simple.
Thanks.
So the problem is in fact the one addressed here: http://support.microsoft.com/kb/238610, which seems pretty vicious to deal with as an add-in. The best solution I've found (again not very elegant) is to just open the command line, write out that we're waiting for the first instance to load, the close it before anyone get's too curious. I tried this on 4 machines and empirically found the longest wait time I needed was 250 ms, so I doubled it to 500 in this:
...
Shell("C:\Windows\System32\cmd.exe", AppWinStyle.MaximizedFocus)
System.Threading.Thread.Sleep(10) 'give cmd just a bit to take the line
SendKeys.Send("Waiting for Excel to register in Running Object Table")
System.Threading.Thread.Sleep(490)
Dim Term() As Process = Process.GetProcessesByName("cmd")
For Each P As Process In Term
P.Kill() 'user probably doesn't have any other instances of cmd open, if they do they are colaterial damage, may handle that if it becomes an issue
Next
Dim objApp As Excel.Application
objApp = Marshal.GetActiveObject("Excel.Application")
objApp.Visible = True
Dim objWorkbook As Excel.Workbook = objApp.Workbooks.Open(MainTemplatePath)
objWorkbook.Worksheets(4).Activate()
Again I would come back and except anything that was more elegant or acceptable to an end user. I would really love to know if there was a way to force Excel to register to the ROT. Perhaps I should turn that into another question.

Object variable or with block variable not set error vb.net

I am populating combo box from database. In debug i can see that the combo box has been populated .
here is the code
Private Sub ComboID_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboID.SelectedIndexChanged
Dim data(21) As String
Try
t_code.Text = ComboID.SelectedItem(0)
ComboID.Visible = False
data = getData(t_code.Text)
populateFields(data)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
but when i run this program i get error:Object variable or with block variable not set error
i would really appreciate your help.
Thanks
Just knowing that the combobox is populated is not enough. You should still be testing for
SelectedIndex >= 0
It is possible that SelectedIndex is changing to -1 if the user clears the selection.
Of course, it is also quite likely that getData is returning Nothing and populateFields can't handle that. It would probably throw a
If data isNot Nothing
end if
test around the populateFields call, too. It never hurts to test for edge cases.