変数の有効期間(範囲)、プロシージャ間の変数の受け渡し

より複雑な内容のマクロを作ろうとすると、プロシージャの分割が必要になります。
プロシージャを分割すると、その間で参照する変数が問題になります。 変数宣言なしでマクロを書いている方は、ここで「壁」に当たることになります。引数を使って引き渡さざるを得ませんから。
それよりもまず、変数をどこに書くとどこで参照可能なのかを正しく理解して下さい。
「変数宣言を強制しないといけないのか。」とやや説明が重複しますが、 こちらではプロシージャ間での変数の受け渡しを中心に説明します。 このページでの説明は、全て「Option Explicit」を書いて、変数宣言を強要しています。
下記は、プロシージャ「TEST1」を呼び出すと、内部でプロシージャ「TEST2」が呼び出されてA1セルの値が変数にセットされメッセージに表示されるという簡単なサンプルです。



間違い例①プロシージャ内で変数を宣言する。

Option Explicit

' TEST2を呼んで結果を受け取りたい
Sub TEST1()
    Dim 変数 As Long

    Call TEST2
    MsgBox 変数
End Sub

' TEST1から呼び出されるサブプロシージャ
Private Sub TEST2()
    ' TEST1で宣言した「変数」に値をセット
    変数 = Range("A1").Value
End Sub
この記述では

となります。このことから、プロシージャ内で宣言する変数は他のプロシージャからは参照できないことが解ります。

元々、変数の宣言を行なわない(Option Explicitを記述しない)で記述する場合は、次項のように個々のプロシージャで同じ名前の変数を宣言したのと同様に処理されます。

間違い例②では、それぞれのプロシージャで宣言したら。

Option Explicit

' TEST2を呼んで結果を受け取りたい
Sub TEST1() 
    Dim 変数 As Long

    Call TEST2
    MsgBox 変数
End Sub

' TEST1から呼び出されるサブプロシージャ
Private Sub TEST2()
    Dim 変数 As Long

    ' TEST1で宣言した「変数」に値をセット
    変数 = Range("A1").Value
End Sub
「間違い例①」NGだからと言って、単純に同じ変数名でエラーとなる側のプロシージャ(TEST2)に変数を宣言すると確かに「コンパイルエラー」は避けられます。
でも、ここでA1セルに何かの数値を入力しておいてマクロを起動しても、メッセージにはA1セルの値は表示されません。
それぞれ別プロシージャに変数を宣言して、たまたま変数名が一致したからと言って、値が自動的に引き継がれることがない位は初心者の方でも気付くと思いますが、

'Option Explicit  ←コメントです。

' TEST2を呼んで結果を受け取りたい
Sub TEST1()
    Call TEST2
    MsgBox 変数
End Sub

' TEST1から呼び出されるサブプロシージャ
Private Sub TEST2()
    ' TEST1で宣言した「変数」に値をセット
    変数 = Range("A1").Value
End Sub
変数を宣言しないでこのようなコードを書いた場合、キーワード「変数」はそれぞれのプロシージャで宣言されたものとして扱われるので、上のコードと同じことになるのです。

引数に割り当てると解決できます。

Option Explicit

' TEST2を呼んで結果を受け取りたい
Sub TEST1()
    Dim 変数 As Long

    Call TEST2(変数)
    MsgBox 変数
End Sub

' TEST1から呼び出されるサブプロシージャ
Private Sub TEST2(HENSU As Long)

    ' TEST1で宣言した「変数」に値をセット
    HENSU = Range("A1").Value
End Sub
宣言した変数を、呼び出すプロシージャの引数に割り当てて、呼び出されたプロシージャでこの変数に値をセットすることでこの問題は解決します。 この場合、変数名の一致は関係なく、宣言の順番だけが意味を持つので、あえて変数名は変えてあります。
この他、引数には「値渡し(ByVal)」と「参照渡し(ByRef)」がありますが、ここでは記述を省略しており、「参照渡し(ByRef)」として機能します。 「参照渡し(ByRef)」の場合は、呼ばれた先のプロシージャでその変数に値をセットすることで呼び元からも参照が可能となります。この点については、「プロシージャ間の変数の受け渡し(その2)でもう少し詳しく説明してあります。
※プロシージャ「TEST2」にはSubの前にPrivateを書いています。
これは書かなくても機能上は問題ありませんが、Privateをを記述しないとプロシージャ「TEST2」自体がマクロの起動の選択に表示されてしまいます。


もう一つは、モジュールレベルで変数を宣言する方法です。

Option Explicit
Dim 変数 As Long

' TEST2を呼んで結果を受け取りたい
Sub TEST1()
    Call TEST2
    MsgBox 変数
End Sub

' TEST1から呼び出されるサブプロシージャ
Private Sub TEST2()
    ' TEST1で宣言した「変数」に値をセット
    変数 = Range("A1").Value
End Sub
プロシージャの外(通常はプロシージャの先頭)で宣言する変数は、モジュールレベル変数(あるいはモジュール変数)などと呼びますが、値をセットしたプロシージャが終わった後でも参照が可能となります。
昔のPCのメモリが少なかったころはモジュールレベルの変数を多用することを嫌いましたが、現在ではこの理由で避けることはないでしょう。
2つのプロシージャ間の変数値の受け渡しだけなら前の「引数」で解決できますが、参照するプロシージャがいくつもあったり、変数の数が多い場合はモジュールレベル変数を使う方法もあります。
但し、一連のマクロ動作中なら問題ありませんが、VBと違って、そのブックを開いている間全てに渡ってそのモジュールレベル変数が有効になるわけではないことは知っている必要があります。

他のモジュールからも参照する場合は?
まず、「Module1」の記述です。

Option Explicit 
Public 変数 As Long 
 
' TEST2を呼んで結果を受け取りたい 
Sub TEST1() 
    Call TEST2 
    MsgBox 変数 
End Sub
ここで呼び出すプロシージャ「TEST2」はこのモジュール上にはありません。
次に、「Module2」の記述です。

Option Explicit
Option Private Module

' TEST1から呼び出されるサブプロシージャ
Public Sub TEST2()
    ' TEST1で宣言した「変数」に値をセット
    変数 = Range("A1").Value
End Sub
このように呼び出されるプロシージャ「TEST2」は別のモジュール上にあります。 この時に変数を参照できるようにするには、モジュールレベル変数の宣言にPublicを使う必要があります。なお、Publicでの宣言は標準モジュールにする必要があります。
Dim又はPrivateで宣言した場合はそのモジュール内のプロシージャからでないと参照できません。
また、プロシージャ自身は逆にSubFunctionで始まるものは他モジュールからも呼び出し可能です。 こちらはあえてPrivateを書くことで他モジュールの参照をできなくしたり、マクロの起動に表示させないようにできます。
※なお、プロシージャ「TEST2」は本来、他モジュールからの呼び出しを許可するので、マクロの起動に表示されてしまいますが、 このサンプルでは、「Option Private Module」を書くことで表示されません。