# 通知設計書 3-Ticker Callback

## 概要

本ドキュメントは、VBCorLibライブラリにおけるTicker Callbackコールバック通知の設計を定義する。Tickerクラスはタイマー完了時にAddressOf演算子で指定されたコールバック関数を呼び出す機能を持つ。

### 本通知の処理概要

Ticker Callbackは、Tickerクラスのタイマーイベント発火時にコールバック関数を呼び出す通知機構である。WithEventsによるイベント通知とは別に、関数ポインタを使用した軽量なコールバック方式を提供する。

**業務上の目的・背景**：VB6のWithEventsはクラスモジュール内でのみ使用可能であり、標準モジュール（.bas）からはタイマーイベントを受信できない制約がある。Ticker Callbackは、AddressOf演算子で任意の関数をコールバックとして指定できるため、標準モジュールからでもタイマー処理を実装できる。また、WithEventsを使用しない軽量な実装が可能となり、パフォーマンスが向上する。

**通知の送信タイミング**：タイマー間隔（Interval）が経過し、OnElapsedメソッドが呼び出された時点でコールバックが実行される。Elapsedイベント発火の直後に呼び出される。

**通知の受信者**：NewTicker関数のAddressOfCallback引数で指定された関数。標準モジュール内のPublic Subプロシージャとして定義する必要がある。

**通知内容の概要**：コールバック関数には、Tickerオブジェクト参照とユーザー定義データ（Data）がByRef引数として渡される。これにより、コールバック関数内でTickerの状態を参照・変更できる。

**期待されるアクション**：コールバック関数内で、定期的に実行すべき処理を実装する。必要に応じてTicker引数を使用してタイマーを停止・再設定することも可能。

## 通知種別

コールバック通知（関数ポインタによるコールバック）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（Windows Timerコールバック経由） |
| 優先度 | 中（Windows Timerの優先度に依存） |
| リトライ | 無（AutoResetによる自動繰り返しあり） |

### 送信先決定ロジック

1. NewTicker関数のAddressOfCallback引数でコールバック関数のアドレスを指定
2. コールバック関数はAction_T_T型（2引数のActionデリゲート）として呼び出される
3. mCallback.Invokeメソッドで実際の関数呼び出しが行われる

## 通知テンプレート

### コールバック関数シグネチャ

| 項目 | 内容 |
|-----|------|
| 関数名 | 任意（AddressOfで指定） |
| 引数1 | ByRef Ticker As Ticker |
| 引数2 | ByRef Data As Variant |
| 戻り値 | なし（Sub） |

### コールバック関数テンプレート

```vb
' 標準モジュール（.bas）内
Public Sub TimerCallback(ByRef Ticker As Ticker, ByRef Data As Variant)
    ' タイマーコールバック処理
    Debug.Print "Timer callback at " & Now
    Debug.Print "Data: " & Data

    ' 必要に応じてタイマーを停止
    If SomeCondition Then
        Ticker.StopTicker
    End If
End Sub
```

### 使用例

```vb
' 標準モジュール（.bas）内
Private mTicker As Ticker

Public Sub Main()
    ' コールバック関数を指定してTickerを作成
    Set mTicker = NewTicker(1000, "MyData", True, AddressOf TimerCallback)
    mTicker.StartTicker
End Sub

Public Sub TimerCallback(ByRef Ticker As Ticker, ByRef Data As Variant)
    Debug.Print "Callback invoked with data: " & Data
End Sub
```

### 添付ファイル

該当なし（プログラム内部コールバック）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| Ticker | Tickerオブジェクト参照 | 呼び出し元Tickerインスタンス | Yes |
| Data | ユーザー定義データ | Ticker.Data プロパティ | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| タイマー | OnElapsed実行 | mCallback Is Not Nothing | Elapsedイベント発火後にコールバック実行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| AddressOfCallback未指定 | NewTicker呼び出し時にコールバックを指定しなかった場合 |
| mCallback Is Nothing | コールバックデリゲートが未初期化の場合 |
| タイマー停止中 | StartTickerが呼び出されていない、またはStopTicker後 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[NewTicker呼び出し] --> B{AddressOfCallback指定あり?}
    B -->|Yes| C[InitDelegate呼び出し]
    B -->|No| D[mCallback = Nothing]
    C --> E[mCallback = Action_T_T型デリゲート]
    D --> F[タイマー開始]
    E --> F
    F --> G[Interval経過]
    G --> H[OnElapsed呼び出し]
    H --> I[RaiseEvent Elapsed]
    I --> J{mCallback Is Nothing?}
    J -->|Yes| K[終了]
    J -->|No| L[mCallback.Invoke Me, mData]
    L --> M[コールバック関数実行]
    M --> K
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（メモリ内処理のみ）

### 更新テーブル一覧

該当なし（メモリ内処理のみ）

#### 内部データ構造

| メンバ変数 | 型 | 用途 |
|-----------|---|------|
| mDelegate | Delegate | デリゲート構造体 |
| mCallback | Action_T_T | コールバック呼び出し用インターフェース |
| mData | Variant | ユーザー定義データ |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 不正なアドレス | 無効な関数アドレス指定 | AddressOf演算子で有効な関数を指定 |
| シグネチャ不一致 | コールバック関数の引数が不正 | 正しいシグネチャ（ByRef Ticker, ByRef Data）で定義 |
| OutOfMemory | デリゲート作成時のメモリ不足 | メモリを解放してから再試行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（自動リトライなし） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 最小間隔 | Intervalプロパティ依存 |
| 最大間隔 | Long型の最大値 |

### 配信時間帯

制限なし

## セキュリティ考慮事項

- AddressOf演算子はコンパイル時に検証されるため、不正なアドレス指定のリスクは低い
- コールバック関数は呼び出し元と同じセキュリティコンテキストで実行される
- Class_TerminateでObjectPtr(mCallback) = vbNullPtrを設定し、アプリケーション終了時のクラッシュを防止

## 備考

- Elapsedイベントとコールバックは同時に発火する（排他ではない）
- コールバック内で発生したエラーはTickerSharedのコールバック処理に伝播する
- 長時間実行するアプリケーションでは、ObjectPtrを使用したコールバック解除が推奨される

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

コールバック機能に関連するデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Ticker.cls | `Source/CorLib/System.Threading/Ticker.cls` | mDelegate, mCallbackメンバ変数（88-89行目） |
| 1-2 | Delegation.bas | `Source/CorLib/System/Delegation.bas` | Delegate構造体（44-47行目）、軽量COMオブジェクト |

**読解のコツ**: mDelegateはDelegate構造体（UDT）、mCallbackはAction_T_T型のCOMインターフェースとして扱われる。

#### Step 2: コールバック登録処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Ticker.cls | `Source/CorLib/System.Threading/Ticker.cls` | Init Sub（271-283行目）でコールバック設定 |
| 2-2 | Delegation.bas | `Source/CorLib/System/Delegation.bas` | InitDelegate関数（99-104行目） |

**主要処理フロー**:
1. **276行目**: AddressOfCallback <> vbNullPtr のチェック
2. **281行目**: InitDelegate(mDelegate, AddressOfCallback)でデリゲート初期化
3. **99-104行目**: Struct.pfnに関数ポインタ設定、OKVTablePtrでVTable設定

#### Step 3: コールバック呼び出し処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Ticker.cls | `Source/CorLib/System.Threading/Ticker.cls` | OnElapsed Sub（255-265行目） |

**主要処理フロー**:
- **260行目**: RaiseEvent Elapsed(mData) でイベント発火
- **262行目**: If Not mCallback Is Nothing Then でコールバック有無チェック
- **263行目**: mCallback.Invoke Me, mData でコールバック呼び出し

#### Step 4: クリーンアップ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Ticker.cls | `Source/CorLib/System.Threading/Ticker.cls` | Class_Terminate（289-292行目） |

**主要処理フロー**:
- **290行目**: StopTicker でタイマー停止
- **291行目**: ObjectPtr(mCallback) = vbNullPtr で参照解除（クラッシュ防止）

### プログラム呼び出し階層図

```
[ユーザーコード]
    │
    └─ NewTicker(Interval, Data, AutoReset, AddressOf CallbackFunc)
           │
           └─ Ticker.Init(Interval, Data, AutoReset, AddressOfCallback)
                  │
                  ├─ AddressOfCallback <> vbNullPtr チェック
                  │
                  └─ Set mCallback = InitDelegate(mDelegate, AddressOfCallback)
                         │
                         └─ Delegation.InitDelegate()
                                │
                                ├─ Struct.pfn = pfn (関数ポインタ設定)
                                │
                                └─ Struct.pVTable = mOkVTablePtr
                                       │
                                       ▼
                                [タイマー発火時]
                                       │
                                Ticker.OnElapsed()
                                       │
                                       ├─ RaiseEvent Elapsed(mData)
                                       │
                                       └─ mCallback.Invoke(Me, mData)
                                              │
                                              └─ [ユーザー定義コールバック関数]
```

### データフロー図

```
[入力]                    [処理]                         [出力]

AddressOf            ───▶ InitDelegate          ───▶ mCallback
CallbackFunc               (Delegation.bas)           (Action_T_T)
(関数ポインタ)

Ticker.Data          ───▶ OnElapsed            ───▶ コールバック引数
(Variant)                  mCallback.Invoke          (ByRef Data)

Ticker               ───▶ OnElapsed            ───▶ コールバック引数
(インスタンス)              mCallback.Invoke          (ByRef Ticker)
                                │
                                ▼
                         CallbackFunc実行
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Ticker.cls | `Source/CorLib/System.Threading/Ticker.cls` | ソース | タイマークラス、コールバック保持と呼び出し |
| Delegation.bas | `Source/CorLib/System/Delegation.bas` | ソース | デリゲート作成・初期化機能 |
| TickerShared.bas | `Source/CorLib/System.Threading/TickerShared.bas` | ソース | タイマーコールバック経由でOnElapsed呼び出し |
| Constructors.cls | `Source/CorLib/System/Constructors.cls` | ソース | NewTicker関数定義 |
