# 機能設計書 106-「ここでTerminalを開く」

## 概要

本ドキュメントは、WindowsエクスプローラーのコンテキストメニューからWindows Terminalを起動するシェル拡張機能について記述する。COM（Component Object Model）ベースのIExplorerCommand実装として、エクスプローラーの右クリックメニューに「ここでTerminalを開く」エントリを追加し、選択されたフォルダまたは現在のフォルダを作業ディレクトリとしてTerminalを起動する。

### 本機能の処理概要

**業務上の目的・背景**：ファイルシステム操作中に、特定のフォルダでターミナル作業を開始したいケースは頻繁にある。エクスプローラーから直接Terminalを起動できるようにすることで、手動でcdコマンドを入力する手間を省き、開発者の生産性を向上させる。

**機能の利用シーン**：ユーザーがエクスプローラーでフォルダを右クリックした際、コンテキストメニューに「Open in Terminal」項目が表示される。クリックするとそのフォルダを作業ディレクトリとしてWindows Terminalが起動する。Ctrl+Shiftを押しながらクリックすると、管理者権限で起動する。

**主要な処理内容**：
1. IExplorerCommandインターフェースの実装によるコンテキストメニュー項目の提供
2. 選択アイテムまたは現在のフォルダパスの取得
3. WindowsTerminal.exe（または管理者昇格シム）のCreateProcessWによる起動
4. ブランディング（Release/Preview/Canary/Dev）に応じたメニュー項目名とGUIDの切り替え

**関連システム・外部連携**：Windows Shell（Explorer）、COM（CoCreatableClass）、WRL（Windows Runtime Library）。

**権限による制御**：Ctrl+Shift押下時は管理者昇格用のElevateShimExeを使用して起動する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | Windowsエクスプローラー（外部） | 主機能 | コンテキストメニューからTerminalを起動 |

## 機能種別

COMシェル拡張 / プロセス起動

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| psiItemArray | IShellItemArray* | No | 選択されたシェルアイテムの配列 | NULLの場合はサイトから取得 |
| pBindContext | IBindCtx* | No | バインドコンテキスト（未使用） | - |
| Ctrl+Shift | キー状態 | No | 管理者昇格フラグ | GetAsyncKeyState確認 |

### 入力データソース

- エクスプローラーからのCOMコールバック（IExplorerCommand::Invoke）
- エクスプローラーのIFolderViewインターフェース（現在のフォルダ取得用）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| HRESULT | HRESULT | 操作結果（S_OK/S_FALSE/エラー） |
| 子プロセス | PROCESS_INFORMATION | 起動されたTerminalプロセス |

### 出力先

- 新しいWindowsTerminal.exeプロセスの起動

## 処理フロー

### 処理シーケンス

```
1. Invoke呼び出し
   |-- Ctrl+Shiftキー状態確認 (IsControlAndShiftPressed)
   |-- 作業ディレクトリの取得
   |   |-- psiItemArrayから選択アイテム取得を試行
   |   |-- 失敗時: IFolderView経由で現在のフォルダを取得 (GetLocationFromSite)
   |   +-- 両方失敗: S_FALSE返却
   |-- ファイルシステムパス取得 (GetDisplayName SIGDN_FILESYSPATH)
   |-- コマンドライン構築
   |   |-- モジュールパスからWindowsTerminal.exeパスを算出
   |   |-- 昇格時: ElevateShimExeに置換
   |   +-- "-d <パス>" 引数を付加
   +-- CreateProcessW実行

2. GetState呼び出し（表示制御）
   |-- 選択アイテムまたは現在フォルダを取得
   |-- ファイルシステムアイテムかどうか確認 (SFGAO_FILESYSTEM)
   |-- 圧縮フォルダかどうか確認 (SFGAO_FOLDER | SFGAO_STREAM)
   +-- 条件に応じてECS_ENABLED/ECS_HIDDENを返却
```

### フローチャート

```mermaid
flowchart TD
    A[コンテキストメニュー選択] --> B[Invoke呼び出し]
    B --> C{Ctrl+Shift押下?}
    C -->|Yes| D[ElevateShimExe使用]
    C -->|No| E[WindowsTerminal.exe使用]
    D --> F[作業ディレクトリ取得]
    E --> F
    F --> G{選択アイテムあり?}
    G -->|Yes| H[選択パス使用]
    G -->|No| I{サイトから取得可能?}
    I -->|Yes| J[現在フォルダ使用]
    I -->|No| K[S_FALSE返却]
    H --> L[コマンドライン構築]
    J --> L
    L --> M[CreateProcessW]
    M --> N[S_OK返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-106-1 | ファイルシステム限定 | ファイルシステムアイテムでない場合はメニュー非表示 | GetState呼び出し時 |
| BR-106-2 | 圧縮フォルダ除外 | 圧縮フォルダ(SFGAO_FOLDER + SFGAO_STREAM)は除外 | GetState呼び出し時 |
| BR-106-3 | 管理者昇格 | Ctrl+Shift同時押下で管理者権限起動 | Invoke時 |
| BR-106-4 | ブランディング | Release/Preview/Canary/Devで異なるメニュー表示名とGUID | 常時 |

### 計算ロジック

特殊な計算ロジックはない。

## データベース操作仕様

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| S_FALSE | 場所なし | 有効なフォルダが取得できない | 何もしないで返却 |
| HRESULT失敗 | シェルアイテム取得失敗 | IShellItem操作失敗 | RETURN_IF_FAILEDで返却 |
| Win32エラー | プロセス作成失敗 | CreateProcessW失敗 | RETURN_IF_WIN32_BOOL_FALSEで返却 |

### リトライ仕様

リトライは不要。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- コンテキストメニュー表示時のGetState/GetTitle/GetIcon呼び出しは高速であること
- Invokeは新規プロセス起動を伴うため、数百ミリ秒程度のレイテンシが発生する

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

- Ctrl+Shift押下時の管理者昇格は、ElevateShimExeを通じてUAC確認が行われる
- ハンドル継承は無効(bInheritHandles=false)に設定

## 備考

- ブランディングに応じた4種類のCLSIDが定義されている（Release: 9f156763、Preview: 02db545a、Canary: 6119575F、Dev: 52065414）
- アイコンはWindowsTerminal.exe,-101（最初のアイコングループ）を使用
- VerbNameは"WindowsTerminalOpenHere"で固定
- WRL(Windows Runtime Library)のCoCreatableClassマクロでクラスファクトリを自動生成

---

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

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

### 推奨読解順序

#### Step 1: データ構造とインターフェースを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | OpenTerminalHere.h | `src/cascadia/ShellExtension/OpenTerminalHere.h` | OpenTerminalHereクラスの宣言。IExplorerCommandとIObjectWithSiteの実装(27-68行目) |

**読解のコツ**: 28-36行目のブランディング条件分岐（#if defined）で4種類のCLSIDが定義されていることを確認する。37行目のRuntimeClassテンプレートでCOM登録の仕組みを理解する。61-63行目のプライベートメソッドがヘルパー関数であることに注意する。

#### Step 2: メイン処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | OpenTerminalHere.cpp | `src/cascadia/ShellExtension/OpenTerminalHere.cpp` | Invoke処理(26-77行目)、GetState(105-128行目)、GetBestLocationFromSelectionOrSite(190-211行目) |

**主要処理フロー**:
1. **30行目**: IsControlAndShiftPressed()で管理者昇格判定
2. **33行目**: GetBestLocationFromSelectionOrSiteでフォルダパスを取得
3. **40行目**: GetDisplayNameでファイルシステムパスに変換
4. **51-52行目**: モジュールパスから実行ファイルパスを構築（昇格時はElevateShimExe）
5. **54-59行目**: コマンドラインを構築（`"path\WindowsTerminal.exe" -d "targetPath"`）
6. **61-72行目**: CreateProcessWでTerminalを起動

#### Step 3: 表示制御を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | OpenTerminalHere.cpp | `src/cascadia/ShellExtension/OpenTerminalHere.cpp` | GetState(105-128行目)、GetTitle(87-103行目)、GetIcon(130-141行目) |

**読解のコツ**: GetStateの123-125行目で、SFGAO_FILESYSTEMかつ非圧縮フォルダの場合のみメニューを表示する判定ロジックを確認する。

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

```
Windows Explorer (コンテキストメニュー)
    |
    +-- OpenTerminalHere::GetState            ... メニュー表示判定
    |       +-- GetBestLocationFromSelectionOrSite  ... パス取得
    |       |       +-- IShellItemArray::GetItemAt   ... 選択アイテム
    |       |       +-- GetLocationFromSite          ... フォルダビュー
    |       |               +-- IServiceProvider::QueryService
    |       |               +-- IFolderView::GetFolder
    |       +-- IShellItem::GetAttributes            ... 属性確認
    |
    +-- OpenTerminalHere::GetTitle            ... メニュー表示名
    |       +-- RS_(リソース文字列)            ... ブランディング分岐
    |
    +-- OpenTerminalHere::GetIcon             ... アイコン取得
    |       +-- wil::GetModuleFileNameW        ... DLLパス取得
    |
    +-- OpenTerminalHere::Invoke              ... メニュー実行
            +-- IsControlAndShiftPressed       ... Ctrl+Shift判定
            +-- GetBestLocationFromSelectionOrSite  ... パス取得
            +-- IShellItem::GetDisplayName     ... パス文字列変換
            +-- QuoteAndEscapeCommandlineArg   ... 引数エスケープ
            +-- CreateProcessW                 ... Terminal起動
```

### データフロー図

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

エクスプローラー               OpenTerminalHere                Terminal
  右クリック ---------> GetState (表示判定) ----------> ECS_ENABLED/HIDDEN
  メニュー選択 -------> Invoke                -------> WindowsTerminal.exe
                          |                              起動 (-d パス)
                          +-- Ctrl+Shift? --> ElevateShimExe (管理者)
                          +-- パス取得 -----> コマンドライン構築
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| OpenTerminalHere.h | `src/cascadia/ShellExtension/OpenTerminalHere.h` | ソース(ヘッダ) | OpenTerminalHereクラス宣言・CLSID定義 |
| OpenTerminalHere.cpp | `src/cascadia/ShellExtension/OpenTerminalHere.cpp` | ソース(実装) | コンテキストメニュー処理の実装全体 |
| WtExeUtils.h | `src/cascadia/WinRTUtils/inc/WtExeUtils.h` | ソース(ヘッダ) | WindowsTerminalExe/ElevateShimExe定数定義 |
| LibraryResources.h | `src/cascadia/WinRTUtils/inc/LibraryResources.h` | ソース(ヘッダ) | RS_マクロ（リソース文字列取得） |
| dllmain.cpp | `src/cascadia/ShellExtension/dllmain.cpp` | ソース(実装) | COM DLLエントリポイント・クラスファクトリ |
