TL;DR —
CallByName(object, "MemberName", callType, args)reads a property, writes a property, or runs a method on an object using the member name as a string. ThecallType—VbGet,VbLet,VbSet, orVbMethod— tells VBA which of those four things you mean, and picking the wrong one is the number-one cause of error 438. It only reachesPublicmembers. Use it when the property or method name is decided at runtime — a data-driven form, a table of settings — not when you already know the member and can writeobj.Memberdirectly.
Sub SetByName()
Dim ctl As Object
Set ctl = Me.Controls("txtName")
' Write a property whose name is a string:
CallByName ctl, "Value", VbLet, "Ada Lovelace"
' Read a property whose name is a string:
MsgBox CallByName(ctl, "Value", VbGet)
End Sub
Normally you reach a member of an object by typing it: range.Value, sheet.Name, form.Hide. That
works because you know the member when you write the code. CallByName is for the case where you do
not — the property or method name arrives as text, from a table, a config sheet, or a loop. It is the
second of three tools that share one idea. Application.Run turns a string
into a running macro; CallByName turns a string into a member call on an object; and
Evaluate turns a string into a computed Excel expression. All three trade the
compiler for runtime flexibility: because the member name is text, a typo is no longer a red squiggle —
it is a run-time error. So the same rule applies: never feed CallByName a member name you did not build
or validate yourself.
What you'll learn
- Where
CallByNamesits next toApplication.RunandEvaluate - The four call types —
VbGet,VbLet,VbSet,VbMethod— and what each one means - Why the wrong call type raises error 438, and how to read the message
- Passing arguments to a method or an indexed property
- The public-only rule that makes some calls silently fail
- The pattern it exists for: applying a table of names and values to an object
The mental model: reflection aimed at an object's members
Application.Run looks up a macro by name. CallByName does the same
trick one level down: it looks up a member of a specific object by name. You hand it the object, the
member name as a string, and one more thing the direct syntax hides from you — what kind of access you
want. obj.Value = 5 and x = obj.Value and obj.Refresh look different in normal code, but to
CallByName they are the same call with a different callType.
x = obj.Caption ' direct: read
CallByName obj, "Caption", VbGet ' the same read, member name as a string
That extra callType argument is the whole reason CallByName feels unfamiliar. Once you see it as
"which of the four kinds of member access", the function stops being mysterious.
The four call types
VBA distinguishes four things you can do to a member, and you must tell CallByName which one:
CallByName obj, "Value", VbGet ' read a property -> returns the value
CallByName obj, "Value", VbLet, "New text" ' write a value property
CallByName obj, "Range", VbSet, someRange ' write an OBJECT property (needs Set)
CallByName obj, "Refresh", VbMethod ' run a method
The split between VbLet and VbSet mirrors VBA's own Let versus Set: use VbLet for a plain
value (a number, a string, a Boolean) and VbSet for an object reference (assigning a Range or another
object to a property). VbGet reads; VbMethod calls. Almost every CallByName bug is picking the
wrong one of these four.
The trap: the wrong call type raises error 438
This is the line to remember. If the member does not exist, or you ask for the wrong kind of access, VBA cannot warn you at compile time — the name is a string — so you get run-time error 438, "Object doesn't support this property or method":
' Wrong: Caption is a property, not a method
CallByName lbl, "Caption", VbMethod ' -> error 438
' Right: read it with VbGet
Dim text As String
text = CallByName(lbl, "Caption", VbGet)
The message is slightly misleading: the object often does support the member — you just asked for it
the wrong way (a VbMethod on a property, or a VbGet on a method that needs VbMethod). When you see
438 from CallByName, check the call type before you doubt the member name. And because the name comes
from data, wrap runtime-sourced calls in On Error handling so an unexpected name
is a handled outcome, not a crash.
Passing arguments to a method or indexed property
Arguments go after the call type, in order:
' A method with arguments:
CallByName ws, "Protect", VbMethod, "password123"
' An indexed property (Cells(2, 3)) via CallByName:
Dim v As Variant
v = CallByName(ws, "Cells", VbGet, 2, 3) ' same as ws.Cells(2, 3)
Arguments are positional, just as they are with Application.Run. If a
method takes several, list them in signature order. This is also how you reach indexed properties like
Cells(row, col) when the property name itself is dynamic.
The public-only rule
CallByName can only reach Public members. A Private property or method inside a class module is
invisible to it, and — because the name is a string — you get the same run-time 438 you would get for a
misspelling, with no hint that the real problem is visibility. If a call fails on a member you are sure
exists, confirm it is declared Public on the object's class. This is a common
snag when driving your own class objects by name rather than built-in Excel objects.
The pattern it exists for: a table of names and values
CallByName earns its place when you have many members to touch and their names live in data. The
textbook case: apply a table of property names and values to a control, a shape, or a chart, without
writing one assignment line per property.
' Settings could come from a worksheet: column A = property, column B = value
Dim props As Variant, vals As Variant, i As Long
props = Array("Caption", "Width", "Visible")
vals = Array("Total", 120, True)
For i = LBound(props) To UBound(props)
CallByName btn, props(i), VbLet, vals(i) ' one loop instead of three assignments
Next i
Three properties is a toy; thirty is a real form, and that is where the loop replaces a wall of
btn.Caption = ... : btn.Width = ... lines. The judgment call is the same as with the rest of this
family: if the member names are constants you type once, use direct syntax — btn.Caption = "Total" is
clearer and the compiler checks it. Only when the names are genuinely data does CallByName pay for the
loss of compile-time safety.
How ExcelMaster helps
The mistakes here are quiet: a VbMethod where a VbGet belonged, a Private member you cannot reach,
a name from a settings sheet that no longer matches the object. Each one surfaces as the same
hard-to-place error 438.
ExcelMaster lets you describe the
intent — "set these properties on this control from a table, and warn me if a name does not fit" — and it
writes the CallByName loop with the right call types and an error guard already in place. You keep the
workbook and the code, and you skip the 438 hunt.
Frequently asked questions
What does CallByName do in VBA?
It gets a property, sets a property, or runs a method on an object using the member name as a string
rather than typed-in code. The syntax is CallByName(object, "MemberName", callType, args), where
callType is VbGet, VbLet, VbSet, or VbMethod. Use it when the member name is decided at run
time; when you know it as you write the code, object.Member is clearer.
What is the difference between VbGet, VbLet, VbSet and VbMethod?
VbGet reads a property and returns its value. VbLet writes a plain value property (number, string,
Boolean). VbSet writes an object property, mirroring VBA's Set. VbMethod runs a method. Choosing
the wrong one is the usual cause of error 438, because the object supports the member but not in the way
you asked.
Why does CallByName raise error 438?
Because the member you named cannot be accessed the way you asked: the name is misspelled, the member is
Private (CallByName only reaches Public members), or the call type is wrong — for example VbMethod
on a property. Since the name is a string, VBA cannot catch this until the line runs. Check the call type
and the member's visibility.
Can CallByName call a private method?
No. CallByName only reaches Public members. A Private property or method in a class module raises
error 438, the same error you get from a misspelled name, so it can be confusing to diagnose. Make the
member Public if you need to reach it by name.
When should I use CallByName instead of object.Member?
Only when the member name is dynamic — it comes from a table, a config sheet, or a loop over property
names. If you know the member as you write the code, use object.Member: it is clearer and the compiler
catches typos. For running a standalone macro by name, use
Application.Run instead.
Tested in
Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-09-27.
Related guides: VBA Application.Run · VBA Evaluate · VBA Class Module · VBA With · VBA On Error · VBA CreateObject · VBA Dictionary
