Run another macro in another Excel file - excel

Hi so I am building an Excel system to run macro for running several different Macros on other Excel files.
Let's say I have two Excel files: "Parent.xlsx" and "child.xlsx" opened already.
In my Parent.xlsx I am running an VBA script, how can I run a macro called "method1" in my "PERSONAL.XLSB" for my "child.xlsx"
Right now, in my Parent.xlsx, I try to run this macro VBA script:
Workbooks("child.xlsx").Application.Run "PERSONAL.XLSB!method1"
In my PERSONAL.XLSB in Modelue6, I have:
Sub method1()
Dim rTable As Range
Selection.AutoFilter
End sub
Error:
Run-time error'1004':
AutoFilter method of Range class failed
Thank you very much!

You need to qualify and reference the workbook that the code should act on.
Look into the ActiveWorkbook property here: https://learn.microsoft.com/en-us/office/vba/api/excel.application.activeworkbook

Application.Run
In Parent.xlsx (to keep the code, save the file as e.g. Parent.xlsm)
Sub callMethod1()
Dim wb As Workbook
Set wb = Workbooks("child.xlsx")
Application.Run "PERSONAL.XLSB!method1", wb
End Sub
In PERSONAL.XLSB
Sub method1(wb As Workbook)
wb.Activate
If Not Selection Is Nothing Then
If TypeName(Selection) = "Range" Then
If ActiveSheet.AutoFilterMode Then
ActiveSheet.AutoFilterMode = False
End If
Selection.AutoFilter
End If
End If
End Sub

First off, you can't expect macros in a workbook of xlsx format. Make sure that you have eikther xlsm or xlsb as a source. This gets us to the second necessity. The workbook must have been saved to have such a format. You can't call a macro from an open workbook that hasn't been saved (because it doesn't have any format yet).
This is the correct syntax for calling an existing macro in another workbook.
Sub Test_TestCall()
Application.Run "'Personal.xlsb'!Method1"
End Sub
Add arguments to the call in brackets in the sequence required by the called procedure.
You may find it easier, however, to simply set a reference to the other workbook. Here is a step-by-step guide how to set a reference. Once a reference is set you can call all macros in the other project as if they were within the calling workbook. The reference gets saved with the workbook and will still be there when you next open the workbook. The drawback of this (and any other method) is that you can't send a working copy of the calling workbook to third parties unless you send the referenced workbook as well.
The error you get has yet another reason. The Selection is made by the user in the ActiveWorkbook, and since you don't tell Method1 which workbook is active it wouldn't be able to find it, right? However, this problem is best solved by following the most basic of all programming rules: "Avoid the Selection object!" Use the Range object instead. If you absolutely must use the Selection` object then pass it to your procedure as an argument.
Application.Run "'Personal.xlsb'!Method1(Selection)"
' and
Sub method1(MyRange As Range)
Dim rTable As Range
MyRange.AutoFilter
End sub

Related

Trigger a VBA Macro on any opened workbook and sheets with following code

I am using the following code to highlight spelling mistakes in the cell-on-cell change
Sub ColorMispelledCells()
For Each cl In ActiveSheet.UsedRange
If Not Application.CheckSpelling(Word:=cl.Text) Then _
cl.Interior.ColorIndex = 15
Next cl
End Sub
Now, the problem is that every time I have to run this code by pressing f5 and also this only works on a particular workbook (Workbook specific).
So, my question is, what is the process to make a universal/global macro that runs on every workbook and every sheet which is opened, and that too runs internally when the cell is changed? It should not open up with the VBA editor window again and again.
Along with this is there any way where I can make a enable and disable button highlighted on the excel toolbar for this macro?
see image to see my VBA editor project explorer hierarchy
I tried searching on the internet and also saw many sample codes but nob body explained to make a global/universal macro, I am unable to figure out how to develop a global/universal macro that runs automatically on cell changes. which is not bounded to any one workbook but works globally on any opened workbook on all sheets.
I'm interested in your case. I myself is not an expert, so just now I search the internet and playing around and try to "cheat" by making this kind of code - which I'm not so sure if that meets your requirement.
In PERSONAL.XLSB workbook - ThisWorkbook module :
Private WithEvents app As Application
Private Sub app_WorkbookOpen(ByVal Wb As Workbook)
On Error Resume Next
If Wb.Name <> "PERSONAL.XLSB" Then
Set src = Workbooks("PERSONAL.XLSB").VBProject.VBComponents("Sheet1").CodeModule
Set trg = Wb.VBProject.VBComponents("ThisWorkbook").CodeModule
trg.insertlines 1, src.Lines(1, src.countoflines)
End If
End Sub
Private Sub Workbook_Open()
Set app = Application
End Sub
In PERSONAL.XLSB workbook - Sheet1 module :
Private Sub Workbook_SheetChange(ByVal sh As Object, ByVal Target As Range)
If Not Application.CheckSpelling(Word:=Target.Text) Then _
Target.Interior.ColorIndex = 15 Else Target.Interior.ColorIndex = xlNone
End Sub
Source code from this link and this link
The sub in "ThisWorkbook" module of PERSONAL.XLSB will be triggered when opening any workbook. The sub will copy the macro in "Sheet1" module of PERSONAL.XLSB into "ThisWorkbook" module of the open workbook.
The sub in "Sheet1" module of PERSONAL.XLSB is an event handler to any sheet of the active workbook which will be triggered if there is a change on any cell of any sheet.
I noticed that if I edit the sub in the PERSONAL.XLSB to something else, then I open another workbook, the sub will not run. I need to close the Excel application first, then open it again. If I don't do any sub-editing, opening other workbook will trigger the sub to run.
The animation above is opening two xlsx workbook which for sure there can't be any code in that workbook. But because at the time it's opened the macro in PERSONAL.xlsb is triggered (copying a sub to "ThisWorkbook" module of that xlsx workbook), so then there is a sub in that xlsx workbook. If I close this xlsx workbook, it will prompt me if I want to save the workbook. If I click yes, it will complain because it can't be saved as xlsx while there is a macro in "ThisWorkbook" module of this workbook.
Still not sure though if this is the kind that you want. Also maybe what you want is something like this : in whatever computer which has Excel app, it can do the same thing without doing anything before hand. So it's not doable, because to any computer which has Excel app, the macro need to be copied to PERSONAL.XLSB on each computer before hand.
Please note, I don't test to open an xlsm workbook which already has Private Sub Workbook_SheetChange(ByVal sh As Object, ByVal Target As Range) in it's "ThisWorkbook" module. And I think most likely it will throw an error if the opened workbook has already Private Sub Workbook_SheetChange(ByVal sh As Object, ByVal Target As Range) in it's "ThisWorkbook" module

"Run Time error 9 : subscript out of range" when copying

I am trying to copy values from one sheet to another and getting this error
"Run Time error 9 : subscript out of range"
when I run the code below.
Sub updatemultiple()
Workbooks.Open ("C:\Users\akpeko.zigah\Documents\EDC\DESTINATION.xlsm")
Range("B2:F2").Copy
'Application.DisplayAlerts = False
ActiveWorkbook.Close
ActiveSheet.Paste Destination:=Worksheets(“Sheet1”).Range("B2:F2")
End Sub
Any help? I'm stuck.
There are a number of unanswered parentage questions to your workbook/worksheet/cell references but if you are pasting into the same workbook as the one containing the code then something like this should work.
Sub updatemultiple()
Workbooks.Open ("C:\Users\akpeko.zigah\Documents\EDC\DESTINATION.xlsm")
Range("B2:F2").Copy Destination:=THISWORKBOOK.Worksheets(“Sheet1”).Range("B2")
'Application.DisplayAlerts = False
ActiveWorkbook.Close False
End Sub
The problems start with how you are determining the currently active worksheet when you open the external workbook. If it was opened, the active worksheet changed then closed byt another person or process then when you opened it it is going to copy the wrong cells. If the destination worksheet is not contained in the workbook containing the code then the destination is an ambiguous reference that depends upon the current state of the application environment.
A short rewrite of your code (with some made-up references thrown in) might be like the following.
Sub updatemultiple()
Dim wb As Workbook
Set wb = Workbooks.Open("C:\Users\akpeko.zigah\Documents\EDC\DESTINATION.xlsm", ReadOnly:=True)
With wb.Worksheets("Sheet1")
.Range("B2:F2").Copy Destination:=ThisWorkbook.Worksheets(“Sheet1”).Range("B2:F2")
.Close SaveChanges:=False
End With
Set wb = Nothing
End Sub
In short, be as explicit as you can by defining the workbook, worksheet and cell references for each side of every operation. Relying on 'what is active' can only lead to trouble.
If I understood your code correctly, it seems to me that you have the code in one file. When you execute the code, it opens the DESTINATION.xlsm file, copy the cells, close the DESTINATION.xlsm file, then paste the data in the active sheet of the original file that contains the code.
I don't know what data you're copying, but when you copy (manually, not in code) a lot of data from an excel file and then you close it, you get a warning:
There is a large amount of information on the Clipboard. Do you want to be able to paste this information to another program later?
For that reason, I recommend you to paste your copied cells first, and then close the file you're copying from.

Using worksheet codenames for sheets added at runtime in VBA

For various reasons I prefer to use codenames to refer to worksheets in VBA code.
However, when I dynamically add a sheet like this (to a workbook that has just 1 sheet):
Sub example()
Worksheets.Add After:=Worksheets(Worksheets.Count)
'SOME OTHER CODE HERE
Sheet2.Activate
End Sub
... it won't compile because Sheet2 isn't defined as a variable.
Is there a way around this? At the moment I am reverting to using the sheet name:
Sheets("Sheet2").Activate
Is there a way to do it using the codename?
Thanks in advance.
It is a great idea to use codenames in VBA for sheets that exist at compile time. As you know, it allows you to refer to the sheet even if a user changes its name.
However, it's harder to do at runtime. You can do it using VBAProject Properties, if the user has granted access to the VBA project object model. But you can accomplish what you want, ongoing user-proof access to the sheet, simply by setting a WorkSheet variable:
Sub example()
Dim NewWorkSheet1 as Excel.Worksheet
Worksheets.Add After:=Worksheets(Worksheets.Count)
Set Newworksheet1 = ActiveSheet
'SOME OTHER CODE HERE
End Sub
Note that after adding a WorkSheet it is the ActiveSheet.
Also, you can declare the WorkSheet as a project- or module-level variable, if that level of scope is required.

Excel's VBA ActiveWorkbook is "Nothing" when "Enable Editing" from ProtectedView

I have a VBA macro which is called from a spreadsheet function (user defined function, UDF). When the spreadsheet is downloaded from the internet and the user has set "Trust Center" settings accordingly, the spreadsheet will open in so the called "Protected View". The function will not be called. A button "Enable Editing" is shown.
If the button is pressed, the spreadsheet is "trusted" and reopened normally, starting calculation, and hence calling the user defined function.
However, in that VBA function the value of Application.ActiveWorkbook is Nothing. This can be verified in the debugger.
Since I just need to read some properties (like path name) of the spreadsheet, I could alternatively inspect the availability of Application.ActiveProtectedViewWindow which should reference to the protected version of the workbook. In the debugger, this object can be inspected. However, running in release (without debug) the value of Application.ActiveProtectedViewWindow is also Nothing.
Both behaviors - especially the first one - appears to be a bug present in Excel 2010 and 2013 (see also a post at the MSDN forum ).
Question: Is there a way to get hold of properties of the active workbook after it has been enabled for editing?
PS: As a follow up to the nice observation of Siddharth Rout, that "ThisWorkbook" might work: In my case, the macro is not part of the Workbook being openend. The UDF is defined in an XLA. Hence, ThisWorkbook would reference the XLA. I do need to get the ActiveWorkbook (= the workbook calling the UDF) instead of ThisWorkbook (= the workbook running the UDF).
IMPORTANT REQUIREMENT:
My function is called as a user defined function, i.e., execution order is determined by Excel updating the cell.
The function is not part of the workbook being opened. It is part of an XLA.
I cannot add any code to the workbook which is opened.
Summary: The problem can be replicated and there are some possible workarounds. The most promising one - resulting from a chat - is to use ActiveWindow.Parent instead of ActiveWorkbook.
I was able to replicate the problem.
I tried
Private Sub Workbook_Open()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ActiveWorkbook Is Nothing)
End Sub
And I got True
However, then I tried this and it gave me False
Private Sub Workbook_Open()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ThisWorkbook Is Nothing)
End Sub
Now answering your question...
Question: Is there a way to get hold of properties of the workbook after it has been enabled for editing?
Yes. Use ThisWorkbook instead of ActiveWorkbook
Followup From Comments
Once the workbook completely loads after you exit the Protected Mode, you would be able to access the ActiveWorkbook object. To test this, put this code in the protected file.
Private Sub Workbook_Activate()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ActiveWorkbook Is Nothing)
End Sub
You will notice that you get a False
So once your workbook loads, your add-in can use ActiveWorkbook to interact with the opened file.
Here is another test
Private Sub Workbook_Activate()
MsgBox ActiveWorkbook.Path
End Sub
This is what I got the moment, I exit the Protected Mode
FOLLOWUP FROM CHAT
Using ActiveWindow.Parent.Path instead of ActiveWorkbook.Path would solve the problem.
I had this same issue today, and neither the accepted answer nor any other answer that I could find on this page or through searching the Google-verse worked for me. I'm using the version of Excel within Office 365, and I figured that was at the root of the problem.
I eventually came to a solution after finding a Microsoft Excel 2010 resource and hitting the old try-fail cycle for a few hours. Here's what I got:
Option Explicit
Public WithEvents oApp As Application
Private bDeferredOpen As Boolean
Private Sub Workbook_Open()
Set oApp = Application
End Sub
Private Sub oApp_WorkbookActivate(ByVal Wb As Workbook)
If bDeferredOpen Then
bDeferredOpen = False
Call WorkbookOpenHandler(Wb)
End If
End Sub
Private Sub oApp_WorkbookOpen(ByVal Wb As Workbook)
Dim oProtectedViewWindow As ProtectedViewWindow
On Error Resume Next
'The below line will throw an error (Subscript out of range) if the workbook is not opened in protected view.
Set oProtectedViewWindow = oApp.ProtectedViewWindows.Item(Wb.Name)
On Error GoTo 0
'Reset error handling
If oProtectedViewWindow Is Nothing Then
bDeferredOpen = False
Call WorkbookOpenHandler(Wb)
Else
'Delay open actions till the workbook gets activated.
bDeferredOpen = True
End If
End Sub
Private Sub WorkbookOpenHandler(ByVal Wb As Workbook)
'The actual workbook open event handler code goes here...
End Sub
The difference between the 2010 solution and mine is that I had to call Workbook_Open and explicitly set the oApp variable there, because without that assignment neither the oApp_WorkbookActivate nor oApp_WorkbookOpen functions would fire when I opened the file.
Figured that someone else might be able to benefit from this, so I posted it, despite the fact that the most recent update to this thread is better than 2 years old.
Best.
Try using Application.Caller.Parent.Parent instead of Application.Activeworkbook
This is not a complete answer to the original question, but a (dirty) workaround for a problem related to this.
I needed ActiveWorkbook to infer the workbooks path, that is ActiveWorkbook.Path.
An alternative to using ActiveWorkbook.Path is to check for Application.RecentFiles(1).Path which is the path of the most recently opened file. In many cases this will be the workbook for which the user just has "Enabled Editing". However, of course, this method may fail: In the case the used opened another sheet, then enabling the previously opened sheet.
(Note: ActiveWorkbook.Path give the path of the folder, while Application.RecentFiles(1).Path gives the complete path of the file, so there has to be some post-processing).
I know it's old thread, but i came across the same issue and i found solution ;)
The only way to go around it, is to use variable type Workbook
Dim wbk as Workbook
Set wbk = Application.ProtectedViewWindows(index).Workbook
Warning:
ActiveSheet returns Nothing when active window is protected too.
Dim wsh As Worksheet
Set wsh = wbk.Worksheets(index)
Try this code it works.
If (UCase(ActiveWorkbook.Name) = ucase("<YOUR XLA NAME WITH EXTENSION>")) Then
End
End If
Set wbObj = ActiveWorkbook
First time when you run the macro, it just ends without doing anything. Second time it picks up the proper file.

What to do to avoid VBA function executed in the wrong work book

I have several excel files that have timer and macros executing. But a big problem is when workbook A's macro is called, while workbook B is active. The macro is executed in the wrong book and failed.
Do I need to put windows().active at the beginning of every function?
If I have different modules how do I pass this workbook name to all of them?
This seems excessive and not right. Is there any good solution to this problem?
Looking forward to your answers
You are on the right track with this
2.If I have different modules how do I pass this workbook name to all of them
I assume that your macro is using the ActiveWorkbook property, or just using Worksheet properties like Range without qualifying them?
Instead of using ActiveWorkbook use ThisWorkbook. Instead of using Range use ThisWoorkbook.Worksheets(1).Range and so forth. Otherwise the macro will assume that the active worksheet is the one you want.
Sub MyMacro
Range("A1").Text = "Test"
End Sub
Try
Sub MyMacro(ByVal oWorksheet as Worksheet)
oWorksheet.Range("A1").Text = "Test"
End Sub
Then pass the worksheet object as a parameter.
You may also find the ThisWorkbook object useful - it is the workbook the macro resides in, or the Application.Caller object, which is the object calling the current macro, for example the Range object if it is a cell formula, or presumably the timer object in your case.
If your macros behave the way you described it, they probably depend explicitly or implicitly on
ActiveWorkbook
or
ActiveSheet
Those kind of dependencies should be avoided, if possible. The macro recorder produces such code, you should change it immediately whenever you have recorded a macro.
For example, if you have some code like
s = Range("A1").Value
Excel implicitly changes that to
s = ActiveSheet.Range("A1").Value
One can avoid that by accessing all cells, ranges, workbook parts etc. by explicitly using the right sheet or workbook object:
Dim sh as Worksheet
Set sh = .... ' Initialize sh the first time where the sheet is created or loaded
'later on:
s = sh.Range("A1").Value
By using a parameters of the form
sh as Worksheet, wb as workbook
for your subs and functions, you can pass the right sheet and workbook between modules, which answers your second question. And if you need access to the workbook where your macro resides, use ThisWorkbook.
I'd go one further... make sure your code doesn't have
Selection
or
ActiveCell
objects within them. You would need to rewrite these using the Range object.

Resources