Highlighting alternating Excel Sheet Rows using VBA from an Access DB - vba

I have been attempting to develop a routine that will highlight every nth row on a range in Excel from an Access database.
This eliminates a lot of the different code offerings on the subject since most leverage the embedded Excel functions.
The code below is a stand alone extraction from my Access VBA that I have been using for testing in hopes that I could find the correct parameter structure to make it work. As such, the code includes some Dim statements, etc that would not be required if I were embedding this macro directly as an Excel macro.
The code I have accomplishes selecting every other row but for some reason, only the first column of the intended range. I have not been able to resolve this problem and include the other columns in the formating process.
Any assistance would be much appreciated.
Sub xxx()
Dim xlbook As Excel.Workbook
Dim xlRng As Range
Dim xlFinalRange As Range
Dim intColumnCount As Integer
Dim introwcount As Integer
Dim strTable As String
Set xlbook = Excel.ThisWorkbook
strTable = "Sheet1"
introwcount = 20
intColumnCount = 14
Set xlFinalRange = Sheets(strTable).Range("A4")
xlFinalRange.Resize(1, intColumnCount).Select
Set xlRng = Sheets(strTable).Range("A4")
xlRng.Resize(1, intColumnCount).Select
intRowsBetween = 2
For i = 0 To introwcount
Set xlRng = xlRng.Offset(intRowsBetween, 0)
xlRng.Resize(1, intColumnCount).Select
Set xlFinalRange = xlbook.Application.Union(xlFinalRange, xlRng)
xlFinalRange.Resize(1, intColumnCount).Select
i = i + (intRowsBetween - 1)
Next i
xlFinalRange.Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorAccent1
.TintAndShade = 0.799981688894314
.PatternTintAndShade = 0
End With
End Sub

The best way is to add a proper Step to your loop. Also, qualify everything properly: Range should be Excel.Range, etc. Try the following:
Sub HighlightXL()
Dim WBK As Excel.Workbook
Dim WS As Excel.Worksheet
Dim Iter As Long
Dim CombinedRng As Excel.Range, IterRng As Excel.Range
Excel.Application.Visible = True
Set WBK = Excel.Workbooks.Add 'Modify as necessary.
Set WS = WBK.Sheets("Sheet1") 'Modify as necessary.
With WS
For Iter = 1 To 22 Step 3 '1, 4, 7, 9... etc...
Set IterRng = .Cells(Iter, 1).Resize(1, 5) 'Resize to 14 in your case.
If CombinedRng Is Nothing Then
Set CombinedRng = IterRng
Else
Set CombinedRng = Union(CombinedRng, IterRng)
End If
Next Iter
End With
CombinedRng.Interior.ColorIndex = 3 'Red.
End Sub
Screenshot:
Let us know if this helps. :)

I have taken a slightly different approach in the past. Below is what I would use:
Sub ColourSheet()
Dim ApXL As Object, xlWBk As Object, xlWSh As Object, _
rng As Object, c As Object
Dim strSheet As String, strFile As String
Dim iColourRow As Integer, iRows As Integer, _
iCols As Integer, x As Integer, iStartRow As Integer
strFile = "C:\YourFolder\YourFile.xlsx"
strSheet = "SheetName"
iColourRow = 3
iRows = 30
iCols = 10
iStartRow = 2
If SmartGetObject("Excel.Application") Then
'excel open
Set ApXL = GetObject(, "Excel.Application")
Else
Set ApXL = CreateObject("Excel.Application")
End If
Set xlWBk = ApXL.Workbooks.Add
'Set xlWBk = ApXL.Workbooks.Open(strFile)
Set xlWSh = xlWBk.activesheet
'Set xlWSh = xlWBk.Worksheets(strSheet)
For x = 1 To iRows
If x Mod iColourRow = 0 Then
With xlWSh.range(xlWSh.cells(iStartRow + x - 1, 1), _
xlWSh.cells(iStartRow + x - 1, iCols)).interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
'.ThemeColor = xlThemeColorAccent1
.Color = 255
.TintAndShade = 0.799981688894314
.PatternTintAndShade = 0
End With
End If
Next x
ApXL.Visible = True
End Sub
A few notes:
Especially if you plan to distribute your database I would advise using late binding for references to Excel, if you use VBA references sooner or later someone's database will stop working and you'll reach the conclusion it is due to a missing reference. Search Late Binding and you'll see plenty on the subject. Note that with late binding you don't get the variables such as xlThemeColorAccent1, you can always get these from opening an Excel VBA instance etc.
I have used a Function call GetSmartObject which identifies if you have Excel running already, a problem I ran into quite a bit was opening additional Excel instances, hitting an error and then that instance remaining running in the background, you then need to go into Task Manager to close it.
Lastly I have just commented out the alternate workbook open where you open a designated file and set the sheet, testing it was easier to open a new workbook and use the active sheet.
Hope this helps
Function SmartGetObject(sClass As String) As Integer
Dim oTmpObject As Object
' If Server running, oTmpObject refers to that instance.
' If Server not running Error 429 is generated.
On Error Resume Next
Set oTmpObject = GetObject(, sClass)
' oTmpObject is reference to new object.
If Err = 429 Then
SmartGetObject = False
Exit Function
' Server not running, so create a new instance:
'Simon noted out: Set oTmpObject = GetObject("", sClass)
' NOTE: for Excel, you can add the next line to view the object
' oTmpObject.Visible = True
ElseIf Err > 0 Then
MsgBox Error$
SmartGetObject = False
Exit Function
End If
Set oTmpObject = Nothing
SmartGetObject = True
End Function
Credit for the above function belongs elsewhere but I've had it so long I don't know where it came from, if anyone can tell me I'll credit it correctly in future.

Option Compare Database
Option Explicit
Sub ExporttoExcel()
Dim i As Integer
Dim y As Integer
Dim varArray As Variant 'Used for obtaining the Names of the Sheets from the DB being exported
Dim varField As Variant 'Used for Naming of the Sheets being exported
Dim dbs As DAO.Database
Dim rst1 As DAO.Recordset 'DB Recordset for the Input and Output information
Dim rst2 As DAO.Recordset 'DB Recordset for the Table names to be exported and sheet names in Excel
Dim rst3 As DAO.Recordset 'DB Recordset that is reused for each Table being exported
Dim strFile As String 'Used for the name and location of the Excel file to be saved
Dim strTable As String 'Table name being exported and also used for the Sheet name
Dim strTitle As String 'Title for the Data on each sheet
Dim xlApp As Object
Dim xlBook As Object
Dim xlSheet As Object
Dim xlRunning As Boolean 'Flag to identify that Excel is running or not
Dim intColumnCount As Integer 'The number of columns on a sheet for formatting
Dim intRowCount As Integer 'The number of rows on a sheet for formatting
Dim intStartRow As Integer 'The row from which to start the highlighting process
Dim intRowsBetween As Integer 'The number of rows between highlighting
If SmartGetObject("Excel.Application") Then
Set xlApp = GetObject(, "Excel.Application") 'Excel is already open so the existing instance will be used
xlRunning = True
Else
Set xlApp = CreateObject("Excel.Application") 'Excel is not open so an instance will be created
xlRunning = False
End If
Set xlBook = xlApp.Workbooks.Add
xlApp.Visible = True
xlApp.DisplayAlerts = False
Set dbs = CurrentDb
'Retrieve Study Location and Name for Import to Database
Set rst1 = dbs.OpenRecordset("StudyTarget")
strFile = rst1!OutputFile
' Removed VBA for File Name & Save Path Information
With xlBook
Set rst2 = dbs.OpenRecordset("ExportTableGroup", dbOpenSnapshot)
' Removed VBA for Excel Naming information from DB
For y = 0 To rst2.RecordCount - 1
strTable = varArray(y, 1)
strTitle = varArray(y, 2)
Set rst3 = dbs.OpenRecordset(strTable, dbOpenTable)
.Sheets.Add after:=Sheets(Sheets.Count)
.Sheets(Sheets.Count).Name = strTable
Set xlSheet = .ActiveSheet
'COPY the Access Table Data to the Named Worksheet
xlSheet.Cells(2, 1).CopyFromRecordset rst3
'Select every X number of rows between sheet Data Rows on Worksheet to highlight
intRowsBetween = 2
intStartRow = 4
For i = 0 To intRowCount Step intRowsBetween
If xlSheet.Cells(intStartRow + i, 1) = "" Then
Exit For
End If
With xlSheet.Range(xlSheet.Cells(intStartRow + i, 1), _
xlSheet.Cells(intStartRow + i, intColumnCount)).Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = RGB(186, 186, 186)
.TintAndShade = 0.6
.PatternTintAndShade = 0
End With
Next i 'Next Row
Next 'Next Table
.Sheets("sheet1").Delete
.Sheets(1).Select 'Go to first sheet of workbook
End With
Export_to_Excel_Exit:
rst1.Close
rst2.Close
rst3.Close
xlApp.ActiveWorkbook.Save
xlBook.Close
If xlRunning Then 'Check to see if used an existing instance of Excel via SmartGetObject
Else
xlApp.Quit
Set xlApp = Nothing
End If
Set xlBook = Nothing
Set rst1 = Nothing
Set rst2 = Nothing
Set rst3 = Nothing
Set dbs = Nothing
Exit Sub

Related

Error 3011 when doing transferspreadsheet

Running VBA through Access.
Attempting to transfer select queries from access to excel.
If I run all the code together, then I get 3011 run-time error on the 2nd DoCmd.TransferSpreadsheet.
If I comment out all the code related to PATH1, then the 2nd DoCmd.TransferSpreadhseet runs fine.
The Microsoft Access database engine could not find the object 'TabUSR1'. Make sure the object exists and that you spell its name and the path name correctly...
I have removed a good bit of code that I feel to be irrelevant to my issue. That is why there are so many variables you do not see any code for.
Dim tempR1 As String
Dim tempR2 As String
Dim tempValue1 As String
Dim tempValue2 As String
Dim tempValue3 As String
Dim tempValue4 As String
Dim tempValue5 As String
Dim dt As Date
Dim d As String
Dim row As String
Dim rngC As Range
Dim rngU As Range
Dim fpath As String
Dim strFileExists
Dim xlappC As Excel.Application
Dim xlbookC As Excel.Workbook
Dim xlsheetC As Excel.Worksheet
Dim xlappU As Excel.Application
Dim xlbookU As Excel.Workbook
Dim xlsheetU As Excel.Worksheet
fpath = "PATH1"
strFileExists = Dir(fpath)
If strFileExists <> "" Then
'set variables for Excel
Set xlappC = CreateObject("Excel.Application")
Set xlbookC = xlappC.Workbooks.Open(fpath)
Set xlsheetC = xlbookC.Worksheets("Audit Fees Remittance")
With xlappC
.Visible = False
.DisplayAlerts = False
.Workbooks.Open fpath
'Update Raw Data Cad and CSCT tab
Set xlsheetC = xlbookC.Worksheets("Raw Data CAD and CSCT")
With xlsheetC
Set rst = CurrentDb.OpenRecordset("Weekly CAN 5 Raw Data to include csct")
If rst.RecordCount > 0 Then
tempR2 = rst.RecordCount + 1
tempR2 = .Cells(.Rows.Count, "CV").End(xlUp).Offset(tempR2).Address(False, False)
tempR1 = .Cells(.Rows.Count, "A").End(xlUp).Offset(1).Address(False, False)
Set rngC = .Range(tempR1, tempR2)
rngC.Name = "TabFA8"
DoCmd.TransferSpreadsheet acExport, 10, "PATH1", True, "TabFA8"
.Rows(2).EntireRow.Delete
rst.Close
Set rst = Nothing
Else
rst.Close
Set rst = Nothing
End If
tempValue2 = "$A$2:" & tempR2
.Range(tempValue2).EntireColumn.AutoFit
tempR1 = ""
tempR2 = ""
End With
'Remit for US
fpath = "PATH2"
strFileExists = Dir(fpath)
If strFileExists <> "" Then
'set variables for Excel
Set xlappU = CreateObject("Excel.Application")
Set xlbookU = xlappU.Workbooks.Open(fpath)
Set xlsheetU = xlbookU.Worksheets("Remittance Tab")
With xlappU
.Visible = False
.DisplayAlerts = False
.Workbooks.Open fpath
'Update INTL Remittance tab
Set xlsheetU = xlbookU.Worksheets("INTL Remittance")
With xlsheetU
Set rst = CurrentDb.OpenRecordset("Weekly US 5 Remittance Tab B DHLG and Jas")
If rst.RecordCount > 0 Then
tempR2 = rst.RecordCount + 1
tempR2 = .Cells(.Rows.Count, "V").End(xlUp).Offset(tempR2).Address(False, False)
tempR1 = .Cells(.Rows.Count, "A").End(xlUp).Offset(1).Address(False, False)
If Len(tempR1) = 3 Then
row = Right(tempR1, 2)
Else
row = Right(tempR1, 3)
End If
'set range for renaming
'this will allow TransferSpreadhseet to know where to export to on the sheet
Set rngU = .Range(tempR1, tempR2)
rngU.Name = "TabUSR2"
DoCmd.TransferSpreadsheet acExport, 10, "Weekly US 5 Remittance Tab B DHLG and Jas", "PATH2", True, "TabUSR2"
'delete row with headers
.Rows(row).EntireRow.Delete
rst.Close
Set rst = Nothing
Else
rst.Close
Set rst = Nothing
End If
End With
While I cannot exactly understand or diagnose your issue, for maintenance and readability, consider separating all Excel and Access processes. Avoid walking over same opened files with both object libraries. Therefore, consider Excel's Range.CopyFromRecordset over Access's DoCmd.TransferSpreadsheet using the very recordset you create.
...
Set rst = CurrentDb.OpenRecordset("Weekly CAN 5 Raw Data to include csct")
...
Set rngC = .Range(tempR1, tempR2)
rngC.Name = "TabFA8"
rngC.CopyFromRecordset rst
rst.Close
...
Set rst = CurrentDb.OpenRecordset("Weekly US 5 Remittance Tab B DHLG and Jas")
...
Set rngU = .Range(tempR1, tempR2)
rngU.Name = "TabUSR2"
rngU.CopyFromRecordset rst
rst.Close
Parfait's suggestion of closing the workbook then doing the TransferSpreadsheet solved my issue.

Generating a Microsoft Word Report from Excel—Application Waiting for OLE Action? (VBA)

I'm trying to write a macro that will generate a Microsoft Word 'report' from an Excel file. I want for the macro to navigate to bookmarks in a Word template for the report, and insert at each certain content or a chart from the native Excel file. The macro works when running in piecemeal, but altogether fails to execute, with Excel repeating over and over that "[It] is waiting for another application to complete an OLE action."
To clarify also, the macro first clears a certain 'data dump' region in the workbook (its native file) and repopulates it with new data from a specified file. This file (its location path) and the various 'target row' and 'identifier' variables you see in the code are inputted by the user to a sort of interface (just a worksheet in the native workbook), where each is labeled manually as a (named) range to be easily fed into to be used by the code. The macro then creates the report by going through the different sheets of the workbook, copying certain content, and turning to Word to paste the copied content at template locations indicated by bookmarks.
I'm completely perplexed by the 'OLE error'. Any ideas about this/the code otherwise? Please share. Thanks for your help!
Sub GenerateReport()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Dim myWorkbook As Excel.Workbook
Set myWorkbook = ThisWorkbook
Dim myWorksheet As Excel.Worksheet
Set myWorksheet = myWorkbook.Sheets("Sheet1")
Dim myWorksheet2 As Excel.Worksheet
Set myWorksheet2 = myWorkbook.Sheets("Sheet2")
Dim myWorksheet3 As Excel.Worksheet
Set myWorksheet3 = myWorkbook.Sheets("Sheet3")
Dim FileName As String
FileName = myWorksheet.Range("FileName")
Dim FilePath As String
FilePath = myWorksheet.Range("FilePath")
Dim TargetSheetName As String
TargetSheetName = myWorksheet.Range("TargetSheetName")
Dim PasteSheetName As String
PasteSheetName = myWorksheet.Range("PasteSheetName")
Dim Identifier As String
Identifier = myWorksheet.Range("Identifier")
Dim Identifier2 As String
Identifier2 = myWorksheet.Range("Identifier2")
Dim TargetRow As String
TargetRow = myWorksheet.Range("TargetRow")
Dim TargetRow2 As String
TargetRow2 = myWorksheet.Range("TargetRow2")
Dim PasteIdentifier As String
PasteIdentifier = myWorksheet.Range("PasteIdentifier")
Dim PasteIdentifier2 As String
PasteIdentifier2 = myWorksheet.Range("PasteIdentifier2")
Dim PasteTargetRow As String
PasteTargetRow = myWorksheet.Range("PasteTargetRow")
Dim PasteTargetRow2 As String
PasteTargetRow2 = myWorksheet.Range("PasteTargetRow2")
Dim Text As String
Text = myWorksheet.Range("Text")
Dim Text2 As String
Text2 = myWorksheet.Range("Text2")
Dim Text3 As String
Text3 = myWorksheet.Range("Text3")
Dim ReportTemplateFilePath As String
ReportTemplateFilePath = myWorksheet.Range("ReportTemplateFilePath")
Dim ReportTemplateFileName As String
ReportTemplateFileName = myWorksheet.Range("ReportTemplateFileName")
Dim SaveToLocation As String
SaveToLocation = myWorksheet.Range("SaveToLocation")
Dim SourceTargetSheet As Excel.Worksheet
Set SourceTargetSheet = myWorkbook.Sheets(PasteSheetName)
Dim TargetWorkbook As Excel.Workbook
Set TargetWorkbook = Workbooks.Open(FilePath)
Dim TargetSheet As Excel.Worksheet
Set TargetSheet = TargetWorkbook.Sheets(TargetSheetName)
'Clear old info
Dim UpperLeftHandCornerOfClear As String
UpperLeftHandCornerOfClear = "A" & PasteTargetRow
Dim LowerRightHandCornerOfClear As String
LowerRightHandCornerOfClear = "XFD" & PasteTargetRow2
SourceTargetSheet.Range(UpperLeftHandCornerOfClear, LowerRightHandCornerOfClear).ClearContents
'Copy new info for pasting
Dim StartingColumnAsRange As Range
Set StartingColumnAsRange = TargetSheet.Cells.Find(Identifier, LookIn:=xlValues, LookAt:=xlPart)
If Not StartingColumnAsRange Is Nothing Then
Dim StartingColumn As String
StartingColumn = Split(StartingColumnAsRange.Address, "$")(1)
End If
Dim EndingColumnAsRange As Range
Set EndingColumnAsRange = TargetSheet.Cells.Find(Identifier2, LookIn:=xlValues, LookAt:=xlPart)
If Not EndingColumnAsRange Is Nothing Then
Dim EndingColumn As String
EndingColumn = Split(EndingColumnAsRange.Address, "$")(1)
End If
Dim UpperLeftHandCornerOfCopy As String
UpperLeftHandCornerOfCopy = StartingColumn & TargetRow
Dim LowerRightHandCornerOfCopy As String
LowerRightHandCornerOfCopy = EndingColumn & TargetRow2
TargetSheet.Range(UpperLeftHandCornerOfCopy, LowerRightHandCornerOfCopy).Copy
Dim PastePasteTarget As String
PastePasteTarget = "A" & PasteTargetRow
SourceTargetSheet.Range(PastePasteTarget).PasteSpecial Paste:=xlPasteValues
'Create a Microsoft Word object (instance of Word to control)
Dim WordApplication As Word.Application
Set WordApplication = CreateObject("Word.Application")
'Error handle if Microsoft Word is open
On Error Resume Next
Set WordApplication = GetObject(class:="Word.Application")
Err.Clear
If WordApplication Is Nothing Then
Set WordApplication = CreateObject(class:="Word.Application")
End If
On Error GoTo 0
'Error handle if report template is specifically already open
On Error Resume Next
Application.DisplayAlerts = False
Documents(ReportTemplateFileName).Close SaveChanges:=wdDoNotSaveChanges
On Error GoTo 0
Application.DisplayAlerts = True
Dim WordDocument As Word.Document
Set WordDocument = WordApplication.Documents.Open(ReportTemplateFilePath)
'Content from 'myWorksheet'
With WordDocument
.Bookmarks("Bookmark1").Range.Text = myWorksheet.Range("Text1")
.Bookmarks("Bookmark2").Range.Text = myWorksheet.Range("Text2")
.Bookmarks("Bookmark3").Range.Text = myWorksheet.Range("Text3")
.Bookmarks("Bookmark4").Range.Text = myWorksheet.Range("Text4")
End With
'Content from 'myWorksheet2'
With WordDocument
.Bookmarks("Bookmark5").Range.Text = myWorksheet2.Range("Text5")
.Bookmarks("Bookmark6").Range.Text = myWorksheet2.Range("Text6")
.Bookmarks("Bookmark7").Range.Text = myWorksheet2.Range("Text7")
.Bookmarks("Bookmark8").Range.Text = myWorksheet2.Range("Text8")
.Bookmarks("Bookmark9").Range.Text = myWorksheet2.Range("Text9")
.Bookmarks("Bookmark10").Range.Text = myWorksheet3.Range("Text10")
End With
'Chart (alone on worksheet)
WordApplication.Selection.Goto What:=wdGoToBookmark, Name:="Chart1"
ThisWorkbook.Sheets("Chart 1 Worksheet Name").ChartObjects(1).Copy
WordApplication.Selection.Paste
WordApplication.Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter
'Two charts grouped together
WordApplication.Selection.Goto What:=wdGoToBookmark, Name:="Chart2"
ThisWorkbook.Sheets("Chart 2 Worksheet Name").ChartObjects(1).Copy
WordApplication.Selection.Paste
WordApplication.Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter
With WordDocument
.SaveAs FileName:=SaveToLocation & " " & Text3, _
FileFormat:=wdFormatDocumentDefault
.Close
End With
WordApplication.Quit
Set WordApplication = Nothing
Set WordDocument = Nothing
Application.ScreenUpdating = True
'Application.DisplayAlerts = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
myWorksheet.Activate
MsgBox "Report successfully generated.", vbInformation, "Completed!"
End Sub
Try modifying your Word application creation script - this is all you need:
On Error Resume Next
Set WordApplication = GetObject(class:="Word.Application")
On Error GoTo 0
If WordApplication Is Nothing Then
Set WordApplication = CreateObject(class:="Word.Application")
End If
It may be that Word is waiting for some input from you but you're not seeing it because you didn't make the instance visible, so try also adding:
WordApplication.Visible = True

Deleting A Row In Excel From VBE

I have got some code that looks for a record in an excel file that contains the same characters as a textbox. I then need to delete that row, but I am not sure how to do this. Please could anybody help me?
Here is my code:
Dim oXL As Excel.Application
Dim oSheet As Excel.Worksheet
Dim oRng As Excel.Range
'create object and open workbook
oXL = CreateObject("Excel.application")
oXL.Workbooks.Open(CreateAdmin.FileLocation + "\DVDRental.xls")
'open worksheet
oSheet = oXL.ActiveWorkbook.Sheets("Logins")
oRng = oSheet.Range("A1:A100")
Dim blankspace As Integer = 1
Do Until oRng(blankspace, 2).value() = ""
If oRng(blankspace, 2).value() <> "" Then
blankspace = blankspace + 1
End If
Loop
Dim introw As Integer = 1
Dim user As String
Dim username As String = TextBox1.Text
If username = "" Then
MsgBox("Please Enter A User ID")
Else
Do
user = oRng(introw, 2).value()
If user = username Then
oXL.ActiveWorkbook.Save()
oXL.ActiveWorkbook.Close()
Exit Do
Else : introw = introw + 1
End If
Loop Until introw = blankspace
oRng = Nothing
oSheet = Nothing
oXL.Quit()
oXL = Nothing
TextBox1.Clear()
The following example removes rows with a first column value equal to zero. Modify it to suit your need.
As you can imagine, removing a row "repositions" all the others below, so I found it easier to work upward, rather than track the changes.
For RowDelete = Worksheets(TargetSheet).UsedRange.Rows.Count To 1 Step -1
If Cells(RowDelete, 1).Value = 0 Then
Rows(RowDelete).Delete
End If
Next RowDelete
(If this runs too slowly try ActiveSheet.EnableCalculation = False
and Application.ScreenUpdating = False before, and then again, but True after the loop.)

Disposing of Excel after using it in VBA

I have a routine that pastes any recordset into an existing workbook from an MS Access database. The code works fine the first time, but I can never run it twice because it leaves one instance of Excel running in the Task Manager. Of course, this causes an error when I refer to Excel objects in my code the 2nd, 3rd, etc. time, because the objects are ambiguous.
For the sake of missing anything here is the entire code:
'I call the routine like so:
Private Sub cmdGenerateRpt
Dim strPath As String
strPath = "C:\Test\MyReport.xlsx"
Call PushToExistingExcel("MAIN SHEET", strPath)
End sub
Public Sub PushToExistingExcel(strSheetToPlaceData As String, strPathToWorkbook As String)
'Puts a recordset into a specific cell of an Excel workbook
Dim xlApp As Object
Dim wb As Object
Dim xlSheet As Object
Dim rs As DAO.Recordset
Dim rsTotals As DAO.Recordset
Dim x As Integer
Dim fld As Variant
Dim intRecords As Integer
Dim intTotals As Integer
Set xlApp = CreateObject("Excel.Application")
Set wb = xlApp.Workbooks.Open(strPathToWorkbook)
Set xlSheet = wb.Sheets(strSheetToPlaceData) 'or you can manually type the sheet name in place of strSheetToPlaceData
Set rs = CurrentDb.OpenRecordset("Select * from qryRPT")
Set rsTotals = CurrentDb.OpenRecordset("Select * from qryTOTALS")
intRecords = rs.RecordCount
intTotals = intRecords + 3
xlSheet.Select
xlSheet.Range("A3:AH3").Select
xlSheet.Range(Selection, Selection.End(xlDown)).Select
'PLACE
xlSheet.Range("A3").CopyFromRecordset rs
xlSheet.Range("L" & intRecords + 3).CopyFromRecordset rsTotals
Cells.EntireColumn.AutoFit
xlSheet.Range("A1").Select
Range("A" & intTotals & ":AH" & intTotals).Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.PatternTintAndShade = 0
End With
With Selection.Font
.Name = "Calibri"
.FontStyle = "Bold"
.Size = 11
.ThemeColor = xlThemeColorDark1
.TintAndShade = 0
End With
Selection.NumberFormat = "$#,##0.00"
Range("A" & intTotals).Value = "TOTALS"
wb.Save
MsgBox "Done"
xlApp.Visible = True
'If I use xlApp.quit it quits, but still leaves it running in task manager
Set wb = Nothing
Set xlSheet = Nothing
Set rs = Nothing
Set rsTotals = Nothing
Set xlApp = Nothing
End Sub
At the end of the day, I want for the finished Workbook to open up presenting itself to the user. There is no reason to just say that the report is done - 'go look for it'.
But I can't seem to figure out how to get rid of the instance of Excel left over from VBA.
In order to clean up successfully, you need to
destroy all objects that refer to objects in the Excel App
close all workbooks
Quit the app
Also, to avoid any mistakes and to create cleaner code you should
avoid use of the implicit ActiveSheet. The unqualified references to Cells. ..., Range( ..., Selection. ... may be leaving references to the Excel app hanging. Use variables for all references
avoid Select, Selection etc
See this answer for help on avoiding these
Cleanup code should be
Set xlSheet = Nothing
For Each wb In xlApp.Workbooks
wb.Close False
Next
xlApp.Quit
Set xlApp = Nothing
This one is closer to 'air tight.' In addition to avoiding using '.select' or '.selection any stray reference like cells.EntireColumn.AutoFit was a Gotcha for me.
Note how I tried to stick to 3 variables for Excel - xlApp, wb and xlSheet
Any reference I used needed all three of these tightly integrating the full address. I also used 'Late Binding.'
Then I isolated the presentation of the workbook in another routine.
Use this as an example for pasting a complex query into an existing workbook at a specified location and presenting the report. It works nicely!
Public Sub PushToExistingExcel(strSheetToPlaceData As String, strPathToWorkbook As String)
'Puts a recordset into a specific cell of an Excel workbook
Dim xlApp As Object
Dim wb As Object
Dim xlSheet As Object
Dim rs As DAO.Recordset
Dim rsTotals As DAO.Recordset
Dim x As Integer
Dim fld As Variant
Dim intRecords As Integer
Dim intTotals As Integer
Set xlApp = CreateObject("Excel.Application")
Set wb = xlApp.Workbooks.Open(strPathToWorkbook)
Set xlSheet = wb.Sheets(strSheetToPlaceData) 'or you can manually type the sheet name in place of strSheetToPlaceData
Set rs = CurrentDb.OpenRecordset("Select * from qryRPT")
Set rsTotals = CurrentDb.OpenRecordset("Select * from qryTOTALS")
intRecords = rs.RecordCount
intTotals = intRecords + 3
xlSheet.Rows("3:" & xlSheet.Rows.Count).ClearContents
'PLACE
With xlSheet
.Range("A3").CopyFromRecordset rs
.Range("L" & intRecords + 3).CopyFromRecordset rsTotals
.Cells.EntireColumn.AutoFit
End With
With xlSheet.Range("A" & intTotals & ":AH" & intTotals).Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.PatternTintAndShade = 0
End With
With xlSheet.Range("A" & intTotals & ":AH" & intTotals).Font
.Name = "Calibri"
.FontStyle = "Bold"
.Size = 11
.ThemeColor = xlThemeColorDark1
.TintAndShade = 0
End With
xlSheet.Range("A3:AH" & intTotals).NumberFormat = "$#,##0.00"
xlSheet.Range("A" & intTotals).Value = "TOTALS"
wb.Save
'cleanup
Set xlSheet = Nothing
For Each wb In xlApp.Workbooks
wb.Close False
Next
Set rs = Nothing
Set rsTotals = Nothing
xlApp.Quit
Set xlApp = Nothing
MsgBox "Report Complete"
PresentExcel (strPathToWorkbook)
End Sub
Public Sub PresentExcel(strPath As String)
Dim xlApp As Object
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = True
xlApp.Workbooks.Open strPath
Debug.Print xlApp.Version
Set xlApp = Nothing
End Sub

Can I list files from a folder sorted by date modified?

I found this code, but list file names sorted by name and I don't know how to adapt it:
Dim MyPathAs String
Dim MyNameAs String
With Dialogs(wdDialogCopyFile)
If .Display() <> -1 Then Exit Sub
MyPath = .Directory
End With
If Len(MyPath) = 0 Then Exit Sub
If Asc(MyPath) = 34 Then
MyPath = Mid$(MyPath, 2, Len(MyPath) - 2)
End If
MyName = Dir$(MyPath& "*.*")
Do While MyName<> ""
Selection.InsertAfterMyName&vbCr
MyName = Dir
Loop
Selection.CollapsewdCollapseEnd
End Sub
Here's a different way to do it. In Word VBA editor:
Tools > References... > checkmark both of:
Microsoft Scripting Runtime
Microsoft Excel Object Library
Then:
Dim iFil As Long
Dim FSO As FileSystemObject
Dim fil As File
Dim fld As Folder
Dim xlApp As Excel.Application
Dim sh As Excel.Worksheet
Dim rngTableTopLeft As Excel.Range
Set xlApp = New Excel.Application
Set sh = xlApp.Workbooks.Add.Sheets(1)
Set rngTableTopLeft = sh.Range("A1") ' or wherever; doesn't matter
'Put file names and date last modified in Excel sheet
Set FSO = New FileSystemObject
Set fld = FSO.GetFolder("C:\Users\jeacor\Documents")
For Each fil In fld.Files
iFil = iFil + 1
With rngTableTopLeft.Cells(iFil, 1)
.Value = fil.Name
.Offset(0, 1).Value = fil.DateLastModified
End With
Next fil
'Sort them by date last modified using Excel Sort function
With sh.Sort
.SortFields.Add Key:=rngTableTopLeft.Offset(0, 1).Resize(fld.Files.Count, 1), Order:=xlAscending
.SetRange rngTableTopLeft.Resize(fld.Files.Count, 2)
.Apply
End With
'Copy result to Word document
With rngTableTopLeft.Resize(fld.Files.Count, 2)
.EntireColumn.AutoFit
.Copy
End With
Selection.Paste
'Goodbye
xlApp.DisplayAlerts = False 'suppress the "exit without saving?" prompt
xlApp.Quit