VBAを始めたばかりの頃、コンパイルエラーでオブジェクト変数またはWithブロック変数が設定されていませんやオブジェクトが必要ですのエラーダイアログに怒られっぱなしだったのをよく覚えています。怒られなくなったのは代入ステートメントが理解できたからです。
コンパイルとは、記述されたコードを機械語に変換することです。コンパイルエラーは、コードのスペルミスや構文規則の誤りなどによって機械語変換に失敗することで起こります。マクロは実行されません。
それに対し実行時エラーは、コンパイルは正常に処理されたものの、マクロ実行中に処理が継続できない場合に起こります。こちらはマクロを中断します。
コンパイルエラーはVBAの記述ルール違反、実行時エラーはコードの組み立て方に問題があります。
1行に1ステートメント
本題とは少しそれますが、Microsoft OfficeVBAリファレンスからの引用です。
ステートメント
1種類の処理、宣言、または定義を表す、構文的に完全な単位。通常では1行に1ステートメントですが、コロン:を使用することで1行に複数のステートメントを記述できます。また、行連結文字_を使用することにより、論理的な1行を物理的な複数行に継続できます。
https://docs.microsoft.com/ja-jp/office/vba/language/glossary/vbe-glossary#statement
いい加減なサンプルですが解説します。下のコードはダイアログでメッセージが表示されるコードです。
Sub デモ1()
Dim 自分 As String: 自分 = "悟空"
MsgBox ("オッス!おら " & _
自分)
End Sub
このサンプルを「1行1ステートメント」に直すと普通は下のようなコードになります。これでも間違いではないのですが、厳密にいえば記述不足です。省略されているステートメントがあります。
Sub デモ2()
Dim 自分 As String
自分 = "悟空"
MsgBox ("オッス!おら " & 自分)
End Sub
省略されているステートメントを記述するとこうなります。
Sub デモ3() 'Subステートメント
Dim 自分 As String 'Dimステートメント
Let 自分 = "悟空" 'Letステートメント
Call MsgBox ("オッス!おら " & 自分) 'Callステートメント
End Sub 'Endステートメント
- 1行1ステートメントなのに先頭が青文字にならない行があるのは、ステートメントを省略しているから
- Letステートメントは省略推奨(公式より)なので、Web上のサンプルに記述されていることはほとんどない
Letステートメントを省略している事が分かると、Setステートメントの理解が早まると思います。
代入(割り当て)ステートメント
OfficeVBAリファレンスを覗くとステートメントについて以下のような説明があります。
Visual Basicのステートメントは完結した命令です。ステートメントにはキーワード、演算子、変数、定数、および式を含めることができます。各ステートメントは次の3つのカテゴリのいずれかに属します。
・変数、定数、またはプロシージャの名前を指定し、データ型を指定できる宣言ステートメント。
・変数または定数に値または式を割り当てる割り当てステートメント。
・アクションを開始する実行可能ステートメント。これらのステートメントは、メソッドまたは関数を実行し、コード ブロックをループまたは分岐できます。多くの場合、実行可能ステートメントには算術演算子または条件付き演算子が含まれます。
https://docs.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/writing-visual-basic-statements
本項は「変数または定数に値または式を割り当てる割り当てステートメント」のお話です。SetとLetのお話です。
- Setステートメント
オブジェクトを変数に代入します。
- Letステートメント
値を変数またはプロパティに代入します。
サンプルを例に説明します。demo4を実行するとA1セルに入力されている文字列がダイアログで表示されます。
Sub デモ4()
Dim テスト As String
テスト = Range("A1")
MsgBox (テスト)
End Sub
省略せずに記述すると以下の通りです。Letステートメントで値を代入しています。ただし省略可能かつ省略推奨(公式より)なので、普段このような記述を見かけることはありません。
Sub デモ5()
Dim テスト As String
Let テスト = Range("A1").Value
Call MsgBox(テスト)
End Sub
LetステートメントをSetに変えると恒例の「オブジェクトが必要です」エラーが出ます。Setはオブジェクトを変数に代入するステートメントなので、「Set使ってるのにオブジェクトがないよ?」と怒られている訳です。
Sub デモ6()
Dim テスト As String
Set テスト = Range("A1").Value 'エラー
Call MsgBox(テスト)
End Sub
Range(“A1”).Valueは値です。RangeオブジェクトのValueプロパティを指定しているからです。
今度は変数の型をオブジェクト型(Range)にしてみます。Valueプロパティをしれっと消しているのは話が脱線するからです。このマクロはオブジェクト変数またはWithブロック変数が設定されていませんエラーが出ます。原因はLetステートメントです。先ほどとは逆のパターンです。
Sub デモ7()
Dim テスト As Range
Let テスト = Range("A1") 'ここでエラーが出るのは変数がオブジェクト変数のせい
Call MsgBox(テスト)
End Sub
値ではなくオブジェクトを代入するときはSetステートメントを使います。
Sub デモ8()
Dim テスト As Range
Set テスト = Range("A1")
Call MsgBox(テスト)
End Sub
Setステートメントを使用してオブジェクトをオブジェクトとして宣言された変数に代入します。
https://docs.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/writing-assignment-statements
「オブジェクトとして宣言された変数」に注目です。「代入するものがオブジェクトかどうか」と同時に「宣言された変数の型がオブジェクト変数かどうか」も重要です。代入式が間違っていなくてもエラーが出るのであれば、変数が間違っています。
ちなみにこのサンプル、Variant型で宣言するとSet/Let(or省略)のどちらでもエラーは出ません。
エラーがでないからといって、どちらでもいい訳ではありません。Setで代入すればオブジェクト、Letで代入すれば値になります。コードによっては意図しない動き(バグ)の元になるので注意が必要です。
値をプロパティに代入するのはLetステートメントです。もちろんLetは省略可能です。
Sub デモ9()
Dim テスト As Range
Set テスト = Range("A1")
Let テスト.Font.Bold = True 'True(値)をBold(プロパティ)に代入
End Sub
「オブジェクトが必要です」と「オブジェクト変数またはWithブロック変数が設定されていません」エラーのまとめ
ステートメントが記述されていない行はLet,Callのステートメントが省略されています。省略されているステートメントを読み取れるようになりましょう。値が代入されていればLet、関数やプロシージャの呼び出しはCallが省略されています。
- オブジェクトが必要です
setを使って値を代入しようとしている。
- オブジェクト変数またはWithブロック変数が設定されていません
オブジェクト変数に対して、値を代入しようとしている。setの付け忘れ。
オブジェクトの中身がEmptyの場合にもエラーは出ますが、今回は触れません。まずは代入ステートメントの違いを覚えましょう。
- 代入するものがオブジェクトか値か確認し、オブジェクトの代入にはSetステートメントを使用する。
- 変数宣言を確認する。変数宣言が不適切だとエラーになる。
オブジェクトが必要です:Setステートメント使ってますけどオブジェクトがありませんよ?そのSet、本当に必要ですか?
オブジェクト変数またはWithブロック変数が設定されていません:変数がオブジェクトで宣言されているのに代入されるものが値になっているんですけど?Set付け忘れていませんか?
※翻訳どおりでないケースもあります。
- ****メソッドを使ったら「オブジェクトが必要です」のエラーが出るんだけど?
メソッドの対象がプロパティになっていませんか?メソッドはオブジェクトを操作する命令文です。例えばRange(“A1”).Copy(オブジェクト.メソッド)が正しい構文なのに対してRange(“A1”).Value.Copy(プロパティ.メソッド)と記述すると、メソッドの対象がオブジェクトではないので「オブジェクトが必要です」のエラーが出ますが、Setの付け忘れではありません。構文間違いです。
- 正しくSetステートメントを使っているはずなのに「オブジェクトが必要です」のエラーが修正できない
変数が間違っています。変数をStringやLongで宣言していませんか?Setステートメントはオブジェクトを変数に代入するステートメントです。変数宣言はオブジェクト型(Range、WorkBook、WorkSheetなど)にする必要があります。
オブジェクトの理解をもっと深めたい人はこちらの記事でRangeオブジェクトを例に解説しています。是非読んでみて下さい。