I need to call and execute VBA in an Excel spreadsheet from an Access form+button using VBA.
I have several Excel spreadsheets that are connected to my Access DB. I created a separate DB with VBA (C:\CDR & Project Inventory Reports\RebuildDB) to refresh and rebuild my source databases and report workbooks. I also created a separate workbook to refresh all of my other reports (C:\CDR & Project Inventory Reports\RefreshReports). Module 15 of that .xlsm will call all of my other modules to refresh those reports.
Prior to executing the Excel portion of the code is all of my code pertaining to rebuilding the DB. Currently, the Excel portion looks like this:
Dim oXLApp As Object
Set oXLApp = CreateObject("Excel.Application")
oXLApp.Visible = True
oXLApp.Workbooks.Open ("C:\CDR & Project Inventory Reports\RefreshReports.xlsm")
Debug.Print "Open report refresh code"
Debug.Print "refreshing all spreadsheets"
oXLApp.Run "RefreshReports.Module15" 'refreshes all CDR and Proj Inv spreadsheets
DoEvents
Debug.Print "Refresh completed"
oXLApp.ActiveWorkbook.Close (True)
oXLApp.Quit
Set oXLApp = Nothing
Debug.Print "Release object"
The problem I'm having is that oXLApp.Run doesn't actually execute the VBA it needs to. It calls it, then immediately closes. Is there any other way to call it that would result in the code actually running?
oXLApp.Run "RefreshReports.Module15"
should be
oXLApp.Run "RefreshReports.xlsm!NameOfMySub" '// Change sub name as required
As it stands, your code isn't running any VBA from Excel because you haven't referred to a specific sub. I presume you have error handling switched off as this should be rather noticeable.
You should fully qualify the sub to be executed, by also giving the module name
so
oXLApp.Run "RefreshReports.Module15"
should be
oXLApp.Run "'RefreshReports.xlsm'!Module15.NameOfMySub"
If you use
oXLApp.Run "'RefreshReports.xlsm'!NameOfMySub"
and
the procedure NameOfMySub is declared in more than one module
you will get and error message.
You could also re-write your code thus:
Dim oXLApp As Object
Dim Wkbk as workbook
Set oXLApp = CreateObject("Excel.Application")
oXLApp.Visible = True
set Wkbk = oXLApp.Workbooks.Open ("C:\CDR & Project Inventory Reports\RefreshReports.xlsm")
Debug.Print "Open report refresh code"
Debug.Print "refreshing all spreadsheets"
oXLApp.Run "'" & Wkbk.Name & "'!Module15.NameOfMySub"
' Note use of single speechmarks arond workbook name
Related
I have several scripts that were working fine until recently. They were running through a task scheduler and when the task scheduler opens the script it gets the following:
line 20 is this:
'Execute Macro Code
ExcelApp.Run MacroPath
here is the full script:
'Input Excel File's Full Path
ExcelFilePath = "C:\vba-files\task_sched\task_modules.xlsm"
'Input Module/Macro name within the Excel File
MacroPath = "Module1.get_data"
'Create an instance of Excel
Set ExcelApp = CreateObject("Excel.Application")
'Do you want this Excel instance to be visible?
ExcelApp.Visible = false
'Prevent any App Launch Alerts (ie Update External Links)
ExcelApp.DisplayAlerts = False
'Open Excel File
Set wb = ExcelApp.Workbooks.Open(ExcelFilePath)
'Execute Macro Code
ExcelApp.Run MacroPath
'Save Excel File (if applicable)
wb.Save
'Reset Display Alerts Before Closing
ExcelApp.DisplayAlerts = True
'Close Excel File
wb.Close
'End instance of Excel
ExcelApp.Quit
'Leaves an onscreen message!
MsgBox "Your Automated Task successfully ran at " & TimeValue(Now), vbInformation
macro settings are enabled as seen here:
when the workbook is opened and the macro is ran manually it works fine.
it is just erroring out when ran through the script
I cannot understand how could it work fine before... Basically, your VBScript code should fully qualify the procedure to be called. I mean, it also needs the workbook name (or full name) where the macro to be called exists. Please, try:
ExcelApp.run "'" & ExcelFilePath & "'" & "!" & MacroPath
"'" characters make the code running well even if the file path contains spaces...
In theory, using the workbook full name will automatically open the workbook keeping the macro, if not open... I did not test it on automation from VBScript. It works in this way when make the call from Excel (tested)
(Minimum requirements: Excel 2010 and Windows 7)
I have managed to use Bill Manville’s answer found in MSDN with minor changes. The suggested recursive code basically uses files’s Workbook_Open to create a separate instance and taht instance opens the file as editable with no prompts for read-only access.
Private Sub Workbook_Open()
Dim oXcl As Excel.Application
If Workbooks.Count > 1 Then
ThisWorkbook.Saved = True
ThisWorkbook.ChangeFileAccess xlReadOnly
Set oXcl = CreateObject("Excel.Application")
oXcl.ScreenUpdating = False
oXcl.Visible = True
oXcl.Workbooks.Open fileName:=ThisWorkbook.FullName, ReadOnly:=False
AppActivate oXcl.Caption
ThisWorkbook.Close SaveChanges:=False
Else
Call Continue_Open
End If
End Sub
The code works very well when Excel is already running as it creates a new instance of Excel and if a new Excel file is opened, it goes to a different Excel instance (running prior to it). But if the file with the Workbook_Open is the one that starts Excel, any further Excel files opened by double-clicking open within that Excel instance as it is the earliest run instance thus ceasing to be separate.
I have got as far as to be able to tell (Windows) whether that file starts Excel by using
Function NumberOfExcelInstances()
strComputer = "."
Set objWMI = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set proc = objWMI.ExecQuery("Select * from Win32_Process Where Name = 'Excel.exe'")
NumberOfExcelInstances = proc.Count
End Function
But I have not been able to find a way to tell NOT to use that Excel instance when opening new files. Any code should be bundled inside the Excel file with the Worbook_Open code.
How could I possibly include VBA code inside a file so that it opens in a separate Excel instance even when that file is the one that fires Excel?
After research on code at Application level, a working solution have been found. I am posting it, in case it is of interest to someone else.
When workbook opens the fist time it sets a workbook open event subroutine at Application level (rather than at Workbook level).
When a new workbook opens, the sub at Applictaion level opens a new instance with the workbook to be kept separate by recursivity - closes that workbook in the application instance that checks being separate thus removing the event handler from the application instance and sets that event handler and code on the newly created application instance.
All relevant code is included and it needs to be in three different modules.
1-a VBA Class Module named cXlEvents is created with the following code:
'VBA Class Module named cXlEvents
Public WithEvents appWithEvents As Application
'Instance variables
Dim sEventSetterPath As String
Dim sEventSetterName As String
Private Sub appWithEvents_WorkbookOpen(ByVal Wb As Workbook)
Call NewAppInstance(Wb, sEventSetterPath, sEventSetterName)
End Sub
2-ThisWorkbook Module includes:
'1-ThisWorkbook VBA Module calling events at
'Workbook level.
'2-At Workbook Open set Application level event
'handler and then instance code by calling subs
'held in VBA standard module.
Private Sub Workbook_Open()
Call SetEventHandler
Call NewAppInstance(Me)
End Sub
'Code to call "undo" special settings upon opening
'when file closes
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call UndoSettings
End Sub
3-All code necessary to create an instance at Workbook level Open event from a class which will end up at Application level is in a standard VBA Module:
'In a VBA standard Module
Dim oXlEvents As New cXlEvents
Sub SetEventHandler()
If oXlEvents.appWithEvents Is Nothing Then
Set oXlEvents.appWithEvents = Application
End If
End Sub
Sub NewAppInstance(wbWbook As Workbook, Optional sEventSetterPath As String, Optional sEventSetterName As String)
Dim oXcl As Excel.Application
Dim wbEventSet As Workbook
Dim lCaseNum As Long
Dim sResetMacro As String: sResetMacroName = "UndoSettings"
'Set instance variables
sEventSetterPath = ThisWorkbook.FullName
sEventSetterName = ThisWorkbook.Name
If wbWbook.ReadOnly And wbWbook.FullName = sEventSetterPath Then
MsgBox "Already open - please use open file.", , "WARNING"
wbWbook.Close False
Exit Sub
End If
If Workbooks.Count > 1 Then
If wbWbook.FullName <> sEventSetterPath Then
lCaseNum = 1
Set wbEventSet = Workbooks(1)
wbEventSet.Save
Application.Run "'" & sEventSetterName & "'!'" & sResetMacro & "'"
Else
lCaseNum = 2
Set wbEventSet = wbWbook
wbEventSet.Saved = True
End If
wbEventSet.ChangeFileAccess xlReadOnly
Set oXcl = CreateObject("Excel.Application")
oXcl.Workbooks.Open Filename:=sEventSetterPath, ReadOnly:=False
oXcl.Visible = True
Set oXlEvents.appWithEvents = Nothing
Select Case lCaseNum
Case Is = 1
AppActivate Application.Caption
Case Is = 2
AppActivate oXcl.Caption
End Select
wbEventSet.Close False
Else
Call Continue_Open
End If
End Sub
Sub Continue_Open()
'Code with special settings and procedures required for the workbook
End Sub
Sub UndoSettings()
'Code to "undo" any special settings when workbook opened
End Sub
I have a huge Excel spreadsheet that I need to allow access to a large set of users so they can manipulate it for their customers, but I don't want them to be able to overwrite the original file (a variable easily set in Excel) or save their file outside the current folder - so I want to force them in a "saveas" mode, and force the file to be saved in that folder. Otherwise, they won't be able to save. I'm not much of a VBA person, and I've found a lot of examples that may work, but nothing seems to be exactly what I need or maybe I'm not smart enough to figure it out. I found this code, but I'm not sure it FORCES the issue. Help?
I've tried to manage this in GPOs but everything seems to give them access to download the folder and save in other places.
Sub ExampleToSaveWorkbookSet()
Dim wkb As Workbook
'Adding New Workbook
Set wkb = Workbooks.Add
'Saving the Workbook
wkb.SaveAs "C:\WorkbookName.xls"
'OR
'wkb.SaveAs Filename:="C:\WorkbookName1.xls"
End Sub
Expected output is the amended Excel file saved in the original directory with a different name, or not at all.
Here's a macro that runs on open and immediately saves as .xlsx to a user location you can specify. Unfortunately the original needs to be .xlsm to store a macro.
This macro is to be located in the "ThisWorkbook" object. It will exit before making a copy when you open the workbook.
Private Sub Workbook_Open()
Dim wb As Workbook
Set wb = ActiveWorkbook
vWbName = wb.Name
vUserProf = Environ("USERPROFILE")
vx = InStr(1, vUserProf, "Users\")
If "<Use your own profileID>" = Mid(vUserProf, vx + 6) Then Exit Sub
vDir = vUserProf & "\Downloads\"
vWbName = Left(vWbName, Len(vWbName) - 5) & ".xlsx"
wb.SaveAs vDir & vWbName, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
MsgBox "You are now using a copy of the original"
End Sub
I have a process that generates multiple spreadsheets. It doesn't save them, which is fine because Normally they would be saved manually.
I want to write a macro that accesses each sheet sequentially, perform some magic and then save and close the sheet.
When using Excel 2016, this is not an issue.
But I have Excel 2010 at work, and it cant see any of the work books.
With Excel 2016 this method works:
Workbooks("Book3").sheets("B3T1").activate
ActiveWindow.WindowState = xlMaximized
with Excel 2010 this is the message I receive:
Workbooks("Book3").sheets("B3T1").activate
runtime error '9'. Subscript out of Range
What is the method I should use to open an external workbook
Thanks, in advance
Assuming the workbook being accessed is already saved at a location.
This will open the external Files and access them. The macro will also close and save the file.
Dim wb As Workbook
Dim fileloc As String
fileloc = "" & "C:namenamename" & ".xlsm" & ""
Set wb = Workbooks.Open(fileloc)
wb.Worksheets("Sheet1").activate
wb.Close savechanges:=True
The worksheet isn't saved by the process that generates the work sheet. That is a manual operation. There are 100 worksheets and I wanted a way to avoid having to maximize each sheet/process it/save it.
The GetObject method finally worked:
For ptr = 1 To 100
Set xlApp = GetObject("Book" & ptr).Application
xlApp.Application.Workbooks("Book" & ptr).Sheets(1).Activate
do some processing
xlApp.Application.Quit
next
A one hour+ process is now reduced to 1 second
I have a scenario. I am using a set of excel files for reporting. All of these excels are macro enabled and have a "login" function to connect to server. To fix the security issue, we have to update "login" function.
Is there a way to write a macro to replace this function in all such excel files? basically a macro to update a macro!!
Enable macro security option:
Then you need a code like this:
Sub AddMacro()
Dim xlbook As Workbook
Set xlbook = ActiveWorkbook 'Assign workbook to have macro added here
Dim xlmodule As Object
Set xlmodule = xlbook.VBProject.VBComponents.Add(1)
Dim strCode As String
strCode = _
"Function MyFunction()" & vbCr & _
"'some function code here" & vbCr & _
"End Function"
xlmodule.CodeModule.AddFromString strCode
End Sub
I have had a similar problem in the past. Basically needed to update functions when new rates came in, to all users.
My solution was to use .xlam files, basically push the updates when users reboot their computers.
If you don't have multiple users, simply having the macro a module in the "PERSONAL.XLSB", will simply require updating that macro, which would then work on every workbook.