【VBA】基礎まとめ【自分用備忘録】

急遽必要になる（かもしれない）ので基本的な文法等を緊急で調査し、記憶の定着・今後の復習のために殴り書きます。
Pythonと違って処理ブロックの終了を明示しないといけない！うおー！！！

## 1. 変数の宣言

VBAでは __Dim__ を使って変数を宣言する

```
Dim x As Integer    ' 整数型
Dim y As Double     ' 小数型
Dim name As String  ' 文字列型
Dim isDone As Boolean ' 真偽値
```
 __Option Explicit__  を使うと、すべての変数を事前に宣言する必要があり、型不一致等でのお叱りを防げる。

```
Option Explicit
```

## 2. 条件分岐

#### ・If ～ Then ～ (Else) ～ End If

```
Dim age As Integer
age = 20

If age >= 20 Then
    MsgBox "成人です"
Else
    MsgBox "未成年です"
End If
```

#### ・Select Case ～ End Select（複数の条件を分岐させる場合）


```
Dim score As Integer
score = 85

Select Case score
    Case Is >= 90
        MsgBox "Aランク"
    Case Is >= 80
        MsgBox "Bランク"
    Case Else
        MsgBox "Cランク"
End Select
```

## 3. 繰り返し処理

#### ・For ～ Next（回数指定）

```
Dim i As Integer
Sub ForNextExample()
    Dim i As Integer
    For i = 1 To 10  ' 1から10まで繰り返す
        Cells(i, 1).Value = "行番号: " & i
    Next i
End Sub
```

#### ・For Each ～ Next（可変な範囲のデータを順番に処理）
```
Sub ForEachExample()
    Dim c As Range
    For Each c In Range("A1:A10")  ' A1～A10のセルを1つずつ処理
        c.Value = "Checked"
    Next c
End Sub
```



#### ・While ～ Wend（条件がTrueの間繰り返す）
```
Dim x As Integer
x = 1

While x <= 5
    MsgBox "xの値: " & x
    x = x + 1
Wend
```

#### ・Do ～ Loop（条件指定）

```
Dim n As Integer
n = 0

Do While n < 5
    Debug.Print "現在の値: " & n
    n = n + 1
Loop
```

#### ・Do Until ～ Loop（条件がFalseの間繰り返す）
```
Dim y As Integer
y = 1

Do Until y > 5
    MsgBox "yの値: " & y
    y = y + 1
Loop
```

## 4. 関数の作成
#### ・Function ～ End Function（関数）
```
Function AddNumbers(a As Integer, b As Integer) As Integer
    AddNumbers = a + b
End Function
```

関数の結果を取得するには
```
Dim result As Integer
result = AddNumbers(10, 20)
MsgBox result
```

#### ・Sub ～ End Sub（Subプロシージャ）
```
Sub SampleSub()
    MsgBox "こんにちは！" ' メッセージボックスを表示
End Sub
```
:::note info
関数（Function）は返り値を返せるが、Subプロシージャ（Sub）は処理の実行のみ。
:::


## 5. Excelオブジェクトの階層構造

Excel VBAでは、Excel全体を「オブジェクト」の集合体として扱う。
基本的な構造は以下

```
Application（Excel全体）
 └ Workbooks（ブックの集合）
    └ Workbook（ブック1つ）
       └ Worksheets（シートの集合）
          └ Worksheet（シート1つ）
             └ Range（セルや範囲）
                └ Cells（個々のセル）
```
#### 1. Application（Excel全体）
```
Application.DisplayAlerts = False ' 警告メッセージを非表示
Application.ScreenUpdating = False ' 画面の更新を停止（処理高速化）
Application.Workbooks.Open "C:\Users\sample.xlsx" ' 指定のファイルを開く
```
:::note info
Application は省略可能なので Workbooks.Open "C:\Users\sample.xlsx" のように記述してもOK。
Application.ScreenUpdating = False は処理速度向上に役立つ
:::

#### 2. Workbooks & Workbook（ブックの管理）

Excelファイル（.xlsx や .xlsm）を操作するオブジェクト。

##### ・現在開いているすべてのブックへの操作
```
Dim wb As Workbook
For Each wb In Workbooks
    Debug.Print wb.Name ' 現在開いているすべてのブックの名前を表示
Next
```

##### ・特定のブックを開く・閉じる
```
Dim wb As Workbook
Set wb = Workbooks.Open("C:\Users\sample.xlsx") ' ファイルを開く
wb.Save ' 上書き保存
wb.Close ' 閉じる
```

##### ・新しいブックを作成
```
Dim newWb As Workbook
Set newWb = Workbooks.Add ' 新規作成
newWb.SaveAs "C:\Users\newfile.xlsx" ' 保存
```
:::note info
.Save だけだと上書き保存、.SaveAs で名前をつけて保存。
:::

:::note warn
.Close するときに .Save していないと変更内容が破棄される。
:::

#### 3. Worksheets & Worksheet（シートの管理）
Excelのシート（タブ）を操作するオブジェクト。

##### ・特定のシートを指定
```
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1") ' シート名で指定
ws.Activate ' シートをアクティブにする
```

##### ・シートを追加・削除

```
Sheets.Add After:=Sheets(Sheets.Count) ' 最後のシートの後に追加
Sheets("Sheet2").Delete ' 指定シートを削除
```

##### ・シート名を変更
```
Sheets("Sheet1").Name = "検査データ"
```
##### ・すべてのシートを取得
```
Dim ws As Worksheet
For Each ws In ThisWorkbook.Sheets
    Debug.Print ws.Name ' シート名を出力
Next
```

:::note warn
Sheets(1) のように番号で指定すると、シートの順番が変わると誤動作の原因になるため、Sheets("シート名") を推奨。
:::


#### 4. Range（セルの範囲）

セルや範囲を操作するオブジェクト。

##### ・特定のセルを指定

```
Dim rng As Range
Set rng = Sheets("Sheet1").Range("A1")
rng.Value = "検査結果" ' A1セルに値を入力
```

##### ・範囲を指定
```
Sheets("Sheet1").Range("A1:B3").Value = "OK"
' A1からB3の範囲をまとめて変更
```

##### ・行や列を選択
```
Sheets("Sheet1").Rows(2).Delete ' 2行目を削除
Sheets("Sheet1").Columns("B").Insert ' B列を挿入
```
##### ・セルの書式設定
```
With Sheets("Sheet1").Range("A1")
    .Font.Bold = True ' 太字
    .Font.Size = 14 ' フォントサイズ
    .Interior.Color = RGB(255, 255, 0) ' 背景色（黄色）
End With
```
##### ・条件に応じてセルを操作
```
Dim cell As Range
For Each cell In Sheets("Sheet1").Range("A1:A10")
    If cell.Value > 100 Then
        cell.Interior.Color = RGB(255, 0, 0) ' 100以上なら赤色
    End If
Next
```

:::note
Range("A1") は 「A1」セルを指すオブジェクト であり、.Value で値を取得・設定できる。
.Interior.Color は背景色、.Font はフォント設定に関わるプロパティ。
:::

#### 5. Cells（個々のセル）

Rangeの一部として、個々のセルを操作できる。

##### ・行・列番号でセルを指定
```
Sheets("Sheet1").Cells(1, 1).Value = "検査結果" ' A1セルに入力
```

##### ・セルのループ処理
```
Dim i As Integer
For i = 1 To 10
    Sheets("Sheet1").Cells(i, 1).Value = i ' A列に1～10を入力
Next i
```

:::note info
Cells(行番号, 列番号) の形式で指定するので、変数を使ったループ処理に活用する
:::

#### 6. まとめ

| オブジェクト | 説明 | 例 |
|:-:|:-:|:-:|
|  Application |  Excel全体	 | Application.ScreenUpdating = False  |
|  Workbook |  Excelファイル | Workbooks.Open("sample.xlsx")  |
|  Worksheet |  シート |  Sheets("Sheet1").Name = "検査データ" |
|  Range |  セル範囲 | Range("A1:B3").Value = "OK"  |
| Cells  | 個々のセル  | Cells(1, 1).Value = "A1"  |

## 6. 変数のスコープ

| 1 | 2 | 3 |
|:-:|:-:|:-:|
| ローカル変数  | Dim を関数内で宣言  | その関数内のみ  |
| モジュールレベル変数  | Dim をモジュールの先頭で宣言  |  そのモジュール内 |
|  グローバル変数 |  Public をモジュールの先頭で宣言	 | 全モジュールで使用可  |

#### ・ローカル変数（関数内だけ有効）
```
Sub TestScope()
    Dim x As Integer
    x = 10
    MsgBox x ' 10と表示
End Sub
```

#### ・モジュールレベル変数（モジュール全体で有効）
```
Dim y As Integer ' モジュールの先頭で宣言

Sub SetY()
    y = 20
End Sub

Sub ShowY()
    MsgBox y ' SetYを実行した後なら 20 と表示
End Sub
```
#### ・グローバル変数（全モジュールで有効）
```
Public z As Integer ' 全モジュールで有効
```

## 7. Withステートメント
Excelのセルやシートなど、同じオブジェクトに対する処理をまとめるのが __With__ の役割。必須。

- With を使わない
```
Sheets("Sheet1").Range("A1").Font.Bold = True
Sheets("Sheet1").Range("A1").Font.Color = RGB(255, 0, 0)
Sheets("Sheet1").Range("A1").Interior.Color = RGB(255, 255, 0)
```

-  With を使う
```
With Sheets("Sheet1").Range("A1")
    .Font.Bold = True
    .Font.Color = RGB(255, 0, 0)
    .Interior.Color = RGB(255, 255, 0)
End With
```

## 8. エラートラップ

#### 1. On Error ステートメント

VBAでは __On Error__ ステートメントを使って、エラー発生時の動作を制御する。

##### ・On Error Resume Next（エラーを無視して次の行へ）

基本的にはデバッグ用に時々使う
```
Sub Example1()
    On Error Resume Next  ' エラーが発生しても次の行へ
    Dim x As Integer
    x = 10 / 0  ' 0で割るエラー（実行時エラー発生）

    MsgBox "処理が続行されました"
End Sub
```

:::note warn
・エラーを検知できない
・例外的に使うべき手法（乱用するとデバッグが難しくなる）
:::


##### ・On Error GoTo ラベル名（エラー発生時にジャンプ）

エラーが発生したら指定したラベルにジャンプし、エラー処理を実行。
```
Sub Example2()
    On Error GoTo ErrorHandler  ' エラー発生時に「ErrorHandler」へジャンプ
    Dim x As Integer
    x = 10 / 0  ' 0で割るエラー

    Exit Sub  ' 正常終了時はエラーハンドラを飛ばす

ErrorHandler:
    MsgBox "エラーが発生しました: " & Err.Description
End Sub
```
:::note info
・エラー時に指定の処理を実行できる
・Err.Number や Err.Description でエラー情報を取得可能
・Exit Sub を使ってエラーハンドラをスキップできる
:::
　
##### ・On Error GoTo 0（エラーハンドリングを無効化）

On Error の設定をリセットし、通常のエラー処理に戻す

```
Sub Example3()
    On Error Resume Next  ' エラーを無視
    Dim x As Integer
    x = 10 / 0  ' ここではエラー無視

    On Error GoTo 0  ' ここから通常のエラーチェックに戻す
    y = 10 / 0  ' ここでエラー発生（プログラムが停止）
End Sub
```

:::note
・On Error Resume Next の影響を解除
・以降のエラーは通常通り停止する
:::

#### 2.  Err オブジェクト（エラー情報の取得）

エラーが発生した場合、Err オブジェクトを使うと詳細情報を取得できる

| プロパティ | 説明 |
|:-:|:-:|
| Err.Number  |  エラー番号 |
|  Err.Description |  エラーメッセージ |
| Err.Source  | エラーの発生元  |
| Err.Clear  |  エラー情報をクリア |

- Err オブジェクトを使ったエラーハンドリング

```
Sub Example4()
    On Error Resume Next
    Dim x As Integer
    x = 10 / 0  ' 0除算エラー

    If Err.Number <> 0 Then
        MsgBox "エラー番号: " & Err.Number & vbNewLine & "内容: " & Err.Description
        Err.Clear  ' エラー情報をクリア
    End If
End Sub
```

```
Sub Example5()
    On Error GoTo ErrorHandler  ' エラーハンドリングを設定

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("NotExist")  ' 存在しないシートを指定（エラー）

    Exit Sub  ' 正常終了時はエラーハンドラをスキップ

ErrorHandler:
    MsgBox "シートが見つかりませんでした: " & Err.Description
End Sub
```

- デバッグ中想定

```
Sub DebugMode()
    On Error Resume Next  ' エラーを無視

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("NotExist")  ' 存在しないシート

    If Err.Number <> 0 Then
        Debug.Print "エラー番号: " & Err.Number & " - " & Err.Description
        Err.Clear  ' エラー情報をクリア
    End If

    On Error GoTo 0  ' エラーハンドリングを解除
End Sub
```

:::note info
・Debug.Print を使ってイミディエイトウィンドウにエラー情報を出力
　⇒ __ログの解析が一番大事__
・Err.Clear でエラー情報をクリア
:::

- 本番環境想定

```
Sub ProductionMode()
    On Error GoTo ErrorHandler  ' エラーハンドリングを有効化

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("NotExist")  ' 存在しないシート（エラー発生）

    Exit Sub  ' 正常終了時はエラーハンドラをスキップ

ErrorHandler:
    MsgBox "エラー発生: " & Err.Description, vbCritical, "エラー"
    ' 必要に応じてログ出力やエラー通知処理
    Err.Clear  ' エラー情報をクリア
End Sub
```

-  __CInt, CDbl, IsNumericを活用したエラーハンドリングも適宜活用__ 
```
If IsNumeric(TextBox_BP.Text) Then
    bloodPressure = CInt(TextBox_BP.Text)
Else
    MsgBox "数値を入力してください！", vbExclamation
End If
```


:::note warn
・On Error GoTo ラベル を使い、エラー発生時の処理を明確に分岐させることは必須
:::

:::note info
・On Error GoTo ErrorHandler を使い、エラー発生時に専用の処理を実行
・MsgBox でユーザーに通知（またはログ出力）
・Err.Clear でエラーをリセット
:::

## 9. エラー時の情報の記録方法

#### 1. ログファイルに記録する

エラーが発生したら、日時・エラー内容をテキストファイルに書き込む。


```
Sub SampleWithLogging()
    On Error GoTo ErrorHandler  ' エラーハンドリング開始
    
    ' ここで何らかの処理
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("NotExist")  ' 存在しないシート → エラー発生

    Exit Sub  ' 正常終了ならエラーハンドラをスキップ

ErrorHandler:
    Call WriteErrorLog(Err.Number, Err.Description)  ' ログに記録
    MsgBox "エラーが発生しました。ログを確認してください。", vbCritical, "エラー"
    Err.Clear  ' エラー情報をクリア
End Sub

Sub WriteErrorLog(ByVal ErrNum As Long, ByVal ErrDesc As String)
    Dim FilePath As String
    FilePath = ThisWorkbook.Path & "\ErrorLog.txt"  ' ログファイルのパス

    Dim FileNum As Integer
    FileNum = FreeFile()
    
    Open FilePath For Append As #FileNum
    Print #FileNum, Now & " - エラー番号: " & ErrNum & " - " & ErrDesc
    Close #FileNum
End Sub
```

:::note info
✔ エラー発生時に自動でErrorLog.txt に記録 し、あとから確認できるようにする
✔ MsgBox でユーザーに通知してもよいが、頻繁に出ると迷惑になるので状況次第
:::

#### 2. Excelシートにエラー情報を記録する

エンドユーザーでも簡単に確認できるように、エラー情報を専用のExcelシートに記録する方法も〇。

```
Sub SampleWithErrorSheet()
    On Error GoTo ErrorHandler
    
    ' ここで何らかの処理
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("NotExist")  ' 存在しないシート → エラー発生

    Exit Sub

ErrorHandler:
    Call LogErrorToSheet(Err.Number, Err.Description)
    MsgBox "エラーが発生しました。ログシートを確認してください。", vbCritical, "エラー"
    Err.Clear
End Sub

Sub LogErrorToSheet(ByVal ErrNum As Long, ByVal ErrDesc As String)
    Dim ws As Worksheet
    On Error Resume Next
    Set ws = ThisWorkbook.Sheets("ErrorLog")
    On Error GoTo 0

    If ws Is Nothing Then
        Set ws = ThisWorkbook.Sheets.Add
        ws.Name = "ErrorLog"
        ws.Cells(1, 1).Value = "日時"
        ws.Cells(1, 2).Value = "エラー番号"
        ws.Cells(1, 3).Value = "エラー内容"
    End If

    Dim LastRow As Long
    LastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row + 1

    ws.Cells(LastRow, 1).Value = Now
    ws.Cells(LastRow, 2).Value = ErrNum
    ws.Cells(LastRow, 3).Value = ErrDesc
End Sub
```

:::note info
✔ 「ErrorLog」シートがなければ自動作成し、エラー情報を書き込む
✔ テキストファイルよりExcelシートの方が、現場での確認が楽な場合もある
:::

#### 3. メール通知を組み込む（Outlook連携）

重大なエラーが発生した場合、担当者にメール通知を送る仕組みを組み込むことも可能。

```Sub SampleWithMail()
    On Error GoTo ErrorHandler
    
    ' ここで何らかの処理
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("NotExist")  ' 存在しないシート → エラー発生

    Exit Sub

ErrorHandler:
    Call SendErrorMail(Err.Number, Err.Description)
    MsgBox "エラーが発生しました。管理者に通知しました。", vbCritical, "エラー"
    Err.Clear
End Sub

Sub SendErrorMail(ByVal ErrNum As Long, ByVal ErrDesc As String)
    Dim OutApp As Object
    Dim OutMail As Object

    Set OutApp = CreateObject("Outlook.Application")
    Set OutMail = OutApp.CreateItem(0)

    With OutMail
        .To = "admin@example.com"
        .Subject = "【緊急】エラー発生通知"
        .Body = "日時: " & Now & vbNewLine & _
                "エラー番号: " & ErrNum & vbNewLine & _
                "エラー内容: " & ErrDesc
        .Send
    End With

    Set OutMail = Nothing
    Set OutApp = Nothing
End Sub
```

:::note info
✔ エラー発生時に管理者へメール通知を送信（Outlookがインストールされている環境で使用可）
✔ 重大なエラーのみ送信し、過剰な通知は避ける
:::

 日常的なエラーはExcelシート or テキストログに残し
 重大なエラーはメール通知で即座に対応できるようにする。

 
## 10.  インターフェース作成に必要な基礎知識

VBAでは UserForm（ユーザーフォーム） を使って、独自のUIを作成できる

#### 1. UserFormの作成
 1. ExcelのVBAエディタ（Alt + F11）を開く
 2. 「挿入」 → 「UserForm」を選択
 3. ボタンやテキストボックスを追加し、コードを記述

#### 2. コントロールの基本
###### ・ボタン（CommandButton）- クリック時の処理を記述

```
Private Sub CommandButton1_Click()
    MsgBox "ボタンがクリックされました"
End Sub
```
###### ・テキストボックス（TextBox）- 入力値を取得する

```
Dim inputText As String
inputText = TextBox1.Value
MsgBox "入力された値: " & inputText
```
###### ・ラベル（Label）- テキストを変更

```
Label1.Caption = "こんにちは"
```

###### ・リストボックス（ListBox）- アイテムを追加
```
ListBox1.AddItem "選択肢1"
ListBox1.AddItem "選択肢2"
```

選択された値を取得

```
Dim selectedItem As String
selectedItem = ListBox1.Value
MsgBox "選択された項目: " & selectedItem
```

###### ・コンボボックス（ComboBox）- ドロップダウンリストを作成
```
ComboBox1.AddItem "オプション1"
ComboBox1.AddItem "オプション2"
```

#### 3. UIを使用したサンプルコード

##### ・ボタンをクリックするとテキストボックスの値を取得し、メッセージボックスに表示するサンプル

```
Private Sub CommandButton1_Click()
    Dim userInput As String
    userInput = TextBox1.Value
    MsgBox "入力された値: " & userInput
End Sub
```

- 実装手順
1. UserFormを追加
2. TextBox（名前: TextBox1）とCommandButton（名前: CommandButton1）を配置
3. 上記コードをCommandButtonのクリックイベントに記述
4. VBAでのフォーム表示
VBAでUserFormを表示するには、標準モジュールに以下のコードを追加。

```
Sub ShowForm()
    UserForm1.Show
End Sub
```

つづく