和風スパゲティのレシピ

日本語でコーディングするExcelVBA

同じコードなのにエラーが変わるRangeの不思議

この記事を書いていて気づいた小ネタです。

www.limecode.jp



ws1.Range(ws2.Cells(1, 1),ws3.Cells(2, 2))

というコードでセル範囲を取得する場合は、

  • ws1~ws3を一致させる
  • ws2とws3を一致させてws1は省略する

のどちらかを満たさないとエラーになります。


この時のエラーは実は3種類あって、

  • Range' メソッドは失敗しました: '_Worksheet' オブジェクト
  • Range' メソッドは失敗しました: '_Global' オブジェクト-
  • アプリケーション定義またはオブジェクト定義のエラーです。

のいずれかが発生します。


このエラーのうちどれが表示されるかを調べてみました。


A,B,Cの3つのシートを用意しAをActiveSheetに。
それぞれを「Worksheets」「オブジェクト名」「シート変数」で指定して、
いろいろなパターンでやった結果がこちらです。

Range親 Cells1親 Cells2親 エラー
wsA. wsB. wsB. Range失敗_ws
wsA. wsB. wsC. Range失敗_ws
wsA. Worksheets("B"). wsB. Range失敗_ws
wsA. Worksheets("B"). Worksheets("C"). Range失敗_ws
wsA. ws変数B. wsB. Range失敗_ws
wsA. ws変数B. Worksheets("B"). Range失敗_ws
wsA. wsA. wsB. Range失敗_ws
wsA. wsA. ws変数B. Range失敗_ws
wsA. wsA. Worksheets("B"). Range失敗_ws
wsA. wsB. Range失敗_ws
wsA. ws変数B. Range失敗_ws
wsA. Worksheets("B"). Range失敗_ws
wsB. wsC. Range失敗_gl
Worksheets("B"). Worksheets("C"). Range失敗_gl
wsA. wsB. Range失敗_gl
wsA. ws変数B. Range失敗_gl
wsA. Worksheets("B"). Range失敗_gl
wsB. Range失敗_gl
ws変数B. Range失敗_gl
Worksheets("B"). Range失敗_gl
Worksheets("A"). wsB. wsB. アプ/オブ定義
Worksheets("A"). wsB. wsC. アプ/オブ定義
Worksheets("A"). Worksheets("B"). wsB. アプ/オブ定義
Worksheets("A"). Worksheets("B"). Worksheets("C"). アプ/オブ定義
Worksheets("A"). ws変数B. wsB. アプ/オブ定義
Worksheets("A"). ws変数B. Worksheets("B"). アプ/オブ定義
Worksheets("A"). wsA. wsB. アプ/オブ定義
Worksheets("A"). wsA. ws変数B. アプ/オブ定義
Worksheets("A"). wsA. Worksheets("B"). アプ/オブ定義
Worksheets("A"). wsB. アプ/オブ定義
Worksheets("A"). ws変数B. アプ/オブ定義
Worksheets("A"). Worksheets("B"). アプ/オブ定義
wsB. Range失敗_ws
Worksheets("B"). アプ/オブ定義


ということで、Cells同士が違う/RangeとCellsが違うなどは一切関係なく、
Rangeの親シートの指定方法によってエラーの種類が決まる
ようです。


Rangeプロパティは様々な親オブジェクトがあるため、

  • Rangeの親を特定できた場合はそのRangeが失敗したエラーを出す
  • Rangeの親を特定できなかった場合は想定外のエラーを出す

って感じなんですかね。


一応予備知識として、

Rangeの親シート省略時はCellsのようにActiveSheetではなく、
Rangeの中身から親シートを特定
しようとします。


今回の題材である

Range(wsB.Cells(1, 1), wsB.Cells(2, 2))

はActiveSheetに関係なくwsBのセル範囲を取得しますし、

Range("データ!A1")

このように「シート名!セルアドレス」という書き方をすれば、
文字列ですら別のシートを指させることもできますし、

Range("名前定義")

と、名前定義を活用した場合も別シートから取得可能です。


この「中身から親シートを特定しようとするRange」というのは、
Application.Rangeが実行されたという扱いとなります。


これが失敗すると、

  • Range' メソッドは失敗しました: '_Global' オブジェクト

が出るということですね。


そしてもう一つの予備知識、

Worksheets("A")という書き方ですが、
実はこれは「Worksheetsプロパティの引数"A"」ではありません。

Worksheetsプロパティで取得したSheetsコレクションのItem("A")を省略した形というのが正しい説明です。


つまり「ただのコレクションのItem」でしかないため、
型はWorksheetsではなくVariantです。

Worksheets("A").

の入力選択肢が出ない原因がこれになりますね。


ということで、

Worksheets("A").Range(wsB.Cells(1, 1), wsB.Cells(2, 2))

は、
「そんなRangeの使い方知らんからどの親VerのRange使えばいいかわからん。
なんか定義間違ってない?」

ということで、

  • アプリケーション定義またはオブジェクト定義のエラーです。

を出すのかもしれませんね。

イミディエイトでの挙動

ここまではまだ何となく理解できるのですが、
上記のコードをイミディエイトウィンドウで実行すると、なんとすべてのコードが

  • アプリケーション定義またはオブジェクト定義のエラーです。

になります。

なぜ?

シートオブジェクトであるwsAがWorksheetオブジェクトかどうかは、
その親であるThisworkbookのコードに書かないとわからないから?


なんて最初は考察したのですが、イミディエイト上で変数を定義して、
「これはWorksheetだぞ」と明示してから実行しても、

  • アプリケーション定義またはオブジェクト定義のエラーです。

が出ます。


どうしてもイミディエイトウィンドウで

  • Range' メソッドは失敗しました: '_Worksheet' オブジェクト

を出すことができませんでした。


不思議ですね・・・


まったく同じコードなのに出るエラーが変わるという現象は、
この現象で初めて遭遇した気がします。


歯切れの悪い終わり方で申し訳ありませんが、
何かわかったらまた追記したいと思いますので、
みなさんの情報提供もお待ちしております。