Access VBA creating unqualified reference while looping through excel sheets - excel

I'm trying to loop through Excel sheets in Access and keep ending up with an unqualified reference to excel.
Dim ExcelApp As Excel.Application
Dim ExcelWorkbook As Workbook
Dim ExcelWorkSheet As Worksheet
Dim i As Integer
Dim salesfile As String
Set ExcelApp = CreateObject("Excel.Application")
salesfile = "C:\filename"
Set ExcelWorkbook = ExcelApp.Workbooks.Open(salesfile)
With ExcelApp
With ExcelWorkbook
For Each ExcelWorkSheet In .Worksheets
i = i + 1
Next ExcelWorkSheet
End With
End With
ExcelWorkbook.Save
ExcelWorkbook.Close
Set ExcelWorkSheet = Nothing
Set ExcelWorkbook = Nothing
Set ExcelApp = Nothing
I've tried moving the qualifications, using .quit, etc., but still end up with that one excel.exe process in task mananger. The issue is definitally in the for each loop. If I close before then, it's good.

It looks like you are closing the excel workbook, but not excel itself. What if you tried something like
ExcelApp.Close

Change this:
i = i + 1
to this:
ExcelWorkSheet.Activate

In VBA even though you set your objects to Nothing, the resources aren't truely released until the variables go out of scope, and in this case this is the end of your sub/function. You can call this code from inside another sub or function and as long as the excel app is locally declared when it goes out of scope it will be removed from the running processes.
There's an entry in Eric Lippert's blog that explains the reasons why.

Related

Run Excel VBA in Access

I have been researching this a great deal and I am not finding any leads to how this would work.
I have written code in Excel that I want to run in MS Access. I have pasted the code I wish to run in Access.
All the examples or information I have found is from 2003 Access. I am using 2016 Access.
The Excel code
Public Function getworkbook()
' Get workbook...
Dim ws As Worksheet
Dim Filter As String
Dim targetWorkbook As Workbook, wb As Workbook
Dim Ret As Variant
Application.DisplayAlerts = False
Sheets("DATA").Delete
' Sheets("DATA").Cells.Clear
Set targetWorkbook = Application.ActiveWorkbook
' get the customer workbook
Filter = "Text files (*.xlsx;*.xlsb),*.xlsx;*.xlsb"
Caption = "Please Select an input file "
Ret = Application.GetOpenFilename(Filter, , Caption)
If Ret = False Then Exit Function
Set wb = Workbooks.Open(Ret)
wb.Sheets(1).Move After:=targetWorkbook.Sheets(targetWorkbook.Sheets.Count)
' ActiveSheet.Paste = "DATA"
ActiveSheet.Name = "DATA"
ThisWorkbook.RefreshAll
' Application.Quit
Application.DisplayAlerts = True
End Function
Code I found and tried to use in Access.
Public Function runExcelMacro(wkbookPath)
Dim XL As Object
Set XL = CreateObject("Excel.Application")
With XL
.Visible = False
.displayalerts = False
.Workbooks.Open wkbookPath
'Write your Excel formatting, the line below is an example
.Range("C2").value = "=1+2"
.ActiveWorkbook.Close (True)
.Quit
End With
Set XL = Nothing
End Function
There are few concepts you need to deal with first.
Library references and scope
Your original code was written in Excel. Therefore, in that VBA project, it has Excel object referenced. In your Access VBA project, that is not referenced. You can compare this by looking at Tools -> References.
That brings us to the concept of "early-binding" and "late-binding". When you type in things like Range., you get VBA's intellisense to tell you what you can do with a Range or whatever. But in Access, you don't have Excel object library referenced by default. Therefore, Range. will not yield intellisense and you can't run the code because Access does not have Range in its object model and your VBA project mostly likely don't have a reference that has it.
Therefore, your code need to be adjusted to run late-bound if you do not want to add reference to Excel object model, and you most likely do want that anyway.
Unqualified Reference
Your original Excel code contains unqualified references to various global objects that are available in Excel's object model.
Application.DisplayAlerts = False
...
Sheets("DATA").Delete
...
Set wb = Workbooks.Open(Ret)
...
Those won't necessarily work consistently in VBA projects hosted by other hosts other than Excel and most certainly won't work in late-bound code. Furthermore, if you elect to add a reference to Excel's object model, you still end up leaking Excel instance which can cause ghost instances because unqualified references to the global objects will implicitly create an Excel instance that you can't interact and that can also cause other runtime error down the path. To make your code more late-bindable, you need something like:
Set ExcelApp = CreateObject("Excel.Application")
ExcelApp.DisplayAlerts = False
...
Set MyBook = ExcelApp.Workbooks("Whatever")
MyBook.Sheets("DATA").Delete
...
Set wb = ExcelApp.Workbooks.Open(Ret)
...
Note how all global objects that you could have accessed in a Excel-hosted context now have to be a variable on its own. Furthermore, you won't have access to ThisWorkbook or even Sheet1 in other VBA projects because Excel is no longer the host. You must adjust accordingly.
Switching between early-binding & late-binding
Early-bound code makes it much easier for you to develop since you get full intelisense and object browser helping you write the code. However, when referencing other object models, you might want to distribute your VBA code using late-binding to avoid versioning problems and broken references. But you can have best from both worlds:
#Const EarlyBind = 1
#If EarlyBind Then
Dim ExcelApp As Excel.Application
#Else
Dim ExcelApp As Object
#End If
Set ExcelApp = CreateObject("Excel.Application")
This illustrates the use of conditional compilation argument to allow you to have ExcelApp variable that can be either Excel.Application (aka early-bound) vs. Object (aka late-bound). To change, you simply change the Const LateBind line between 0 or 1.
First, to clear up terminology:
VBA is a separate language and not tied to any MS Office application. Under Tools\References, you will see Visual Basic for Applications is usually the first checked object. What differs between running VBA inside Excel, Access, Word, Outlook, etc. is the default access to their object library. Specifically:
Only Excel sees Workbook, Worksheet, etc. without defining its source
Only Access sees Forms, Reports, etc. without defining its source
Only Word sees Documents, Paragraphs, etc. without defining its source
When running a foreign object library inside an application, such as MS Access accessing Excel objects, you must define and initialize the foreign objects via reference either with early or late binding:
' EARLY BINDING, REQUIRES EXCEL OFFICE LIBRARY UNDER REFERENCES
Dim xlApp As Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
Set xlApp = New Excel.Application
Set wb = xlApp.Workbooks.Open(...)
Set ws = wb.Worksheets(1)
' LATE BINDING, DOES NOT REQUIRE EXCEL OFFICE LIBRARY UNDER REFERENCES
Dim xlApp As Object, wb As Object, ws As Object
Set xlApp = CreateObject("Excel.Application")
Set wb = xlApp.Workbooks.Open(...)
Set ws = wb.Worksheets(1)
With that said, simply keep original code nearly intact but change definitions and initializations. Notably, all Application calls now point to Excel.Application object and not to be confused with Access' application. Plus, best practices of avoiding .Select/ .Activate/ Selection/ ActiveCell/ ActiveSheet/ ActiveWorkbook.
Public Function getworkbook()
' Get workbook...
Dim xlApp As Object, targetWorkbook As Object, wb As Object, ws As Object
Dim Filter As String, Caption As String
Dim Ret As Variant
Set xlApp = CreateObject("Excel.Application")
Set targetWorkbook = xlApp.Workbooks.Open("C:\Path\To\Workbook.xlsx")
xlApp.DisplayAlerts = False
targetWorkbook.Sheets("DATA").Delete
' get the customer workbook
Filter = "Text files (*.xlsx;*.xlsb),*.xlsx;*.xlsb"
Caption = "Please Select an input file "
Ret = xlApp.GetOpenFilename(Filter, , Caption)
If Ret = False Then Exit Function
Set wb = xlApp.Workbooks.Open(Ret)
wb.Sheets(1).Move After:=targetWorkbook.Sheets(targetWorkbook.Sheets.Count)
Set ws = targetWorkbook.Worksheets(targetWorkbook.Sheets.Count)
ws.Name = "DATA"
targetWorkbook.RefreshAll
xlApp.DisplayAlerts = True
xlApp.Visible = True ' LAUNCH EXCEL APP TO SCREEN
' xlApp.Quit
' RELEASE RESOURCEES
Set ws = Nothing: Set wb = Nothing: Set targetWorkbook = Nothing: Set xlApp = Nothing
End Function
By the way, above can be run in any MS Office application as no object of the parent application (here being MS Access) is used!

Unable to read excel file using ssis Script Source

I have a case where I need to read an excel file with filtered rows using SSIS.
I've started testing the process but all I get when I look in my table is "System.__ComObject"
I'm sure I doing something stupid.
Thanks
Public Overrides Sub CreateNewOutputRows()
Dim xlApp = New Excel.Application
Dim wb As Microsoft.Office.Interop.Excel.Workbook
Dim rw As Excel.Range
xlApp.DisplayAlerts = False
wb = xlApp.Workbooks.Open("C:\PosData\test.xlsx")
Dim visible As Excel.Range = wb.Sheets("Data").UsedRange.SpecialCells(Excel.XlCellType.xlCellTypeVisible, Type.Missing)
For Each rw In visible.Rows
Output0Buffer.AddRow()
Output0Buffer.Column = rw.Cells(1, 1).ToString
Next
Output0Buffer.SetEndOfRowset()
End Sub
That happens sometimes when using the Interop.
All objects from Excel, in this case, are indeed COM Objects.
Use Cells(1,1).Value or Cells(1,1).Value2 or Cells(1,1).Text. Wich fits best for you.
(Maybe you need to Cast or Convert the Cells to Range first)

Excel VBA Run-time error '424': Object Required when trying to copy TextBox

I'm attempting to copy the contents of a text box from one workbook to another. I have no problem copying cell values from the first workbook to the 2nd, but I get an object required error when I attempt to copy the text box. This macro is being run from the workbook containing the data I want copied. Using Excel 2007 Code:
Sub UploadData()
Dim xlo As New Excel.Application
Dim xlw As New Excel.Workbook
Set xlw = xlo.Workbooks.Open("c:\myworkbook.xlsx")
xlo.Worksheets(1).Cells(2, 1) = Range("d4").Value 'Copy cell content (this works fine)
xlo.Worksheets(1).Cells(2, 2) = TextBox1.Text 'This gives me the object required error
xlw.Save
xlw.Close
Set xlo = Nothing
Set xlw = Nothing
End Sub
Thanks for any help.
The problem with your macro is that once you have opened your destination Workbook (xlw in your code sample), it is set as the ActiveWorkbook object and you get an error because TextBox1 doesn't exist in that specific Workbook. To resolve this issue, you could define a reference object to your actual Workbook before opening the other one.
Sub UploadData()
Dim xlo As New Excel.Application
Dim xlw As New Excel.Workbook
Dim myWb as Excel.Workbook
Set myWb = ActiveWorkbook
Set xlw = xlo.Workbooks.Open("c:\myworkbook.xlsx")
xlo.Worksheets(1).Cells(2, 1) = myWb.ActiveSheet.Range("d4").Value
xlo.Worksheets(1).Cells(2, 2) = myWb.ActiveSheet.TextBox1.Text
xlw.Save
xlw.Close
Set xlo = Nothing
Set xlw = Nothing
End Sub
If you prefer, you could also use myWb.Activate to put back your main Workbook as active. It will also work if you do it with a Worksheet object. Using one or another mostly depends on what you want to do (if there are multiple sheets, etc.).
I think the reason that this is happening could be because TextBox1 is scoping to the VBA module and its associated sheet, while Range is scoping to the "Active Sheet".
EDIT
It looks like you may be able to use the GetObject function to pull the textbox from the workbook.
The issue is with this line
xlo.Worksheets(1).Cells(2, 2) = TextBox1.Text
You have the textbox defined at some other location which you are not using here. Excel is unable to find the textbox object in the current sheet while this textbox was defined in xlw.
Hence replace this with
xlo.Worksheets(1).Cells(2, 2) = worksheets("xlw").TextBox1.Text

Populate Word Combobox with Excel data through VBA

I'm new to VBA, so I'm struggling with this for a couple of days now.
I have a combobox in Word with contacts, I also have an excel file(contacts.xls) with one column (names of the all the contacts). When I open the Word-document the Macro has to fill in the combobox with all the names from the excel file.
Is it possible to send me a working example of this for word 2007? Seems quite simple, but just can't get this to work...
Thanks in advance!
If you intend on reading from an Excel file in Word you are going to have to open it. It will use code like this:
Dim oExcel As Object
Dim oBook As Object
Dim oSheet As Object
'Start a new workbook in Excel
Set oExcel = CreateObject("Excel.Application")
Set oBook = oExcel.Workbooks.Open("FileName.xlsx")
You will also probably want to go to Tools->References in the VB project and find whatever Excel library is on your machine (and check it of course). There are ways around this if needed, but it is easier.
You then can read the values from the workbook:
oBook.Worksheets(1).cells(1,1)
I'm not totally sure what the syntax to get it into the combo box is. Look up "combobox object members" in the vbe in word. There will be a list property, or something like that.
Sorry, I'm on a linux machine right now, so I can't debug exact code for you.
I have more full code for you now:
Option Explicit
Sub TestDropDownFromExcel()
Dim counter As Long
Dim xlApp As Excel.Application
Dim xlBook As Workbook
Dim oCC As ContentControl
Set oCC = ActiveDocument.ContentControls(1)
Set xlApp = CreateObject("Excel.Application")
Set xlBook = xlApp.Workbooks.Open("C:\Path\To\MyFile.xlsx")
If xlBook Is Nothing Then
Exit Sub
End If
oCC.DropdownListEntries.Clear
For counter = 1 To 10
oCC.DropdownListEntries.Add Text:=xlBook.Worksheets(1).Cells(counter, 1), Value:=CStr(counter)
Next counter
xlApp.Visible = True
Set xlBook = Nothing
Set xlApp = Nothing
End Sub
Be aware that there is very little error checking going on here. Also, rather than the call to .Visible at the end, you can simply call .Close if you do not want the user to see it (for the final project, this is probably preferable. The deferencing (= Nothing) is good practice to have, but VBA cleans up automatically at the end of execution. Obviously I am assuming tyou want the first dropdown (ContentCOntrols(1)) but this may not be true.

How to get reference to an open Excel spreadsheet from Word?

I have some VBA code that copies stuff from Excel and pastes it into Word. The problem I'm having is how to open the spreadsheet. I can open it using an absolute path reference by
Workbooks.Open "C:\path\filename.xls"
I would prefer to reference the spreadsheet using a relative path reference. I was able to find code for relative path references from an Excel workbook to another one but it doesn't seem to work if you're doing it from Word.
Add a reference to Excel object library, then create an object in code and use that object to control an instance of Excel. Just make sure to avoid things like ActiveWorkbook, just in case.
After adding the reference:
Sub DoStuffWithExcelInWord()
Dim xl As Excel.Application
Dim wkbk As Excel.Workbook
Dim wk As Excel.Worksheet
Set xl = CreateObject("Excel.Application")
Set wkbk = xl.Workbooks.Open("C:\test.csv")
Set wk = wkbk.Sheets(1)
Debug.Print wk.Cells(1, 1).Value
xl.Quit
Set wk = Nothing
Set wkbk = Nothing
Set xl = Nothing
End Sub
You can create something very similar using Excel to automate Word too, if that's more of what you're looking for.

Resources