🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA CallByName in Excel — Get or Set a Property, or Run a Method, by Name

|

VBA CallByName in Excel — Get or Set a Property, or Run a Method, by Name

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. The callType — VbGet, VbLet, VbSet, or VbMethod — tells VBA which of those four things you mean, and picking the wrong one is the number-one cause of error 438. It only reaches Public members. 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 write obj.Member directly.

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 CallByName sits next to Application.Run and Evaluate
  • 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