I'm having some problems closing Excel files. I am doing a program that opens some Excel files, uses the information and then close them.
I tried some different codes, but they didn't work because the EXCEL process is still running.
My first code:
Dim aplicacaoexcel As New Excel.Application
Dim livroexcel As Object
Dim folhaexcel As Excel.Worksheet
livroexcel = aplicacaoexcel.Workbooks.Open("C:\Users\LPO1BRG\Desktop\Software Fiabilidade\Tecnicos.xlsx", UpdateLinks:=False, ReadOnly:=False, Password:="qmm7", WriteResPassword:="qmm7")
folhaexcel = livroexcel.sheets("Folha1")
aplicacaoexcel.DisplayAlerts = False
aplicacaoexcel.Visible = False
folhaexcel = Nothing
livroexcel.Close()
livroexcel = Nothing
aplicacaoexcel.Quit()
aplicacaoexcel = Nothing
Then I added this: System.GC.Collect() but it still not closing the Excel process.
Now I am trying this:
Dim process() As Process = system.Diagnostics.Process.GetProcessesByName("EXCEL")
For Each p As Process In process
p.Kill()
Next
Actually, this one is working, but it closes all the Excel files (even those that are not opened by my program).
What can I do to close just the Excel files opened by my program? :)
Releasing the Excel.Application Interop COM object is a little bit trickier than other Office Interop objects, because some objects are created without your knowledge, and they all must be released before the main Application can be actually closed.
These objects include:
The Excel.Application
The Excel.Application.WorkBooks collection
The WorkBooks collection opened WorkBook
The WorkBook Sheets collection
The Sheets collection referenced Worksheet
These objects must all be released in order to terminate the EXCEL process.
A simple solution is to use explicit declarations/assignment for all the COM objects:
Dim ExcelApplication As New Microsoft.Office.Interop.Excel.Application()
Dim ExcelWorkbooks As Workbooks = ExcelApplication.Workbooks
Dim MyWorkbook As Workbook = ExcelWorkbooks.Open("[WorkBookPath]", False)
Dim worksheets As Sheets = MyWorkbook.Worksheets
Dim MyWorksheet As Worksheet = CType(worksheets("Sheet1"), Worksheet)
When you're done, release them all:
Imports System.Runtime.InteropServices
Marshal.ReleaseComObject(MyWorksheet)
Marshal.ReleaseComObject(worksheets)
MyWorkbook.Close(False) '<= False if you don't want to save it!
Marshal.ReleaseComObject(MyWorkbook)
ExcelWorkbooks.Close()
Marshal.ReleaseComObject(ExcelWorkbooks)
ExcelApplication.Quit()
Marshal.FinalReleaseComObject(ExcelApplication)
Marshal.CleanupUnusedObjectsInCurrentContext()
Try something like workbook.Save, workbook.close
I'm a little rusty on my VB and don't have access to Visual Studio right now to test this, but I will try writing the code from memory.
The problem you are running into is that setting an object equal to Nothing still leaves the object allocated in memory and doesn't dispose of the object entirely. Even the Close() and Quit() methods of the object still leave it allocated in memory in case the program needs to access them again later. The Kill() method works because it simply kills the Excel application that is running in memory, which closes any open Excel documents as well as the ones used by COM objects. What you want to do is dispose of the specific COM objects that your application is using.
Try this change in your code though.
Dim aplicacaoexcel As New Excel.Application
Dim livroexcel As Object
Dim folhaexcel As Excel.Worksheet
livroexcel = aplicacaoexcel.Workbooks.Open("C:\Users\LPO1BRG\Desktop\Software Fiabilidade\Tecnicos.xlsx", UpdateLinks:=False, ReadOnly:=False, Password:="qmm7", WriteResPassword:="qmm7")
folhaexcel = livroexcel.sheets("Folha1")
aplicacaoexcel.DisplayAlerts = False
aplicacaoexcel.Visible = False
DisposeComObj(folhaexcel)
DisposeComObj(livroexcel)
DisposeComObj(aplicacaoexcel)
Then add the following to your application as well.
Private Sub DisposeComObj(ByRef Reference As Object)
Try
Do Until _
System.Runtime.InteropServices.Marshal.ReleaseComObject(Reference)<=0
Loop
Catch
Finally
Reference = Nothing
End Try
End Sub
Hopefully my memory is serving me well. I haven't written any code in VB or C# in more than a year and feel very out of practice. I only saw your question because I clicked on the wrong link and remembered how I used to struggle with garbage collection when I started programming.
Related
The Correct Code is
Private Sub DeleteTSheets()
Dim xl As New Excel.Application
Dim wb As Excel.Workbook
Dim ws as Excel.Worksheet
wb = xl.Workbooks.Open("C:\Patches\Main_Final.xlsm")
For Each ws in wb.sheets
If InStr(1, ws.Name, "T") Then
If len(ws.name)=3 then
ws.delete()
End If
End If
Next
wb.Close(Savechanges:=vbTrue)
End Sub
The Code is running only when I removed all macros from the excel sheet!!!
Thanks, Regards
Moheb Labib
Fundamentally, for integer loops use For and not Do loop and handle code logic without On Error redirects. Additionally, since your second code implementation works, your main issue is conflating VBA and VB.Net. Though they share same constructs, these two are fundamentally different languages. VBA is an extended utility with MS Office applications while VB.Net is a standalone general purpose language with its .NET sibling, C#.
Consequently, you cannot exactly copy/paste code between the two types. Main difference with VBA and VB.Net is initializing the COM object, Excel.Application. VB.Net (like C#) requires sourcing in the Microsoft.Office.Interop API.
VBA
Dim xl As New Excel.Application
VB.Net (see docs)
Imports Microsoft.Office.Interop
...
Dim xl As Excel.Application = New Excel.Application()
C# (see docs) (as comparison)
using Excel = Microsoft.Office.Interop.Excel;
...
var xl = new Excel.Application();
Additionally, since COM (Command Object Model) is not limited to Microsoft tools but available resource on Windows machines, other COM-connected languages ike open source tools can connect to Excel. Notice like VB.Net and C#, the respective COM libraries must be imported (PHP doing so as .ini extension).
Python
import win32com.client as COM
xl = COM.Dispatch("Excel.Application")
R
libray(RDOMClient)
xl <- COMCreate("Excel.Application")
PHP
$xl = new COM("Excel.Application", NULL, CP_UTF8) or Die ("Did not instantiate Access");
In the first code try replacing the line If InStr(1, wb.Sheets(i).Name, "T") Then with
If InStr(1, wb.Sheets(i).Name, "T") > 0 Then
In the code you developed try replacing the line If InStr(1, wb.Sheets(i).Name, "T") Then with
If InStr(1, ws.Name, "T") > 0 Then
Remember you are looping with ws not i
I am opening an excel file from my winform using:
Dim xlsApp3 As Excel.Application
Dim xlsWB3 As Excel.Workbook
xlsApp3 = CreateObject("Excel.Application")
xlsApp3.Visible = True
xlsWB3 = xlsApp3.Workbooks.Open("C:\myfile.xlsm")
Try
Marshal.ReleaseComObject(xlsWB3)
Marshal.ReleaseComObject(xlsApp3)
xlsWB3 = Nothing
xlsApp3 = Nothing
Catch ex As Exception
xlsWB3 = Nothing
xlsApp3 = Nothing
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
When I look at my task manager, it closes the process, but as soon as I close the excel application window that I open, the process comes back? If I close my whole winform application, thats when the excel process closes. Is the application holding on to the process in memory?
Add the following lines:
xlsWB3.Close(True, Type.Missing, Type.Missing)
xlsApp3.Quit
Don't access anything unmanaged with two dots like you did in
xlsApp3.Workbooks.Open
The Workbooks object is opened behind the scenes but .NET doesn't know about it and it can't clean it up properly.
Instead, you need to make a .NET reference to Workbooks, and the GC will clean everything up for you. Your entire code snippet can be reduced to this
Dim xlApp = New Excel.Application()
Dim xlBooks = xlApp.Workbooks
Dim xlBook = xlBooks.Open("C:\myfile.xlsm")
xlApp.Visible = True
I have encountered a problem regarding the workbook.open function, when trying to open a workbook located on a network folder. The VBA macro leeds to an
"1004 Error"
without any specific reasons, only that the file path is not available.
I have used Google and this community for a very long time to solve this issue, below my steps I tried and my only solution at the end.
My question is: WHY does Excel behave like that and what can I do the next time?
Initially the user inputs the file path in a cell within the Source Workbook, saved locally on the computer. The VBA code take the input of the cell (I tried Range("K4") and also Range("K4").value) and aligns it to the string, which is visible (Variable Watch while Debugging) but failes when it comes to the Workbook.open function.
I tried to use the user specific network path (e.g. "G:/...") but also the Universal Network convention path ("\\xxx.xxx...") which is more accurate because not every user has mapped the network folder to the same drive letter.
At the end my only working solution was the hard coded path in the VBA editor with the UNC path.
Why is so? In this case the networkpath does not change, but when it comes to the moment where it is necessary that the folder must be written in a cell I will be lost.
Thank you for your feebdack!
EDIT:
Basically it's this code... I removed the unnecessary parts...
'Variablen
Dim MA$, Monat$, Fehltag$, Ort$, Projekt$, FilePlanung$, MainString$, NeuerString$
Dim LastRowM&, StartZelleP&, ProjektP&
Dim wb, wbP As Workbook
Dim wsK, wsS, wsM As Worksheet
Dim StartDatumM As Date
Dim array_monate As Variant
'Arbeitsblätter
Set wb = ThisWorkbook
Set wsK = wb.Sheets("Kopfblatt")
Set wsS = wb.Sheets("Stammdaten")
Set wsM = wb.ActiveSheet
'Fix
MA = wsK.Range("D2")
Monat = wsM.Name
FilePlanung = wsS.Range("K4")
Application.ScreenUpdating = False
Set wbP = Workbooks.Open(fileName:=FilePlanung)
'Set wbP = Workbooks.Open(FilePlanung) --> Tried also this and many other ways...
Set wsP = wbP.Sheets("aktuell")
This is the code I use:
Dim wb As Workbook
Set wb = Workbooks.Open(Worksheets("Sheet1").Range("A1").Value)
A Visual Management Board at my workplace uses an excel spreadsheet, with many sheets in it, to populate a VMB on a TV. These sheets have many charts in them. I don't work a whole lot in VB so please bear with me.
I'm convinced my problem is because the chart the program is trying to access, literally isn't visible in the excel sheet that pops up. I may just not know what I'm talking about but this seems like a terrible way to get and display data. But I'm supposed to fix it. Here's the sheet that pops up on the screen (I've erased some information for privacy reasons):
Here is an example of the bmp being created when it tries to export a chart that is not visible in the excel window:
From my research, I've found many have resolved this issue by Activating the chart object before exporting it. I tried to do that here, but an exception gets thrown. Here's the entire section of code dealing with the charts and exports to make BMPs that are supposed to reside in the Documents folder:
Dim xlApp As Excel.Application
Dim xlWorkBooks As Excel.Workbooks
Dim xlWorkBook As Excel.Workbook
Dim xlWorkSheets As Excel.Sheets
Dim xlWorkSheet As Excel.Worksheet
xlApp = New Excel.Application
xlApp.Visible = True
xlWorkBooks = xlApp.Workbooks
xlWorkBook = xlWorkBooks.Open(ScoreCard)
xlWorkSheets = xlWorkBook.Sheets
For x As Integer = 1 To xlWorkSheets.Count
xlWorkSheet = CType(xlWorkSheets(x), Excel.Worksheet)
If xlWorkSheet.Name = My.Settings.Org Then
xlWorkSheet.ChartObjects(2).chart.Export(Filename:=path + "\Documents\OnTimeDelivery.bmp", FilterName:="BMP")
picOnTimeDelivery.Image = New System.Drawing.Bitmap(path + "\Documents\OnTimeDelivery.bmp")
xlWorkSheet.ChartObjects(3).chart.Export(Filename:=path + "\Documents\Quality.bmp", FilterName:="BMP")
picQuality.Image = New System.Drawing.Bitmap(path + "\Documents\Quality.bmp")
xlWorkSheet.ChartObjects(1).chart.Export(Filename:=path + "\Documents\NoDemandInventory.bmp", FilterName:="BMP")
picNoDemandInventory.Image = New System.Drawing.Bitmap(path + "\Documents\NoDemandInventory.bmp")
xlWorkSheet.ChartObjects(7).chart.Export(Filename:=path + "\Documents\ExcessInventory.bmp", FilterName:="BMP")
picExcessInventory.Image = New System.Drawing.Bitmap(path + "\Documents\ExcessInventory.bmp")
xlWorkSheet.ChartObjects(4).chart.Export(Filename:=path + "\Documents\Freight.bmp", FilterName:="BMP")
picFreight.Image = New System.Drawing.Bitmap(path + "\Documents\Freight.bmp")
xlWorkSheet.ChartObjects(5).chart.Export(Filename:=path + "\Documents\ShortagesByStart.bmp", FilterName:="BMP")
picShortagesByStart.Image = New System.Drawing.Bitmap(path + "\Documents\ShortagesByStart.bmp")
xlWorkSheet.ChartObjects(6).chart.Export(Filename:=path + "\Documents\ShortagesRootCause.bmp", FilterName:="BMP")
picShortagesRootCause.Image = New System.Drawing.Bitmap(path + "\Documents\ShortagesRootCause.bmp")
End If
Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkSheet)
Next
xlWorkBook.Close()
xlApp.UserControl = True
xlApp.Quit()
'Close connection to excel sheet
MyConnection.Close()
The program crashes, throwing an Invalid Parameter exception when trying to set
picFreight.Image = New System.Drawing.Bitmap(path + "\Documents\Freight.bmp")
because the Freight.bmp in my documents folder is a 0kb file. If I change what image it's loading next (comment that line out and let it try to load ShortagesByStart.bmp) it crashes for the same reason. All of the charts past this point have one thing in common, they aren't visible on screen. Still this seems like a stupid reason to me; surely something like that wouldn't cause an issue!
First I tried to Activate the xlWorkSheet
xlWorkSheet.Activate()
but this changed nothing.
So I tried to activate the individual ChartObjects by adding
xlWorkSheet.ChartObjects(2).chart.Activate()
xlWorkSheet.ChartObjects(3).chart.Activate()
xlWorkSheet.ChartObjects(1).chart.Activate()
xlWorkSheet.ChartObjects(7).chart.Activate()
xlWorkSheet.ChartObjects(4).chart.Activate()
before the export statements. This actually threw an exception:
So, at this point I'm stuck. How can I activate the chart objects in the worksheet properly? Perhaps there's another problem that's causing this.
So the solution was to update Microsoft Office on whichever PC we wanted to run the VMB from. Corporate creates this Excel spreadsheet with the data on it that this program was trying to display. Eventually, whoever creates this excel sheet got a newer version of Excel, which creates an .xlxs file.
I honestly don't know why the spreadsheet successfully opened in ReadOnly mode, but I suppose there wasn't full support there for Office 2007 and 2010. After upgrading the copy of office on the PC the program created all of the bmps. Weird and I'm sorry if this isn't much of an answer for others but this resolved my issue!
Through excel VBA I'm opening a workbook and closing it, thus quitting the Excel application. But it is still running in the Task manager, which prevents my addin from working properly. Part of the code is as follows:
Private Sub btn_Click()
Dim oExcel As Object
Dim oBook As Object
Dim oSheet As Object
Dim i As Integer
Dim j As Integer
Sheets("Sheet1").Select
Finalrow = Cells(Rows.count, 1).End(xlUp).row
LastCol = Cells(1, Columns.count).End(xlToLeft).Column
Set oExcel = New Excel.Application
Set oBook = oExcel.Workbooks.Open(ADDIN_PATH & "\" & "hello.xls")
Set oSheet = oBook.Worksheets(1)
sCellName = "--Select--"
oSheet.Range("A3").Value = "Name"
If (ComboBox1.Value = "--Select--" Or ComboBox1.Value = "") Then
MsgBox "Please Map the name field"
'Else
'oSheet.Range("B2").Value = ComboBox1.Value
Exit Sub
End If
'oSheet.Range("B2").Value = ComboBox1.Value
oSheet.Range("B2").Value = ComboBox1.Value
If (ComboBox2.Value = "") Then ComboBox2.Value = sCellName
oBook.SaveAs ADDIN_PATH & "\" & "hello.xls"
oBook.Close
oExcel.Quit
Set oExcel = Nothing
MsgBox "Your current setting has been saved"
SettingForm.Hide
End Sub
When I run into this issue with ghost Excel processes after creating and manipulating an excel application/workbook (usually from Access) the solution I found is always the same - but after doing some testing on my end, the code you have there does not create a ghost process for me. Either way implementing this fix should work, so here are two suggestions:
1.) You need to fully qualify every reference when you are creating/manipulating a new excel application - COM keeps track of the number of open objects by a simple count - when you open a new excel process it ticks up by 1, but if you make an unqualified reference with two open (Say, using Cells(Rows.Count,1)... instead of oExcel.Workbooks(oBook.Name).Worksheets(oSheet.Name).Cells(oExcel.Workbooks(oBook.Name).WorkSheets(oSheet.Name).Rows.Count,1)..., it ticks up again, and now even when they're all closed the internal count of COM objects is still at 1 and Excel stays open. (Obviously you don't have to type out the full thing each time, just when creating the objects.) So I would use -
dim thisSheet as Worksheet
set thisSheet = ActiveWorkbook.Worksheets(1)
thisSheet.Activate
Even if it isn't what's causing the problem now, doing something like Sheets("Sheet1") when automating Excel from another application, without the accompanying workbook/application reference is 100% guarenteed to make a ghost process.
When you do that for all the Rows.Count, Columns.Count, etc, I think you should specify the sheet for the comboboxes too.
2.) When all thats done, the surefire way to figure out which line is incrementing that COM object count is to move the oExcel.Quit line to just after the Set oExcel = New Excel.Application line , and keep re-running it and moving that oExcel.Quit line further and further down until you encounter the ghost process, and that'll be your culprit.
I hope that helps fixed it, I wasted way too many hours a similar problem, hopefully it's the same root cause.