Reading excel files in vb.net leaves excel process hanging - vb.net

The following code works fine but seems to leave instances of excel.exe running in the background. How do I go about closing out this sub properly?
Private Sub ReadExcel(ByVal childform As Fone_Builder_Delux.frmData, ByVal FileName As String)
' In progress
childform.sampleloaded = False
Dim xlApp As Excel.Application
Dim xlWorkBook As Excel.Workbook
Dim xlWorkSheet As Excel.Worksheet
xlApp = New Excel.ApplicationClass
xlWorkBook = xlApp.Workbooks.Open(FileName)
xlWorkSheet = xlWorkBook.Worksheets(1)
Dim columnrange = xlWorkSheet.Columns
Dim therange = xlWorkSheet.UsedRange
childform.datagridHeaders.Columns.Add("", "") ' Super imporant to add a blank column, could improve this
For cCnt = 1 To therange.Columns.Count
Dim Obj = CType(therange.Cells(1, cCnt), Excel.Range)
childform.datagridSample.Columns.Add(Obj.Value, Obj.Value)
childform.datagridHeaders.Columns.Add(Obj.Value, Obj.Value)
Next
For rCnt = 2 To therange.Rows.Count
Dim rowArray(therange.Columns.Count) As String
For cCnt = 1 To therange.Columns.Count
Dim Obj = CType(therange.Cells(rCnt, cCnt), Excel.Range)
Dim celltext As String
celltext = Obj.Value.ToString
rowArray((cCnt - 1)) = celltext
'MsgBox(Obj.Value)
Next
childform.datagridSample.Rows.Add(rowArray)
Next
AdjustHeaders(childform)
childform.sampleloaded = True
End Sub

Short answer: close each item appropriately, then call FinalReleaseComObject on them.
GC.Collect()
GC.WaitForPendingFinalizers()
If xlWorkSheet Is Nothing Then Marshal.FinalReleaseComObject(xlWorkSheet)
If xlWorkBook Is Nothing Then
xlWorkBook.Close(false, false)
Marshal.FinalReleaseComObject(xlWorkBook)
End If
xlApp.Quit()
Marshal.FinalReleaseComObject(xlApp)
Long answer: read this answer to another question (the entire post is helpful too).

I ran into this problem and what I found worked was making sure I called the Close() method on all Workbook and Workbooks objects, as well as the Quit() method on the Excel Application object. I also call System.Runtime.InteropServices.Marshal.ReleaseComObject on every Excel object was instantiated. I do all this in reverse order of age, so the newest object gets cleaned up first and the oldest, which is the Application object, gets taken care of last. I don't know if the order really matters, but it seems like it might.
I've seen examples where GC.Collect() was called at the very end, but I've never had to do that to get the excel.exe process to end.

Related

Loading Powerpoint Array from Excel with VBA

I am trying to have powerpoint macro populate an array by taking data from excel file. In fact, there are two arrays. Same code, same file, but one array gets populated and the other one doesn't (or gets only partially populated). Below, FindList array is working fine and ReplaceList does not. I'd appreciate any help or suggestions for an alternative method.
Option Explicit
Sub Multi_FindReplace()
Dim FindList As Variant
Dim ReplaceList As Variant
Dim x, i, j As Long
Dim xlApp As Object
Dim xlBook As Object
Dim xlSheet As Object
Set xlApp = CreateObject("Excel.Application")
Set xlBook = xlApp.Workbooks.Open("C:\Users\wip\dev\DRAFT Exhibits (Client) 04-06-18.xlsm")
xlBook.Application.Visible = False
FindList = xlBook.Worksheets("FindReplace").Range("A1:A22").Value
ReplaceList = xlBook.Worksheets("FindReplace").Range("B1:B22").Value
Problem solved with using a single array to load all the data. Looks like this:
ExcelData = xlBook.Worksheets("FindReplace").Range("A1:B22").value

.Net Interop Excel ExportAsFixedFormat() Exception HResult: 0x800A03EC

I'm currently implementing a method that generated multiple sheets and export them as PDF. For this I'm using the Microsoft.Office.Interop Library (v14.0.0.0) with .NET 4.5.2 . Running Office is 2016
My code:
Dim excel As New Application()
excel.Visible = False
excel.DisplayAlerts = False
Dim workbooks As Workbooks
workbooks = excel.Workbooks
Dim workbook As Workbook = workbooks.Add(Type.Missing)
[...]
workbook.ExportAsFixedFormat(XlFixedFormatType.xlTypePDF, String.Format(<a Path>)
ReleaseComObject(workSheet)
workbook.Close()
ReleaseComObject(workbook)
excel.Quit()
ReleaseComObject(excel)
The ReleaseComObject() looks like this (according to Microsoft Support):
Private Sub ReleaseComObject(objectToRelease As Object)
While System.Runtime.InteropServices.Marshal.ReleaseComObject(objectToRelease) > 0
End While
objectToRelease = Nothing
End Sub
This works fine if I run this code for one iteration BUT I noticed that the EXCEL-Process is still running.
If I try to do this in batch-mode (in the meaning of a for-loop) I get an excetion when entering the 2nd interation:
System.Runtime.InteropServices.COMException (0x800A03EC): Ausnahme von HRESULT: 0x800A03EC
bei Microsoft.Office.Interop.Excel.WorkbookClass.ExportAsFixedFormat(XlFixedFormatType Type, Object Filename, Object Quality, Object IncludeDocProperties, Object IgnorePrintAreas, Object From, Object To, Object OpenAfterPublish, Object FixedFormatExtClassPtr)
bei Controller.CreateListing(DataTable data, Int32 year, String mandantShortName) in ...
Line that throws exception:
workbook.ExportAsFixedFormat(XlFixedFormatType.xlTypePDF, String.Format(<a Path>)
For reseach/testing I debugged before reentering the loop and killed the excel-process but w/o any changes.
Anyone faced this problem as well? Solutions/Suggestions?
In order to address the issue with Excel not closing properly replace:
Private Sub ReleaseComObject(objectToRelease As Object)
While System.Runtime.InteropServices.Marshal.ReleaseComObject(objectToRelease) > 0
End While
objectToRelease = Nothing
End Sub
With this bit of code as illustrated by Siddharth Rout:
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
obj = Nothing
Catch ex As Exception
obj = Nothing
Finally
GC.Collect()
End Try
End Sub
You could use your While statement instead of the Do statement but I feel it reads better this way. The important bit here is GC.Collect().
You also have to make sure you release in the right order and release everything. This is usually in backwards order. So in your case, start off with the workSheet then the workbook then workbooks and then lastly excel:
ReleaseObject(workSheet)
workbook.Close()
ReleaseObject(workbook)
ReleaseObject(workbooks)
excel.Quit()
ReleaseObject(excel)
This is the code I put together to test:
Dim app As New Excel.Application()
app.Visible = False
app.DisplayAlerts = False
Dim wbs As Excel.Workbooks = app.Workbooks
Dim wb As Excel.Workbook = wbs.Add()
Dim ws As Excel.Worksheet = CType(wb.Sheets(1), Excel.Worksheet)
ReleaseObject(ws)
wb.Close()
ReleaseObject(wb)
ReleaseObject(wbs)
app.Quit()
ReleaseObject(app)
The process starts and once ReleaseObject(app) has been called the process then closes.
I just figured it out what the problem caused (own Bug).
But it still leaves the question open, why the Excel-Proccess ain't closing.
Try to run application or service as administration account. This solved my problem.

How Do I Use the Excel Function LoadPicture() from a Windows Form Application

I have a Windows Form application that my company uses to access all it's reports. Most of the reports are given to the user in an Excel sheet that is created at run-time either from scratch or an Excel Template. This has been working fine for everything up until now. The problem I am running into now is that I need to load an ImageBox on the Excel Template with an image saved on the drive. I have the filepath of the image (this will change each time this run). The only way I have found to be able to set the picture property of the ImageBox is like this...
Dim FileStr As String = "C:\Folder\ImageFile.jpg"
xlWorksheet.ImageName.Picture = LoadPicture(FileStr)
The problem is I can't figure out how to call the LoadPicture() function from within the windows form. I know I could create an Excel Module at run-time that call the LoadPicture() then delete it, but i just figured there had to be a better way? Hoping someone out there has suggestions. Thanks.
Edit:- Here is an example of the code I am Using to Open The Excel Sheet
Imports ExcelVB = Microsoft.Office.Interop.Excel
Imports ad = GartnerInterface.AdminClass.AdminTools
Imports xl = GartnerInterface.AdminClass.XlHelp
Public Class TestClass
Public Shared Sub NewSub()
Dim xlApp As ExcelVB.Application
Dim xlWorkbook As ExcelVB.Workbook
Dim xlWorksheet As ExcelVB.Worksheet
Dim TestSht As String
TestSht = "H:\Josh\ExcelTest.xlsm"
xlApp = CreateObject("Excel.Application")
xlWorkbook = xlApp.Workbooks.Add(TestSht)
xlApp.DisplayAlerts = False
xlApp.Visible = True
xlWorksheet = xlApp.Sheets("Sheet1")
Dim FileStr As String = "H:\12117\12117_Original.png"
'xlWorksheet.RFQImg.Picture = LoadPicture(FileStr)
End Sub
End Class
Now that I'm looking at the code, you would likely need the .AddPicture method
At the bottom of this code, you would need something like the following:
Dim FileStr As String = "H:\12117\12117_Original.png"
xlWorksheet.Shapes.AddPicture(FileStr, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, 50, 50, 300, 45)
Taken from,
https://msdn.microsoft.com/en-us/library/office/ff198302.aspx
OR, if you have a template where the image is already named "test"
Dim FileStr As String = "H:\12117\12117_Original.png"
Dim imgName as String = "test"
For Each myShape In xlWorksheet.Shapes
If myShape.Name = imgName then
cTop = myShape.ShapeRange.Top 'we must save the values here
cLeft = myShape.ShapeRange.Left
cHeight = myShape.ShapeRange.Height
cWidth = myShape.ShapeRange.Width
myShape.delete
Exit For
end if
next
xlWorksheet.Shapes.AddPicture(FileStr, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, cLeft, cTop, cWidth, cHeight)
An example, with user interaction, I borrowed from

Object Library not registered

I am using Access(2007) vba to process an Excel(ACCDB) workbook on a Sharepoint 2007 document library. In the process, I update two properties using the ExcelWB.ContentTypeProperties() proerty. It's been working fine literally for years. Today, it stopped working. It throws a "Object Library not registered" error. Research seems to suggest I've somehow gotten an old DLL for Excel registered, and the interface is using an old DLL that doesn't support the property.
My libraries are all found, no changes were made (by me...) in the last month to the machine. It's a client machine, so I can't speak for MS updates, etc. that were made. How can I fix this? It's literally got my client sitting on her hands, unable to work.
Dim xlApp As Excel.Application
Dim xlWB As Excel.WorkBook
Dim xlSH As Excel.Worksheet
Dim xlRA As Excel.Range
Dim bValidWorkbook As Boolean
Dim lngTrueHighestRow As Long
Dim strWorkbookName As String
WorkbookMessage , 3 'Clears the message queue
bValidWorkbook = True
strNewTimesheetStatus = "Rejected"
Set xlApp = New Excel.Application
xlApp.visible = False 'Don't let the workbook be shown
Set xlWB = xlApp.Workbooks.Open(URL) 'Open the workbook
lngCurrentVendorID = ParseVendor(strCurrentVendorName) 'Sets up the vendor recordset and the Labor rate Recordset. If it returns 0, it couldn't find the vendor
If lngCurrentVendorID = 0 Then
bValidWorkbook = False
WorkbookMessage "Workbook Rejected -- Vendor could not be validated"
GoTo ExitWorkbook
End If
'
' If the workbook is read-only, we can't proceed
'
If xlWB.ReadOnly Then
bValidWorkbook = False
WorkbookMessage "Workbook Rejected -- Workbook is read-only state"
GoTo ExitWorkbook
End If
Set xlSH = xlWB.Worksheets(1) 'Must be the first worksheet in the workbook
strWorkbookName = xlWB.NAME
Set xlRA = xlSH.UsedRange 'xlRA is the range of cells in the first workbook that are "USED"
strSheetArray = xlRA 'This sets an array of variants to the two dimensional range xlRA
xlRA.EntireRow.Hidden = False 'Unhides all rows to prevent misunderstandings.
...
...
...
...
If Not xlWB.ReadOnly Then
xlWB.ContentTypeProperties("TimesheetStatus") = strValidateWorkbook***
xlWB.ContentTypeProperties("OverrideStatus") = strNewOverrideStatus
xlWB.Save
End If

Write to Excel Object

I am trying to write a code that exports data to excel after user prompted actions.
Basically, I have been able to export to Excel successfully, but the 2nd instance I want to write to a new tab instead of a new Excel application.
oExcel = CreateObject("Excel.Application")
oExcel.Visible = True
oBook = oExcel.Workbooks.Add
oSheet = oBook.Worksheets(3)
oSheet.Delete()
oSheet = oBook.Worksheets(2)
oSheet.Delete()
oSheet = oBook.Worksheets(1)
oSheet.Name = "Run " & Counter
At this point, the user will press a button, making Excel no longer active.
So when I want to write more data to a new sheet, the Object commands do not
'work unless I repeat code exactly.
I tried:
Counter +=1
'For the first instance
If Counter = 1 Then
oExcel = CreateObject("Excel.Application")
oExcel.Visible = True
oBook = oExcel.Workbooks.Add
oSheet = oBook.Worksheets(3)
oSheet.Delete()
oSheet = oBook.Worksheets(2)
oSheet.Delete()
oSheet = oBook.Worksheets(1)
oSheet.Name = "Run " & Counter
Else
'For every instance after that the user wants to do another run
oExcel.ActivateObject(Excel.Application)
oBook = oExcel.Workbooks(1)
oSheet = oBook.Worksheets.Add
oSheet.Name = "Run " & Counter
End If
I have been looking for days and am getting very frustrated. I do not know how to reference back to the open excel in order to continue to writing data ... after the user has pressed a button on the VB form confirming they want to do another run.
To get a reference to an already-running instance of excel you can use GetObject.
Eg:
' Test to see if a copy of Excel is already running.
Private Sub testExcelRunning()
On Error Resume Next
' GetObject called without the first argument returns a
' reference to an instance of the application. If the
' application is not already running, an error occurs.
Dim excelObj As Object = GetObject(, "Excel.Application")
If Err.Number = 0 Then
MsgBox("Excel is running")
Else
MsgBox("Excel is not running")
End If
Err.Clear()
excelObj = Nothing
End Sub
http://msdn.microsoft.com/en-us/library/e9waz863(v=vs.90).aspx
If Excel is not already running you can start a new instance using CreateObject.
I used to write VBA, but I was taught to get out of the habit of using CreateObject. you could also use a boolean just as well, but i imagine thats just preference. You should create the excel object outside the loop, hold reference at class level once its assigned. you then use the loop to solely assign the next sheets and add values. Keeping the dimension at class level means you do not need to get rid of the object immediately, because there is possibility the user might still need to use the reference.
Public Class Form1
Dim firstRun As Boolean = True
Dim xlApp As New Excel.Application
Dim xlWb As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'set up us some excel
xlApp.Visible = True
xlApp.SheetsInNewWorkbook = 1
xlWb = xlApp.Workbooks.Add
'imaginary loop
For i = 0 To 5
Dim msgResponse = MessageBox.Show("Do you want to loop?", "Keep Looping?", MessageBoxButtons.YesNo)
If msgResponse = Windows.Forms.DialogResult.No Then Exit For
If firstRun Then
xlSheet = xlWb.Sheets(1)
firstRun = False
Else
xlWb.Activate()
xlSheet = xlWb.Sheets.Add(After:=xlWb.Sheets(xlWb.Sheets.Count))
End If
xlSheet.Name = "TEST" & i
xlSheet.Range("A1").Value = "Some Data"
Next i
End Sub
End Class
You will need to make sure that you clean up your references once you are certain the user is done with the sheet.