Calling a Macro in a VBA-locked xlsm file - excel

Im currently trying to write a "controller" File with a macro, that opens other files and just calls 3 Macros in each file.
All these files have the same structure.
My Problem is that I can't see the VBA structure, but I do know the Macronames as they are shown to me.
But everytime I try to call these macros im getting the following Error Message:
Runtimeerror 1004 The Makro !xxxx can't run. It's maybe not available
in this file or all macros have been deactivated.
Macros are activated. If I click the button in that File the macro that gets triggered works perfectly fine.
Any suggestions welcome
Thanks
I've tried everything with wrong filenames with false characters and single commatas.
The Opening of the File via
Set wb = Workboos.open
works perfectly fine, so the file name probably can't be a part of the problem.

You need to specify the name of the workbook you are calling the sub from.
Suppose you have these subs in both workbooks 1 & 2:
Public Sub macro1()
MsgBox "macro1 launched from " & ThisWorkbook.Name
End Sub
Public Sub macro2()
MsgBox "macro2 launched from " & ThisWorkbook.Name
End Sub
Now you can call the following sub from your controller macro:
Sub launcher()
Dim wb As Workbook
Set wb = Workbooks.Open("C:\Users\<username>\Desktop\Workbook1.xlsm")
Application.Run wb.Name & "!" & "macro1" 'the distant macro which you know the name
End Sub
Works perfectly

Answer to the riddle: Even tho I thought that I checked the File Name with 'Filename.xlsm', the problem resolved when I removed all dashes from the Filenames!

Related

Run Excel VBA macro from another Excel macro-Error: Can't find PERSONAL.XLSB

I'm trying to run a Macro across multiple files. I have used this script before and it worked, but maybe I have changed something inadvertently? The problem I'm having is that I get an error message that "PERSONAL.XLSB cannot be found, is it possible it was moved..." However, I'm using the full path of the .xlsb file and I am running the macro from that file.
This is my script:
'Sub SHELLforMacros()
Dim wbMatrix As Workbook
Dim strFileName As String
Dim strPath As String
Dim strExt As String
Dim objWorkbook As Workbook
Dim ws As Worksheet
'This is the folder of files it needs to run through:
strPath = "C:\Users\myname\Desktop\All_mricgcm3_files\45\Fall45\test\"
strExt = "csv"
strFileName = Dir(strPath & "*." & strExt)
While strFileName <> ""
Set wbMatrix = Workbooks.Open(strPath & strFileName)
'Set objWorkbook = ActiveWorkbook
Application.Run "C:\Users\myname\AppData\Roaming\Microsoft\Excel\XLSTART\PERSONAL.XLSB'!Graph_NEW"
wbMatrix.ActiveWorkbook.Save
wbMatrix.Close SaveChanges:=True
strFileName = Dir
Wend
End Sub
I have also tried putting the PERSONAL.XLSB file into the same folder as the files it needs to run (and changing the path to reflect that). And I tried to run, or get it started, from one of the files in the folder rather than from the PERSONAL.XLSB without using a full path. I don't understand why it can't be found. Thanks.
Running a macro from another workbook can be simple done in a way I will try describing in a following step.
But running "a Macro across multiple files" does not mean any modification in terms of calling the macro. You did not show the macro code, but if it references ThisWorkbook like the one to be processed, it must reference the needed one (ActiveWorkbook, or wb, if previously it has been Set).
If "I am running the macro from that file" means running the macro from "PERSONAL.XLSB", it should simple be called as:
Graph_NEW
as it has already been suggested. If you insist to use Application.Run, even if it is not necessary, you can try:
Application.Run "Graph_NEW"
Now, if you run the code in a different workbook and need to run the macro from "PERSONAL.XLSB", the simplest way is using:
Application.Run "PERSONAL.XLSB!Graph_NEW"
Of course, "PERSONAL.XLSB" should be open.
If you insist to use the "PERSONAL.XLSB" full path, it will also work and you can use something similar with what you are trying. The advantage is that, if the workbook is closed, it will be open to run the macro:
Application.Run
Application.Run "'C:\Users\myname\AppData\Roaming\Microsoft\Excel\XLSTART\PERSONAL.XLSB'!Graph_NEW"
Your code misses the prefix "'". It will work without it if no any spaces exist in the workbook full path, but you should delete the one before "!"... In your case, if "myname" does not contain any space, it may be missing.
You can also add a reference to "Personal.xlxb" VBAProject and call the macro as you do in the working workbook. To do that, previously change the initial/existing standard name in something like "PersVBProject" (right click on the project from VBE 'Project Explorer`, choose 'VBAProject Properties...', change the existing name and press 'OK').
So, simple call it as:
Graph_NEW

How do I change the worksheet referenced to be a variable? I want it to activate

The macro I wrote - it pulls information from two different spreadsheets and formats it all. Those spreadsheets will change every month. How do I get the "ACTIVATE" command to reference the worksheet I have open instead of the one that WAS open when I recorded this macro? One of the commands loos like this:
Windows("273517_0273517M190831_08_31_2019_Toll Detail.csv").Activate
I tried to copy and paste a few lines of the code but THIS site rejected it, saying it wasn't formatted properly. Since it was an exact copy of a fully functioning macro - I haven't got a clue what it wants.
It is the "...Toll Detail.csv" file in line 4 that needs to be a variable as it changes monthly. This file is referenced several times in my recorded macro. I will need to be able to go in and change each of them in whatever manner you suggest. I am TOTALLY NEW to VBA so please, dumb it down as best you can. :) Thank you!
You can use GetOpenFileName to select and open your file each time. Following is the code for your reference:
Sub OpenFile()
Dim myFile As String
Dim Wb As Workbook
myFile = Application.GetOpenFilename("CSV Files (*.csv), *.csv")
'Exit if no file selected
If myFile = "False" Then
MsgBox "No file selected!"
Exit Sub
End If
'Open the file
Set Wb = Workbooks.Open(myFile)
'Do what you want with the file here onward
'
'
'
End Sub
I would suggest declaring a variable in the beginning of the sub, like so:
Dim strFile As String
strFile = "273517_0273517M190831_08_31_2019_Toll Detail.csv"
Throughout the sub you can reference the variable, so in every instance where there is the workbook name in the code replace it with strFile like this:
Windows(strFile).Activate
That way you only have to update it once for the entire procedure.
There might be more elegant solutions, but more information on the use case would be required for that.

Is there a way to automatically run a macro upon opening Excel files with similar file names?

I have researched a good amount and tried this on my own this afternoon but was unsuccessful. When users download and open .XLS files that begin with "Current Approved", I want a macro to automatically run from my "PERSONAL.XLSB" file on any "Current Approved*.XLS" file upon opening it (* is a Wildcard). That way I can just put the code in any given users "PERSONAL.XLSB" file one time, and the macro will just automatically be triggered without the user needing to remember to trigger the macro via a shortcut key or button.
From my research here and in other places, I have only seen ways to:
Run the macro when opening the workbook which contains the macro.
Run a macro when any workbook is opened.
I have tried to modify #2 from the link above, but I've NOT figured out how to automatically run macros in this manner on files with similar names.
'Declare the application event variable
Public WithEvents MonitorApp As Application
'Set the event variable be the Excel Application
Private Sub Workbook_Open()
Set MonitorApp = Application
End Sub
'This Macro will run whenever an Excel Workbooks is opened
Private Sub MonitorApp_WorkbookOpen(ByVal Wb As Workbook)
Dim Wb2 As Workbook
For Each Wb2 In Workbooks
If Wb2.Name Like "Current Approved*" Then
Wb2.Activate
MsgBox "Test"
End If
Next
End Sub
Essentially, if I download an excel file from our CRM that begins with "Current Approved" and open it, I would like to see a messagebox of "Test".
Your code doesn't look like what you're describing. The below code should display the "Test" MsgBox when opening workbooks meeting the rule of starting with "Current Approved"
'This Macro will run whenever an Excel Workbooks is opened
Private Sub MonitorApp_WorkbookOpen(ByVal Wb As Workbook)
Const cText As String = "Current Approved"
If UCase(Left(Wb.Name, Len(cText))) = UCase(cText) Then
' Wb2.Activate
MsgBox "Test"
End If
End Sub

Run-time error '1004': Method 'SaveAs' of object'_Workbook' failed

Users have an Excel spreadsheet. To save it, they press a button which runs the below VBA code.
The code attempts to save the Excel spreadsheet to a network location amending the file name with today's date.
Intermittently the code will fail with
"Run-time error '1004': Method 'SaveAs' of object'_Workbook' failed".
The script is:
Public Sub Copy_Save_R2()
Dim wbNew As Workbook
Dim fDate As Date
fDate = Worksheets("Update").Range("D3").Value
Set wbNew = ActiveWorkbook
With wbNew
ActiveWorkbook.SaveAs Filename:="Q:\R2 Portfolio Prints\#Archive - R2 Portfolio\" & "R2 Portfolio - CEC A " & Format(fDate, "mm-dd-yyyy")
End With
Sheets("Update").Activate
End Sub
As Hugo stated, it could be an issue with the mapped drive. I prefer to use the full UNC path (\\Thismachine\...), in case the workbook gets used on a machine that doesn't have the mapped drive set up.
I thought the missing extension could be the problem, but I just tested it in Excel 2013 and it automatically added .xlsx to the filename.
The issue is probably due to the wbNew reference. It's completely unnecessary and should not be combined with ActiveWorkbook. Basically, you should have either a reference to a workbook, or use the predefined ActiveWorkbook reference. I'd also recommend using ThisWorkbook instead, since the user might click on another book while code is running.
Public Sub Copy_Save_R2()
Dim wbNew As Workbook
Dim fDate As Date
fDate = Worksheets("Update").Range("D3").Value
Application.DisplayAlerts = False
ThisWorkbook.SaveAs Filename:="Q:\R2 Portfolio Prints\#Archive - R2 Portfolio\R2 Portfolio - CEC A " & Format(fDate, "mm-dd-yyyy") & ".xlsx"
Application.DisplayAlerts = True
ThisWorkbook.Sheets("Update").Activate
End Sub
Edit: Added Application.DisplayAlerts commands to prevent any Save popups, such as using .xlsx instead of .xlsm, and overwriting an existing copy.
Edit 2018-08-11: Added escape backslashes to fix UNC path display. Added strike-through to inaccurate statement about the With statement (see comments below). Basically, since nothing between With and End With begins with a ., the statement isn't doing anything at all.
I was also looking for the cause of this error, and then remembered I was working on a version of my spreadsheet that had been recovered. Once I manually saved the recovered file and reopened it there was no problem with the vba code to save the workbook.
When it happened to me, I added a command before the save.
On Error Resume Next
Kill TargetFullname
On Error GoTo 0
wb.SaveCopyAs TargetFullname
(I also use application.display=false)
Had the same error message when saving a workbook with a dynamic file name to a network location. Saving stage suddenly stopped working. Error 1004 Workbook SaveAs method failed.
Realised after a while that the location where the macro saved the output had changed and the macro's script had been tweaked by the team to reflect the new network address.
Whilst the new network address was accurate, it was simply too long...
I shortened the folder names, as well as the file name and bingo... Workbooks are being saved again, with no error messages.
This worked for me. Ensure your workbook is not shared. I guess "Shared" workbooks have limitations.
Saw this here:
https://www.ozgrid.com/forum/forum/help-forums/excel-general/27843-save-xls-as-txt
In the "Review" tab click "Share Workbook" and ensure "Allow changes by more than one user at the same time. This allows workbook merging" is unchecked.

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.

Resources