- name
- windows-app-automation
- description
- Windows デスクトップアプリ(Win32・WPF・WinForms・UWP・メモ帳・タスクマネージャーなど)の GUI 自動化・UIテストスキル。「Windows アプリを自動化して」「デスクトップアプリをテストして」「ファイルダイアログを操作して」「Excelを自動操作して」などで発動。pywinauto/winauto 使用。Copilot・Kiro・WSL 対応。
- metadata
- {"version":"1.2.0","tier":"experimental","category":"implementation","tags":["windows","native-app","pywinauto","ui-automation","win32","wpf","winforms","desktop-automation","e2e-testing","wsl","copilot","kiro","cross-agent"]}
# windows-app-automation
Windows ネイティブアプリを自動化するときは、Python + pywinauto で自動化スクリプトを作成する。
## 対応エージェント環境
| 環境 | 実行場所 | winauto コマンド |
|------|---------|----------------|
| **Claude Code** (Windows) | PowerShell / CMD ターミナル | `winauto` または `python winauto.py` |
| **GitHub Copilot** (VS Code) | VS Code 統合ターミナル(PowerShell) | `winauto` または `python winauto.py` |
| **Kiro** (AWS IDE) | Kiro 統合ターミナル(PowerShell) | `winauto` または `python winauto.py` |
| **WSL** (WSL2 端末) | bash/zsh from VS Code / Kiro / Windows Terminal | `winauto`(ラッパー経由で Windows Python を呼ぶ) |
> **注意**: pywinauto は Windows 専用。WSL から呼ぶ場合は Windows 側 Python で実行される。
---
## セットアップ(初回のみ)
### Windows ネイティブ(Claude Code / Copilot / Kiro 共通)
```powershell
# 依存ライブラリと winauto コマンドのインストール
python tools/winauto/install.py
# インストール確認(doctor が pywinauto・デスクトップ到達性・ロック状態を確認する)
winauto doctor
winauto apps
```
### WSL 端末から使う場合
```bash
# Windows 側 Python を自動検出してインストール
python tools/winauto/install.py
# インストール確認(新しい端末で)。doctor は WSL→Windows のブリッジを
# 端から端まで確認する: interop → ラッパー → Windows Python → デスクトップ到達性
winauto doctor
winauto apps
```
`winauto doctor` は **WSL 側でも Windows 側でも動く唯一のコマンド**で、WSL から実行すると
ラッパー越しに Windows 側の doctor を呼び出して所見を合流させる。`--output json` で機械可読。
問題があれば終了コード 1 を返すので、セットアップスクリプトからも判定に使える。
インストーラーが Windows 側 Python を見つけられない場合:
```bash
# Windows Python のパスを確認
cmd.exe /c where python
# 手動で Windows 側に pip インストール
cmd.exe /c python -m pip install pywinauto Pillow pywin32 comtypes
```
### 依存ライブラリのみインストール(既存 Python 環境に追記)
```powershell
pip install pywinauto>=0.6.9 Pillow>=9.0.0 pywin32>=306 comtypes>=1.4.0
```
---
## 環境別の実行方法
### Claude Code / GitHub Copilot / Kiro(Windows ターミナル)
これらはすべて Windows 上で動作するため、同じコマンドが使える。
```powershell
# winauto CLI(インストール済みなら直接呼べる)
winauto apps
winauto tree --app notepad
winauto click "name:=OK" --app notepad
# またはリポジトリから直接
python tools/winauto/winauto.py apps
python tools/winauto/winauto.py tree --app notepad
# ヘルパースクリプトも同様
python .github/skills/windows-app-automation/scripts/element_inspector.py --list
```
**Copilot(VS Code)固有の注意点:**
- VS Code のターミナルが PowerShell の場合、`python` コマンドが正しく通るか確認する
- `python --version` で Python 3.9 以上が表示されることを確認する
**Kiro IDE 固有の注意点:**
- Kiro の統合ターミナルから実行する(PowerShell)
- Kiro エージェントは直接 `!winauto tree --app notepad` のようにシェルコマンドを呼べる
- スキルの自動化スクリプトを Kiro に書かせた後、Kiro のターミナルで実行する
### WSL 端末(VS Code Remote / Kiro WSL / Windows Terminal)
```bash
# winauto ラッパー経由(インストール済みなら直接呼べる)
winauto apps
winauto tree --app notepad
# パスは WSL パス形式で指定可能(ラッパーが wslpath 変換を行う)
winauto screenshot --app notepad --output /tmp/screenshot.png
# スクリプトを WSL から実行(Windows Python が呼ばれる)
winauto run my_automation.py
# ヘルパースクリプトは Windows Python 経由で実行
cmd.exe /c python .github/skills/windows-app-automation/scripts/element_inspector.py --list
```
**WSL 固有の注意点:**
- ラッパーは Windows の `python.exe` を**直接 exec** する(`cmd.exe` を挟まない)。
`cmd.exe` 経由だと WSL の cwd に対して「CMD does not support UNC paths as current
directories」を吐き、出力を読むエージェントを惑わせるため
- **引数のパス変換はラッパーが行うが、変換対象は限定されている**。変換されるのは
`screenshot` / `codegen` / `record` の `--output` の値、`run` のスクリプト位置引数、
および `/` で始まる絶対パスだけ。セレクタ(`name:=OK`)や `type` に渡す入力テキストは
**変換しない**(同名のファイルが cwd にあるだけで壊れるのを避けるため)
- `winauto run` に渡す**スクリプトの中身**のパスは Windows パス(`C:/...`)で書く
——変換されるのはコマンドライン引数だけで、スクリプト本文は解釈しない
- `WINAUTO_NO_PATH_CONV=1` で変換を完全に無効化できる
- WSL ターミナルに出力は返ってくるが、GUI 操作の対象は Windows デスクトップ上のウィンドウ
### kiro-cli から呼び出す場合
kiro-cli は WSL 上で動作する AI エージェント CLI。`--trust-all-tools` を付けると winauto コマンドを
自律的に呼び出してスクリプトを生成・実行できる。詳細は `references/kiro-cli-usage.md` を参照。
**基本パターン:**
```bash
# タスク記述に「winauto CLI が使える」と明示するとエージェントが活用する
kiro-cli chat --no-interactive --trust-all-tools \
"Notepad を起動してテキストを入力してスクリーンショットを /tmp/sc.png に保存して。winauto CLI が使える。"
```
**自動実行フロー:**
```
kiro-cli --trust-all-tools
├─ winauto apps ← 起動中アプリを偵察
├─ winauto tree --app <name> ← UI 要素を偵察
├─ [Python スクリプト生成] ← pywinauto スクリプトをファイルに書く
├─ winauto run script.py ← 実行(Windows Python が動く)
└─ winauto screenshot ← 結果を画像で確認
```
**kiro-cli 固有の注意点:**
- `--trust-all-tools` がないと winauto などの外部コマンド実行が承認待ちになる
- GUI 操作は Windows デスクトップ側で発生するため stdout に状況は出ない。`winauto screenshot` で確認
- kiro-cli が生成したスクリプト内の Windows パスは `C:/...` 形式にする
---
## 並列実行とデスクトップ排他
**Windows デスクトップは 1 セッションに 1 つしかない共有排他資源**。フォーカスとマウスカーソルは
1 組しかないため、複数プロセスが同時に `set_focus()` / `click_input()` を撃つと互いの操作を
奪い合って壊れる。agent-flow を `--workers 3` のように並列で回すと即座にこれが起きる。
winauto は**入力・フォーカスを奪うコマンドをファイルロックで直列化**する(既定で有効)。
| | コマンド |
|---|---|
| ロックを取る | `launch` `click` `type` `select` `keys` `screenshot` `run` `inspect` `codegen` |
| ロックを取らない | `apps` `tree` `get-text` `wait` `doctor` `record` |
読み取り専用をロック対象から外してあるのは、長い `wait` がロックを占有して他の発行を
止めてしまわないようにするため。
WSL からの呼び出しもラッパー経由で Windows Python に収束するので、**ロック 1 本で
「WSL 発」と「Windows ネイティブ発」の双方が直列化される**。分散実行(`agent-flow --git`)では
PC ごとにデスクトップが別なので、PC 単位でロックが閉じるこの構造が意味論的にも正しい。
```bash
winauto --lock-timeout 600 click "name:=OK" --app myapp # ロック待ちの上限(既定 300 秒・0 で無限)
winauto --no-lock screenshot --app myapp # 排他を外す(並列 GUI 操作は壊れる)
WINAUTO_LOCK_TIMEOUT=600 winauto click ... # 環境変数でも指定可
```
ロック待ちの告知と待ちタイムアウトは **stderr** に出る(stdout はエージェントが読む結果なので汚さない)。
誰が握っているかは `winauto doctor` の `lock` 行で分かる。
**agent-flow から使うときの注意:**
- GUI ノードは Windows デスクトップのある PC でしか実行できない。分散時は GUI 対応 PC だけが
`participate` するバス/ブランチに分ける
- `agent_timeout`(既定 600 秒)は GUI タスクには短いことがある。ロック待ち時間も含まれる点に注意
- 画面ロック中・RDP 切断中・未ログオンのセッションでは GUI 操作もスクリーンショットも失敗する。
`winauto doctor` の `desktop` 行が warn を出す
---
## 利用可能な補助スクリプト
- `scripts/element_inspector.py` — UIツリーの探索・セレクタの特定(最初に必ず実行)
- `scripts/app_launcher.py` — アプリ起動・待機・コマンド実行・終了を一括管理
- `tools/winauto/winauto.py` — Playwright 風の統合 CLI(inspect/click/type/select/screenshot/codegen/record)
**最初に `--help` を実行して利用方法を確認する。必要になるまでスクリプト本体は読まない。**
## 進め方の判断フロー
```
依頼内容 → 対象アプリは何か?
├─ Win32 (MFC / VCL / Delphi / 古いC++) → backend=win32 を優先
├─ WPF / UWP / WinForms / Qt → backend=uia を使う
└─ 不明 → uia から試し、失敗なら win32
アプリは起動済みか?
├─ No → Application.start(app_path) で起動
└─ Yes → Application.connect(title_re / process) でアタッチ
要素が見つかるか? → 必ず先に element_inspector.py で探索する
├─ auto_id あり → child_window(auto_id="...") ← 最優先
├─ name + type → child_window(title="...", control_type="...")
└─ class あり → child_window(class_name="...") ← Win32 向け
```
## ワークフロー: 調査 → 生成 → 実行
### Step 1: 要素ツリーを探索する
```bash
# アプリ一覧を確認
python scripts/element_inspector.py --list
# UIツリーを探索(depth は 3〜5 が目安)
python scripts/element_inspector.py --app notepad --depth 4
# 特定の子要素から探索
python scripts/element_inspector.py --app notepad --selector "control:=Document"
# JSON で出力(大きなツリーの解析に便利)
python scripts/element_inspector.py --app notepad --json > tree.json
```
winauto CLI でも同等の操作が可能:
```bash
python tools/winauto/winauto.py apps
python tools/winauto/winauto.py tree --app notepad --depth 4
python tools/winauto/winauto.py inspect --app notepad # 対話REPL
```
### Step 2: スクリプトを生成する
```bash
# codegen でテンプレートを生成(アプリを起動してツリーを取得)
python tools/winauto/winauto.py codegen notepad.exe --output test_notepad.py
# 生成されたスクリプトを確認・編集して完成させる
```
または、手動で直接スクリプトを書く(Step 1 で取得したセレクタを使う)。
**人にやって見せてもらう(record)** — 手順が言葉で書き取りにくいときは、操作を記録する。
```bash
winauto record --app 勤怠管理 --output events.jsonl
# 人がアプリを操作する → Ctrl+C で止める
```
1 行 1 JSON(JSONL)で、押した要素・入力欄の確定値・選択・チェック・ウィンドウの
切り替わりが残る。**打鍵そのものは記録しない**——低レベルキーボードフックは
デスクトップ全体のキーロガーになり、対象アプリ以外へ打ったパスワードまで拾うため。
入力欄へ打った文字は `value` として、キーボードから開いたメニューは `invoke` として
残るので、実務上の穴は「UI に対応物の無いショートカット」だけである(必要なら
`{"event":"keys",...}` の行を手で足す)。
記録の 1 行は**そのまま打てる argv ではなく、何が起きたかの事実**である。再現するときは
`launch` → `winauto launch`、`window` → その画面を `winauto wait` で待つ、
`click` / `toggle` → `winauto click`、`value` → `winauto type`、
`select` → `winauto select <セレクタ> <項目名>` と読み替える。
agent-dashboard の「手順を組み立てる → 操作を記録する」に貼り付けると、工程列に変換される。
### Step 3: 実行する
```bash
# 単体実行
python my_automation.py
# app_launcher.py 経由(アプリ起動・終了を自動管理)
python scripts/app_launcher.py --app notepad.exe -- python my_automation.py
# winauto CLI でワンライナー実行
python tools/winauto/winauto.py click "name:=OK" --app notepad
```
---
起動・接続、要素の検索、操作、スクリーンショット、ダイアログ処理の定型コードは
`references/pywinauto-patterns.md` にまとめてある。
---
## コントロールタイプ別 Tips
### リストボックス / コンボボックス
```python
# ListBox: アイテム選択
lb = win.child_window(control_type="ListBox")
lb.select("Item Name") # 名前で選択
lb.get_item(0).click_input() # インデックスで選択
# ComboBox: ドロップダウン
cb = win.child_window(control_type="ComboBox")
cb.select("Option 1") # 項目を選択
items = cb.item_texts() # 全項目取得
# ListBox のアイテムを全取得
items = [item.window_text() for item in lb.children()]
```
### ツリービュー
```python
tv = win.child_window(control_type="Tree")
# ルートアイテム取得
roots = tv.children(control_type="TreeItem")
# 子アイテム展開
root = roots[0]
root.expand()
children = root.children(control_type="TreeItem")
# パスで選択
tv.get_item(r"\Root\Child\Grandchild").select()
```
### チェックボックス / ラジオボタン
```python
cb = win.child_window(title="Enable feature", control_type="CheckBox")
cb.check() # チェック ON
cb.uncheck() # チェック OFF
is_checked = cb.get_toggle_state() == 1
rb = win.child_window(title="Option A", control_type="RadioButton")
rb.select()
```
### タブコントロール
```python
tab = win.child_window(control_type="Tab")
tab.select("Settings") # タブ名で選択
tab.select(1) # インデックスで選択
```
---
## よくある落とし穴と対処法
| 症状 | 原因 | 対処 |
|------|------|------|
| `ElementNotFoundError` | セレクタが間違い / UIが未描画 | `element_inspector.py` で再確認; `wait()` を追加 |
| 操作が失敗・無応答 | 要素が disabled / focus なし | `wait("enabled")` → `set_focus()` → `click_input()` |
| テキスト入力が文字化け | IME / Unicode 問題 | `type_keys()` の代わりに `set_text()` を使う |
| `click()` が効かない | 座標クリックが必要 | `click_input()` を使う(より低レベル) |
| ダイアログが検出できない | タイトルの一致パターンが違う | `title_re=".*キーワード.*"` で部分一致 |
| `backend=uia` で要素が見えない | アプリが UIA 非対応 | `backend=win32` に切り替える |
| 高速実行で要素見つからない | UI描画遅延 | `win.wait("ready")` / `elem.wait("exists")` を使う |
| 管理者権限アプリを操作できない | UAC 分離 | スクリプト自体を管理者権限で実行 |
| **WSL**: `pywinauto` が import できない | Linux Python に入っている | Windows Python で実行する: `cmd.exe /c python script.py` |
| **WSL**: `winauto` コマンドが見つからない | インストール未完 / PATH 未設定 | `python tools/winauto/install.py` を再実行; `source ~/.bashrc` |
| **WSL**: `winauto apps` が空 | WSL から Windows デスクトップが見えない | `winauto doctor` で interop / ラッパー / デスクトップ到達性を切り分ける |
| 操作が数分止まったまま | 別プロセスが GUI ロックを保持している | `winauto doctor` の `lock` 行で保持者を確認。並列度を下げるか `--lock-timeout` を延ばす |
| `GUI ロックを … 秒以内に取得できませんでした` | 先行の GUI 操作が長い/異常終了 | `--lock-timeout 0`(無限待ち)か、保持者プロセスを終了させる |
| スクリーンショットが真っ黒/ウィンドウが 0 個 | 画面ロック中・RDP 切断中・未ログオン | セッションをアンロックした状態を保つ。`winauto doctor` の `desktop` 行で検知できる |
| **Copilot**: ターミナルで `python` が見つからない | PATH に Python が未登録 | VS Code の Python インタープリタ設定を確認; `where python` でパスを確認 |
| **Kiro**: エージェントが winauto を実行できない | Kiro がシェルコマンドをブロック | Kiro の設定で `trustTools: true` を確認; 手動でターミナル実行 |
---
## ベストプラクティス
- **必ず先に explorer で探索する**: `element_inspector.py --app <name>` でセレクタを確認してからスクリプトを書く
- **`auto_id` を最優先にする**: 開発者が設定した AutomationID は最も安定したセレクタ
- **`wait()` を省略しない**: `click_input()` の前に必ず `wait("enabled")` か `wait("exists")` を入れる
- **`click_input()` を使う**: `click()` より確実。マウスイベントを直接送信する
- **`set_text()` を使う**: `type_keys()` は IME 問題が起きやすい。値を確定させるだけなら `set_text()` が確実
- **`app_launcher.py` でライフサイクル管理**: アプリの起動・終了をスクリプト内に書かず、`app_launcher.py` に任せる
- **スクリーンショットで状態確認**: エラー時は `capture_as_image().save()` で状態を記録する
---
## 参照
- `references/selector-syntax.md` — セレクタ構文の詳細リファレンス
- `references/pywinauto-patterns.md` — 起動・検索・操作・ダイアログの基本パターン集
- `examples/notepad_automation.py` — Notepad 自動化の完全サンプル
- `examples/file_dialog_handling.py` — ファイルダイアログ操作パターン集
- `tools/winauto/winauto.py` — winauto CLI(`--help` で各コマンド確認)
- [pywinauto Documentation](https://pywinauto.readthedocs.io/)
- [Windows UI Automation](https://learn.microsoft.com/windows/win32/winauto/entry-uiauto-win32)
View on GitHub