TL;DR —
Application.Run "MacroName", arg1, arg2runs a macro you name with a string, so the code decides which macro to run while it is running, not when you write it. Use it as a statement (no parentheses) or capture aFunction's return withx = Application.Run("MyFunc", 5). The catch: the name is text, so a typo is a run-time error 1004, not a red squiggle at compile time. Reach for it when the macro name is not known until runtime — a dispatch table, a name in a cell, a plugin. If you already know the macro, just call it directly.
Sub RunByName()
' Decide the macro name at runtime, then run it
Dim macroName As String
macroName = Range("Config!B2").Value ' e.g. "BuildMonthlyReport"
Application.Run macroName, Date, "Finance" ' pass arguments by position
End Sub
Most of the time you call a macro the obvious way: you type its name, BuildReport, and it runs. That
only works because you know the name when you write the code. Application.Run is for the other
case — when the name lives in a cell, a settings sheet, or a variable, and you do not know it until the
macro is already running. This is the first of three tools that share one idea, so it is worth stating
that idea up front: Application.Run, CallByName and
Evaluate all turn a string into an executed action at runtime. Run executes a
macro named by a string, CallByName invokes an object member named by a string, and Evaluate
computes an Excel expression held as text. The power is indirection; the price is that you give up the
compiler, so a typo becomes a run-time error instead of a squiggly line. The rule that governs all
three: never hand them a string you did not build or check yourself.
What you'll learn
- The mental model that ties
Application.Run,CallByNameandEvaluatetogether - The statement form versus capturing a
Function's return value - Passing arguments by position — up to 30, and why objects arrive as values
- Running a macro that lives in another open workbook
- Why a mistyped name fails at run time, not compile time, and how to guard it
- The one situation that actually justifies
Application.Run: a dispatch table
The mental model: a switchboard that looks up the macro by name
Think of a switchboard operator. A direct call, BuildReport, is a private line wired straight to one
office — fast, but fixed forever. Application.Run is the operator: you say a name out loud and they
connect you to whoever that name points to right now. The name can come from anywhere — a cell, a
config sheet, a loop over a list — because the connection is made at runtime, not soldered in when you
write the code.
BuildReport ' direct call: name fixed at write time
Application.Run "BuildReport" ' indirect call: name resolved at run time
Those two lines do the same thing today. The difference only shows up when the name is not a constant.
The moment you write Application.Run someVariable, you have bought yourself indirection — and taken on
the responsibility for making sure someVariable holds a real macro name. That trade is the whole
topic.
The syntax: statement form vs capturing a return value
Like MsgBox, Application.Run has two shapes, and the brackets decide which one
you get. As a statement, drop the parentheses:
Application.Run "FormatSheet", ActiveSheet.Name ' run a Sub, ignore any result
To capture what a Function returns, wrap the whole call in parentheses and assign it:
Dim total As Double
total = Application.Run("SumColumn", "B") ' capture the Function's return value
The rule is the same one that trips people with MsgBox: parentheses mean this is an expression whose
value I want. Use the statement form for a Sub; use the function form for a
Function whose result you need.
Passing arguments — by position, up to 30, as values
You pass arguments after the name, separated by commas. Two hard limits are worth memorising:
Application.Run "PostEntry", 2026, "March", 4820.5 ' args go by POSITION, not by name
First, arguments are positional only — you cannot use named:= arguments here, so the order in
your call has to match the order in the target macro's signature exactly. Second, there is a ceiling of
30 arguments, which in practice means: if you are approaching it, pass an array or a
Dictionary instead of a long argument list.
One quiet surprise: objects are passed as values, not as live references. If you pass a Range, the
target receives its .Value, not the range itself. When a macro needs to act on a real object, put the
name in the string and let the target resolve the object itself, rather than trying to hand the object
across Application.Run.
Running a macro in another open workbook
This is where Application.Run earns regular use: calling a macro that lives in a different workbook —
a shared add-in, a tool workbook, a report template. Qualify the name with the workbook:
Application.Run "'Monthly Tools.xlsm'!Module1.RefreshData"
Three details decide whether this works. The workbook must be open — Application.Run will not open
it for you. Wrap the workbook name in single quotes if it contains spaces ('Monthly Tools.xlsm').
And the target macro must be Public (the default for a Sub in a standard module); a Private
macro is invisible from outside its own module. Get any of the three wrong and you land on the same
error the next section is about.
The trap: the name is a string, so a typo is a run-time error
Here is the cost of indirection, and the single most important line on this page. When you call a macro
directly and misspell it, the VBA compiler stops you before anything runs. When you misspell the string
in Application.Run, the compiler has nothing to check — it is just text — so the mistake surfaces only
when that line executes, as run-time error 1004, "Cannot run the macro":
Sub SafeRun(macroName As String)
On Error GoTo NotFound
Application.Run macroName
Exit Sub
NotFound:
MsgBox "Macro not found or failed: " & macroName, vbExclamation
End Sub
Because the compiler cannot help you, you have to. Any time the macro name comes from outside your
code — a cell, a file, user input — wrap the call in On Error handling and treat
"macro not found" as a normal outcome, not a crash. A string-driven call without an error guard is a
bug waiting for the first typo in a config sheet.
When Application.Run actually earns its place: a dispatch table
If you know the macro at write time, call it directly — Application.Run "BuildReport" is slower to read
and easier to break than BuildReport. The method pays off only when the name is genuinely dynamic. The
cleanest example is a dispatch table: map a set of names to a set of actions, then run whichever one
the situation calls for.
Sub RunAction(actionName As String)
' A whole Select Case collapses into one line:
' the button's Tag, a cell, or a config row decides which macro runs.
Application.Run "Actions." & actionName ' "Actions.Export", "Actions.Refresh", ...
End Sub
That single line replaces a growing Select Case that you would otherwise have
to edit every time you add an action. It is the pattern behind plugin architectures, ribbon buttons that
carry their handler name in a Tag, and macros whose behaviour is driven by a settings sheet. The test
for whether you should use Application.Run is simple: is the name a constant? If yes, call directly.
If no, this is the tool.
Application.Run vs a direct call vs CallByName
Three ways to invoke code, three jobs. A direct call is for a macro you know by name at write time —
always prefer it when you can. Application.Run is for a macro whose name is a string decided at
run time, including one in another workbook. CallByName is the same idea
aimed at an object's property or method rather than a standalone macro. If you find yourself building a
string to reach a member of a specific object (a control, a shape, a class), that is CallByName's job,
not Run's.
How ExcelMaster helps
The failure on this page is silent by design: a name that is right today and wrong the moment someone
edits a config sheet, surfacing as error 1004 deep in a run. Guarding every string-driven call, quoting
workbook names, checking that a target is Public — it is easy to get one of them wrong.
ExcelMaster lets you describe
the goal in plain words — "run the macro named in this cell, and tell me clearly if it does not exist" —
and it writes the Application.Run code with the error guard, the workbook qualifier, and the return
handling already in place. You keep the workbook and the code, and you skip the 1004.
Frequently asked questions
How do I run a macro by its name as a string in VBA?
Use Application.Run "MacroName". As a statement you omit the parentheses; to capture a Function's
result, write x = Application.Run("MyFunc", arg). Because the name is a string the compiler cannot
check it, so wrap the call in On Error handling — a typo shows up as run-time
error 1004 rather than a compile error.
How do I pass arguments to a macro with Application.Run?
List them after the name, separated by commas: Application.Run "PostEntry", 2026, "March". Arguments
are positional only — you cannot use named:= syntax — and there is a limit of 30. Objects are passed as
values (a Range arrives as its .Value), so for anything larger pass an array or a
Dictionary.
How do I run a macro in another workbook?
Qualify the name with the workbook: Application.Run "'Tools.xlsm'!Module1.RefreshData". The other
workbook must already be open, use single quotes around its name if it contains spaces, and the
target macro must be Public. Application.Run does not open the workbook for you.
Why does Application.Run give error 1004, cannot run the macro?
Almost always the name string does not resolve to a runnable macro: it is misspelled, the workbook that
holds it is not open, or the macro is Private. Because the name is text, this can only be caught at run
time. Check the spelling, confirm the workbook is open, and make sure the target is Public.
When should I use Application.Run instead of just calling the macro?
Only when the name is not known until run time — it comes from a cell, a config sheet, a variable, or a
loop, or the macro lives in another workbook. If you know the macro name as you write the code, call it
directly: BuildReport is clearer and the compiler will catch typos. For invoking a member of an
object by name, use CallByName instead.
Tested in
Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-09-27.
Related guides: VBA CallByName · VBA Evaluate · VBA Sub · VBA Function · VBA On Error · VBA Select Case · VBA Dictionary
