技術仕様・設計

キャレット位置取得技術仕様・解説書 (Caret Position Technical Guide)

本書は、多様な Windows アプリケーション(Win32、Chromiumブラウザ、Electron/VSCode、UWP等)において、キャレット(入力カーソル)座標を正確に追従取得するためのハイ...

低遅延・低負荷で高精度なキャレット追従を実現するハイブリッド取得エンジンの技術解説

本書は、多様な Windows アプリケーション(Win32、Chromiumブラウザ、Electron/VSCode、UWP等)において、キャレット(入力カーソル)座標を正確に追従取得するためのハイブリッド技術仕様を解説するドキュメントです。


目次


1. ハイブリッドキャレット取得アーキテクチャ

fjFlow は、定期タイマー(30ms間隔)によってアクティブウィンドウのキャレット位置を追跡します。その中核である GetCaret 関数は、以下の優先順位とフォールバック戦略に基づいて最適な手法を選択します。

graph TD
    A[WatchCaret 定期タイマー 30ms] --> B{アクティブウィンドウ判定}
    B -- Chromium系ブラウザ --> C[1. GetCaretViaUIA]
    B -- その他アプリ --> D[2. GetCaretViaOleacc]
    C -- 成功 --> E[キャッシュ保存 & インジケーター表示]
    C -- 失敗 --> D
    D -- 成功 --> E
    D -- 失敗 --> F[3. GetCaretViaUIA]
    F -- 成功 --> E
    F -- 失敗 --> G[4. CaretGetPos 標準API]
    G -- 成功 --> E
    G -- 失敗 --> H[取得失敗 / 前回座標維持]

2. 負荷軽減とキャッシュ制御の仕組み

COM操作に伴うCPU負荷を極小化するため、以下のキャッシュ制御を導入しています:

  • ウィンドウ・フォーカス変化検知: アクティブウィンドウやフォーカスコントロールが切り替わった場合、またはウィンドウ位置・サイズが変化した場合のみ、キャッシュをクリアして再取得します。
  • 休止モードによる CPU 0% 化: キー入力やマウス操作が 300ms 以上途絶えている場合、キャレットは静止していると判定し、API呼び出しをスキップして前回のキャッシュ座標(lastX, lastY)を即座に返します。

3. 各種キャレット取得手法の詳細

3.1 Active Accessibility (MSAA / oleacc) による Win32 アプリの取得

伝統的な Win32 アプリ(メモ帳等)に対し、oleacc.dll の AccessibleObjectFromWindow API を使用して OBJID_CARET (0xFFFFFFF8) を指定し、スクリーン座標を取得します。軽量でオーバーヘッドが極小です。

3.2 UI Automation (UIA) によるモダンアプリの取得

Chrome、Edge、VSCode などの独自描画環境に対し、TextPattern2 の GetCaretRange、または TextPattern の GetSelection(長さ0の選択範囲)を探索してバウンディングボックスを取得します。

3.3 SAFEARRAY のメモリリーク防止技術 (F_OWNVALUE)

UIA から座標配列(SAFEARRAY)を取得する際、COMポインタの解放漏れを防ぐため、ComValue(0x2005, psa) でラップした上で ComObjFlags(Rect, 1)(F_OWNVALUE = 1)を設定し、スコープを抜けた際に AutoHotkey エンジンが自動的に SafeArrayDestroy を呼び出すよう制御しています。

3.4 マルチモニター環境における DPI スケーリング補正

マルチモニター環境でモニターごとにDPI拡大率が異なる場合でも座標がズレないよう、取得スレッドに対して SetThreadDpiAwarenessContext で一時的に -3(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)を設定し、物理スクリーン座標を取得した後に安全復元します。


4. Chromium系ブラウザのアクセシビリティ自動復旧ロジック

Chrome や Edge は、アイドル時にアクセシビリティツリーへの応答を停止することがあります。

  1. Chromium の描画ウィンドウ(Chrome_RenderWidgetHostHWND)を検出します。
  2. WM_GETOBJECT (0x003D) メッセージを送信し、内部のアクセシビリティツリー構築を強制ウェイクアップします。
  3. 取得失敗が100回連続した場合は、キャッシュフラグをクリアして自動修復を再試行します。

5. スクロールおよびマウスクリック時の同期フック制御

ブラウザ上でスクロールした場合、文字インデックスが変わらないため座標更新通知が届かない場合があります。

  • マウスホイール(WheelUp / WheelDown)をグローバルフックし、ホイール回転を検知した瞬間に キャッシュ座標を強制破棄 してリアルタイムに新規座標を再取得します。
  • マウスクリック時も同様に休止モードから即座に復帰し、新しい入力位置へインジケーターをジャンプさせます。