TL;DR —
CallByName(object, "MemberName", callType, args)は、メンバー名を文字列として 使って、オブジェクト のプロパティを読み、プロパティを書き、あるいはメソッドを実行します。callType—VbGet、VbLet、VbSet、VbMethod— が、その四つのうちどれをするつもりなのかを VBA に伝えます。そして間違ったものを選ぶことが、エラー 438 の第一の原因です。到達できるのはPublicのメンバーだけです。プロパティ名やメソッド名が実行時に決まるとき — データ駆動のフォーム、設定の表 — に使いましょう。すでにメンバーがわかっていてobj.Memberと直接書けるときは使 いません。
Sub SetByName()
Dim ctl As Object
Set ctl = Me.Controls("txtName")
' 名前が文字列であるプロパティに書き込む:
CallByName ctl, "Value", VbLet, "Ada Lovelace"
' 名前が文字列であるプロパティを読み取る:
MsgBox CallByName(ctl, "Value", VbGet)
End Sub
ふだん、オブジェクトのメンバーには打ち込んで到達します:range.Value、sheet.Name、form.Hide。これが成り立つの
は、コードを書く時点でメンバーがわかっている からです。CallByName は、そうではない場合 — プロパティ名やメソッド名
が、表や設定シート、ループからテキストとして届く場合 — のためのものです。これは同じ考え方を共有する三つの道具の二つ目
です。Application.Run は文字列を実行される マクロ に変え、CallByName は文字列を
オブジェクト に対するメンバー呼び出しに変え、Evaluate は文字列を計算された Excel の 式
に変えます。三つとも、コンパイラを引き換えに実行時の柔軟さを手に入れます。メンバー名はテキストなので、打ち間違いはも
はや赤い波線ではなく — 実行時エラーです。ですから同じ鉄則が当てはまります。自分で組み立てるか検証したのではないメンバ
ー名を、決して CallByName に渡してはいけません。
この記事で学べること
CallByNameがApplication.RunとEvaluateの隣でどこに位置するか- 四つの呼び出しの種類 —
VbGet、VbLet、VbSet、VbMethod— と、それぞれの意味 - 間違った呼び出しの種類がエラー 438 を起こす理由と、メッセージの読み方
- メソッドやインデックス付きプロパティへの引数の渡し方
- 一部の呼び出しを静かに失敗させる、Public 限定の規則
- この関数が存在する目的のパターン:名前と値の表をオブジェクトに適用する
考え方の軸:オブジェクトのメンバーに向けたリフレクション
Application.Run は マクロ を名前で探します。CallByName は同じ技を一段下で行いま
す。特定のオブジェクトのメンバー を名前で探すのです。渡すのは、オブジェクトと、文字列としてのメンバー名、そして直接
の構文があなたから隠しているもうひとつのもの — どんな種類のアクセスがほしいか です。obj.Value = 5 と
x = obj.Value と obj.Refresh は、普通のコードでは違って見えますが、CallByName にとっては callType が違うだけ
の同じ呼び出しです。
x = obj.Caption ' 直接: 読み取り
CallByName obj, "Caption", VbGet ' 同じ読み取り、メンバー名は文字列
この余分な callType 引数こそ、CallByName が見慣れなく感じられるすべての理由です。それを「四種類のメンバーアクセス
のどれか」ととらえられれば、この関数は神秘的ではなくなります。
四つの呼び出しの種類
VBA は、メンバーに対してできることを四つに区別していて、そのどれなのかを CallByName に伝えなければなりません。
CallByName obj, "Value", VbGet ' プロパティを読む -> 値を返す
CallByName obj, "Value", VbLet, "New text" ' 値プロパティに書き込む
CallByName obj, "Range", VbSet, someRange ' オブジェクトプロパティに書き込む (Set が必要)
CallByName obj, "Refresh", VbMethod ' メソッドを実行する
VbLet と VbSet の分かれ目は、VBA 自身の Let と Set の違いをそのまま映したものです。素の値(数値、文字列、ブー
ル値)には VbLet を、オブジェクト参照(Range や別のオブジェクトをプロパティに代入する)には VbSet を使います。
VbGet は読み、VbMethod は呼び出します。CallByName のバグはほとんどすべて、この四つのうち間違ったものを選ぶこと
です。
落とし穴:間違った呼び出しの種類はエラー 438 を起こす
これは覚えておくべき一行です。メンバーが存在しないか、間違った 種類 のアクセスを求めると、VBA はコンパイル時に警告 できません — 名前は文字列だからです — ですから 実行時エラー 438「オブジェクトは、このプロパティまたはメソッドをサポ ートしていません」 になります。
' 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)
このメッセージは少し誤解を招きます。オブジェクトはたいてい、メンバーを ちゃんと サポートしているのです — ただ、間違
ったやり方で求めただけです(プロパティに対する VbMethod、あるいは VbMethod が必要なメソッドに対する VbGet)。
CallByName から 438 が出たら、メンバー名を疑う前に呼び出しの種類を確かめましょう。そして名前はデータから来るので、
実行時に取得した呼び出しは On Error の処理で包み、予期しない名前が、クラッシュではなく処理
済みの結果になるようにしましょう。
メソッドやインデックス付きプロパティに引数を渡す
引数は、呼び出しの種類のあとに、順番どおりに置きます。
' 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)
引数は、Application.Run と同じく位置指定です。メソッドが複数を取るなら、シグネチャの
順に並べます。これは、プロパティ名そのものが動的なときに Cells(row, col) のようなインデックス付きプロパティへ到達す
る方法でもあります。
Public 限定の規則
CallByName が到達できるのは Public のメンバーだけです。クラスモジュールの中の Private のプロパティやメソッ
ドは、この関数からは見えません。そして — 名前は文字列なので — 綴り間違いのときと同じ実行時エラー 438 が返り、本当の問
題が可視性であるという手がかりは何もありません。存在するはずのメンバーで呼び出しが失敗したら、それがオブジェクトの
クラス で Public と宣言されているか確かめましょう。これは、組み込みの Excel オブジェク
トではなく自作のクラスオブジェクトを名前で操作するときによくつまずく点です。
この関数が存在する目的のパターン:名前と値の表
CallByName が居場所を得るのは、触れるべきメンバーが たくさん あって、その名前がデータの中にあるときです。教科書的
な例が、プロパティごとに代入行を一つずつ書くことなく、プロパティ名と値の表をコントロールやシェイプ、グラフに適用する
ことです。
' 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
プロパティ三つはおもちゃです。三十なら本物のフォームで、そこでこそループが btn.Caption = ... : btn.Width = ... とい
う壁のような行の連なりを置き換えます。判断の基準は、この一族のほかと同じです。メンバー名が一度だけ打ち込む定数なら、
直接の構文を使いましょう — btn.Caption = "Total" のほうが分かりやすく、コンパイラが確かめてくれます。名前が本当にデ
ータであるときにだけ、CallByName はコンパイル時の安全性を失う見返りに値します。
ExcelMaster の活用
ここでの間違いは静かです。VbGet であるべきところの VbMethod、到達できない Private のメンバー、オブジェクトとも
う一致しなくなった設定シートの名前。どれもが、原因を突き止めにくい同じエラー 438 として表に出ます。
ExcelMaster には、意図を伝えられます — 「この
コントロールのこれらのプロパティを表から設定して、名前が合わないなら警告して」 — すると、正しい呼び出しの種類とエラー
ガードをそろえた CallByName のループを書いてくれます。ブックとコードはあなたの手元に残り、438 の原因探しも避けられま
す。
よくある質問
VBA の CallByName は何をするものですか?
打ち込んだコードではなく、メンバー名を 文字列 として使って、オブジェクトのプロパティを取得し、プロパティを設定し、
あるいはメソッドを実行します。構文は CallByName(object, "MemberName", callType, args) で、callType は VbGet、
VbLet、VbSet、VbMethod のいずれかです。メンバー名が実行時に決まるときに使います。コードを書く時点でわかっている
なら、object.Member のほうが分かりやすいです。
VbGet、VbLet、VbSet、VbMethod の違いは何ですか?
VbGet はプロパティを読んで、その値を返します。VbLet は素の値プロパティ(数値、文字列、ブール値)に書き込みます。
VbSet はオブジェクトプロパティに書き込み、VBA の Set を映したものです。VbMethod はメソッドを実行します。間違った
ものを選ぶことがエラー 438 のよくある原因です。オブジェクトはメンバーをサポートしていても、あなたが求めたやり方ではサ
ポートしていないからです。
CallByName がエラー 438 を起こすのはなぜですか?
指定したメンバーに、求めたやり方でアクセスできないからです。名前が綴り間違いか、メンバーが Private か(CallByName は
Public のメンバーにしか到達しません)、あるいは呼び出しの種類が間違っている — たとえばプロパティに対する VbMethod
— かのいずれかです。名前は文字列なので、VBA はその行が実行されるまでこれを捕まえられません。呼び出しの種類と、メンバー
の可視性を確かめましょう。
CallByName で Private のメソッドを呼び出せますか?
いいえ。CallByName が到達できるのは Public のメンバーだけです。クラスモジュールの Private のプロパティやメソッド
は、綴り間違いのときと同じエラー 438 を起こすので、原因の切り分けがまぎらわしくなります。名前で到達する必要があるな
ら、そのメンバーを Public にしましょう。
object.Member ではなく CallByName を使うべきなのはいつですか?
メンバー名が動的なときだけです — 表、設定シート、あるいはプロパティ名を回すループから来る場合です。コードを書く時点で
メンバーがわかっているなら、object.Member を使いましょう。そのほうが分かりやすく、コンパイラが打ち間違いを捕まえて
くれます。単独の マクロ を名前で実行するには、代わりに Application.Run を使います。
検証環境
検証環境: Excel 365 (Windows 11)、VBA 7.1 — 最終確認 2026-09-27。
関連ガイド: VBA Application.Run · VBA Evaluate · VBA Class Module · VBA With · VBA On Error · VBA CreateObject · VBA Dictionary
