はじめに
自作の VBA 管理ツール(vba_manager.py)の話の続きです。Excel で開いたままのブックに対して、マクロの取得・置換からシート編集までをコマンドで行う Python ツールで、コードは GitHub で公開しています(shu-vba-manager)。
前回の更新記事では、Stefan Broenner さんの Excel MCP を参考に「目」(シートを読む)と「手」(シートを編集する)の機能を追加した話を書きました。あの作業は Claude の新しいモデル、Claude Fable とやったものです。
ところがその後、Fable は使えなくなってしまいました。ご存じの方も多いと思いますが、公開からわずか3日の6月12日、米商務省の輸出管理命令で Fable 5 / Mythos 5 へのアクセスが停止されたためです。規制は6月30日に解除され、7月1日に利用が再開されました。
というわけで、Fable がようやく復活したので、止まっていたこのツールの改修をまとめてやりました。その記録です。
TL;DR
- 「徹底的に点検してくれ」の一言から始めたら、自分では気づいていなかったバグが重大5件を含めて十数件出てきた。全部直して回帰テストまで通した
- ついでに便利化。全マクロ横断のコード検索(grep)、バックアップから戻す undo 導線(restore)、コマンド列を接続1回で流す batch(Excel の起動込みで6コマンド約1秒)などを追加した
- フォームは作り方が変わった。行構造を8行書けば、整列済みのフォームが建つ。配置図のプレビューは Excel 不要、構築後はスクリーンショットと機械検査(lint)で検証、既存フォームは宣言コードへの逆変換もできる
- 仕上げに**ブックの健康診断(checkup)**を作った。構成・呼び出し関係・フォーム検査・死んだコードの検出を1枚のレポートに。10年育てた自作ブックにかけたら、消したボタンのイベントコード7件が見つかった
- 翌日にはこれを育てて、日本語名(
健康診断)・前回との差分(定期健診)・総合判定・修理メモのカルテまで付けた。実戦では、私自身が「直さないとな」と思ったまま放置していた「押すと落ちるメニューボタン」を機械が名指しして、一語で修理できた - 三日目は検査のほうを疑った。全フォームで鳴る警報や、修理に一度も繋がらない検査は撤去。意図的なデザインは「確認済み」として記録する仕組み(
--ack-all)を付けたら、所見103件が正味の0件・総合判定Aになった - vba_manager.py は 8,782 行・67 コマンド、ツール一式で 1.2 万行になった。全部、会話で作った
前回までのあらすじ
この連載は「会話するだけでマクロが直る」から始まりました。VBA のコードを AI に渡して直してもらうのではなく、開いたままのブックに Python から接続して、取得も置換も保存も済ませる。前回の更新でそこに「目と手」が付いて、シートの中身を読みながらマクロを直せるようになりました。
つまり道具は揃っていたのです。揃っていたのですが、道具そのものの点検はしていませんでした。
本題1: まず徹底的に点検させた
再会した Fable への最初の指示はこれだけです。
VBAマネージャーの点検をしてくれ徹底的に
Fable は観点別に4系統のレビュー(基盤・VBAコマンド・シート編集・重量級コマンドと文書の整合)を並列で走らせて、あわせてスクラッチのテスト用ブックで実際にコマンドを動かす実運転テストをやりました。数十分後に返ってきた報告には、重大5件・中9件・小物多数のバグが並んでいました。
毎日使っている道具です。それでこれだけ出ました。いくつか紹介します。
バグ1: End Sub 'コメント で隣のマクロが消える
一番危なかったのがこれです。プロシージャ置換コマンドには、ショートカットキー定義(Attribute 行)を保持するための特別な経路があるのですが、そこで使っていた End 検出の正規表現が行末までを要求していました。
End Sub ' 末尾コメント
この形にマッチしません。すると置換範囲のスキャンが次のプロシージャの End Sub まで伸びて、条件が揃うと隣のマクロを丸ごと巻き込んで消します。警告は出ません。
修正は正規表現に末尾コメントを許容させる1行ですが、怖いのはここからで、Fable は修正後に「Attribute 行あり × 末尾コメントあり × 後続 Sub あり」の再現条件をテスト用ブックに実際に作り、修正前なら消えていたはずの二番目の Sub が生き残ることを確認してから完了と報告してきました。直しましたと言われるより、消えなくなったのを見せられるほうが信用できます。
バグ2: オプションのタイプミスが「全消し」に化ける
引数解析が未知のオプションを黙って捨てる作りになっていました。何が起きるかというと、
py vba_manager.py clear-range "Sheet1!A1:C10" --contets # ←タイプミス
--contents(値のみクリア)のつもりのタイプミスが黙殺されて、既定の動作、つまり値も書式も全部消すほうに落ちます。タイプミス1文字で挙動が破壊側に化けるわけです。現在は未知のオプションを検出した時点でエラー停止になりました。
その他
別名保存が既存ファイルを無確認で上書きする、マクロ実行後に Excel の警告設定(DisplayAlerts)が切れたまま残る、「シート名だけ」を範囲指定に渡すと使用範囲全域が破壊対象になる——このあたりも全部塞ぎました。人間がうっかりやりそうなことを片っ端からエラーで止める方向です。
本題2: ついでに便利にした
点検が終わったところで「もっと便利にできないか、徹底的に」と聞いてみました。今度は使い勝手の観点でレビューが走って、提案が20件ほど返ってきました。全部やってもらいました。主なもの:
grep(全マクロ横断のコード検索)。「どのマクロが ActiveSheet を使っているか」が1コマンドで [モジュール] プロシージャ名:行番号 の形で返ります。今までは全モジュールをエクスポートして手元で探すしかありませんでした。
list-backups / restore(undo 導線)。このツールは置換のたびにバックアップを5世代自動で残すのですが、戻すコマンドがありませんでした。溜まる一方で、いざ戻すときは手作業。restore はバックアップファイルの中の VB_Name から対象モジュールを特定して、照合つきの安全な経路で書き戻します。
batch(コマンド列を接続1回で実行)。このツールは1コマンドごとに Excel へ COM 接続し直す作りで、そこが一番重い。batch はテキストファイルに書いたコマンド列を接続1回で流します。実測で、Excel の自動起動から後片付けまで込みで6コマンド約1秒でした。
read-range --tsv(読み書きの往復)。マクロの修正は「get でファイルに出す → 編集 → replace で戻す」の往復が確立していたのに、セルの値は画面表示だけで、直したければ手で写す必要がありました。読んだ範囲を TSV に書き出して、編集して、write-range で書き戻す。マクロと同じ型がセルにも通るようになりました。
本題3: フォームが「宣言」で建つようになった
一番変わったのはフォーム(UserForm)です。以前の記事で「会話するだけでフォームが作れる」と書きましたが、あのときの実態は、AI が座標を1個ずつ暗算して add_btn(f, "BtnOK", "OK", 80, 160, 60, 20) のように置いていく方式でした。動きはしますが、ラベルの縦がそろわない、ボタンの高さがばらつく、といった細かい崩れは人間が見て直していました。
今回、レイアウトエンジン(form_layout.py)を作って、フォームを「行の構造」で書けるようにしました。
build_form("F_Order", "受注入力", rows=[
heading("顧客情報"),
row(lbl("顧客名"), txt("txtName", required=True)),
row(lbl("区分"), combo("cmbKind", items=["法人", "個人"])),
frame("配送オプション",
row(lbl("優先度"), opt_group(("optHigh", "急ぎ"), ("optNorm", "通常"))),
row(chk("chkGift", "ギフト包装"))),
spacer(),
button_bar(ok("btnSave", "登録"), cancel("btnClose", "閉じる")),
], vba_stub=True, png=True)
これだけです。ラベル列の幅をそろえる、入力欄の右端をそろえる、ボタンを右下に同サイズで並べる、Enter と Esc を割り当てる、タブ順を視線順にする——そういう計算は全部 Python 側がやります。required=True を付けた項目はラベルに「*」が付いて、実行ボタンには「入力してください」の空チェックのコード雛形まで自動で入ります。
作ったあとの検証も機械化しました。フォームを実際に画面表示してスクリーンショットを撮るコマンド、重なり・はみ出し・不揃い・タブ順のずれを検査する lint、コントロールの枠と名前を画像に描き込むオーバーレイ。lint はコードとの整合も見ていて、コントロールを消したのにイベントプロシージャが残っている「孤児ハンドラ」も検出します。
逆方向もあります。既存のフォームを解析して、上の宣言コードに逆変換するコマンド(--to-layout)です。逆変換したコードをそのまま実行すると同じフォームが建つことを往復テストで確認してあるので、古いフォームの大改修は「宣言に起こす → 宣言を編集する → 建て直す」になりました。タブ付きフォーム(MultiPage)も一通り対応しています。
一つだけ、できなかったこともあります。セル範囲を選ばせる RefEdit コントロールは、COM からの挿入が Excel 側の信頼設定でブロックされることが実機テストで分かりました。これは TextBox+「選択」ボタン+Application.InputBox(Type:=8) の組み合わせで代替しています。挙動としてはむしろこちらのほうが安定しています。
本題4: ブックの健康診断ができるようになった
一日の仕上げに、ここまでの部品を束ねた checkup というコマンドを作りました。ブックを1コマンドで診察して、Markdown のレポートを吐きます。見るのは:構成(シート・マクロ・フォームの棚卸し)、存在しないマクロへの呼び出し(コピペ改修の置き土産でよくある「一語だけ違う Call」の検出)、フォームの機械検査(重なり・タブ順・消したコントロールのイベントコードが残っていないか)、バックアップの状況。
試しに、10年育ててきた自作の業務ブックにかけてみました。結果が面白かった。マクロ本体(101本・2,376行)は所見ゼロ。ところがフォーム側から、死んだイベントコードが7件出てきました。中身を見ると「検索ボタン」の残骸——昔は文字を入れてボタンを押して検索する作りで、その後「打った瞬間に絞り込む」方式に進化させたとき、ボタンだけ消してコードが残っていたのです。ページ送りボタンの残骸もありました。10年分の進化の痕跡が、診断書に浮かび上がった格好です。7件とも、現役のロジックが別の場所で生きていることを確認したうえで、その場で削除しました(もちろん自動バックアップつきで)。
診断は「読むだけ」なので安全です。閉じているブックを診る場合は、読み取り専用+マクロの自動実行を止めた状態で開くようにしてあります。会社に眠っている「誰が作ったか分からないマクロ付きブック」にも、起こさずに聴診器を当てられます。
翌日、健康診断を育てた
実はこの checkup、記事を仕上げている翌日にもう一段育ちました。まず名前です。日本語の別名で呼べるようにしました。
py vba_manager.py 健康診断
この連載でずっと「マクロ名は日本語でいい」と書いてきた筋からすれば、自分のツールのコマンドが英語のままなのは格好がつかないからです。
中身も健診らしくなりました。診断のたびに結果をブック別の履歴へ記録して、次回から「前回との比較」を自動で出します。新しく出た所見、解消した所見、マクロ・シート・フォームの増減。行番号のずれでは差分を出さない作りなので、コードが伸びただけでは騒ぎません。レポートの先頭には総合判定(A=所見なし/B=所見あり/C=壊れた参照・呼び出しあり)が付き、シート側も診るようになりました。数式のエラーセル、参照設定の破損(眠っていたブックが動かない原因の筆頭です)。VBA にパスワードが掛かったブックに当たった場合は、転ばずにシート側だけの縮退診断へ自動で切り替わります。
修理の側には 影響範囲(impact)というコマンドを足しました。「このマクロに手を入れると、どこまで波及するか」を修正前に一覧します。呼び元は間接呼び出しまで遡り、シート上のボタンやフォームのイベントからの経路も出ます。
それから「カルテ」。修理のあとに 健康診断 --note "何を直したか" と打つと、その一言が履歴に残ります。--history で過去の診断を一覧でき、各回の間にどの所見が消えたかまで表示されます。AI に直してもらった修理は、自分の手で直したときほど記憶に残らない——これは AI と仕事をしていての正直な実感です。だから、覚えている代わりに思い出せる仕組みを道具の側に持たせました。
育てた甲斐は、その日のうちにあった
別の自作ブック(マクロ 214 本・4,245 行)に健康診断をかけたら、総合判定 C。メニューのボタンが、存在しないマクロを呼んでいました。マクロを改名したときにボタン側の呼び出しだけ取り残された、いわゆる一語バグです。
白状すると、ここは私自身「直さないとな」と自覚したまま何年も放置していた箇所でした。頭の中にだけあった「いつか直す」リストを、機械は忘れも遠慮もせずに名指ししてきます。呼び先を一語直して再診断すると判定は B に戻り、比較欄には「解消 1」。診断 → 修理 → 再診断で治癒を確認する、という健康診断の型が一周した瞬間でした。
もう一つ正直に書いておくと、実戦のブックに次々かけるうちに、最初は診断側の誤検知も出ました。Win32 API の宣言(Declare)への呼び出し、実行時に呼び先が決まる動的な Application.Run、オブジェクトのメソッド呼び出し——どれも「存在しないマクロ」と誤報されましたし、シート上のボタンに登録されたマクロを「どこからも呼ばれていない」扱いする見落としもありました。原因を直すたびに、次の診断の比較欄が「解消 5」「解消 12」と自動で報告してくるので、検査器を鍛える作業そのものの答え合わせにもなりました。診断機は最初から賢かったわけではなく、実物のブックに揉まれて賢くなった、というのが実態です。
三日目、検査のほうを疑った
三日目はフォーム検査の番でした。アドイン本体の17枚と業務ブックの9枚、あわせて26枚のフォームを、診断レポートと実表示のスクリーンショットを突き合わせながら1枚ずつ総点検しました。本物の修理もそれなりに出ています。消したボタンのイベントコードの残骸が10件、実行ボタンがリストの縁に食い込んでいた重なり、1個だけ小さい打ち間違いのフォント、ばらばらのタブ移動順。どれも自動バックアップつきでその場で直しました。
ところが、直しても直しても所見の合計が減りません。アドイン側は103件。中身を見ると大半が「フォントサイズが揃っていない」「同じ行のボタン幅が3種類ある」の類で、どれもわざとやっているデザインでした。検索欄だけ大きい。矢印ボタンだけ小さい。意図的にそういうものばかりなのだから、そこを検査するほうがおかしい。
所見の側ではなく検査の側を疑って、1本ずつ実績を調べさせました。
- 「Enter の既定ボタンが未設定」——17枚中17枚で発火していました。全部で鳴る警報は警報ではありません。撤去
- 「コントロールの重なり」——境界がぴったり接しているだけの隣どうしを20件、重なり扱いしていました。許容誤差 2pt で解消
- 「フォント混在」——手作業で10年育てたフォームには 11.8pt や 14.1pt という端数が普通に住んでいます(拡縮やコピーの丸め誤差)。見た目で区別のつかない 1.5pt 未満の差は報告しないことにしました
- 「同じ行のボタン幅が3種類」「左端の縦ラインが多い」——過去いちどの修理にも繋がっていない検査でした。撤去
- シート側の「ゴースト行(ファイル肥大の候補)」——罫線を引いてある記入欄、つまり設計そのものを肥大扱いする誤爆をやりました。撤去
それでも最後に残るのが、本当に意図的なフォントの使い分けです。機械に意図は読めません。ここは検査の精度ではなく仕組みで解決しました。健康診断 --ack-all と打つと、いま出ている所見を「確認済み(意図的)」として登録し、以後は件数にも総合判定にも数えず、レポートの末尾に事実として残し続けます。判断を変えたくなったら --unack で戻せます。検査ロジックの変更で二度と出なくなった古い登録は、診断のたびに自動で掃除されます。
なお、この日の改修は Claude Sonnet と進め、仕上げに Fable へレビューを頼んだら、この新機構が確認済みリストを消しすぎる潜在バグを1件見つけました。作った目とは別の目で点検する——道具の改修にも健診が要るようです。
結果、アドイン本体(マクロ312本・4,845行・フォーム17枚)も業務ブックも所見0件・総合判定Aになりました。ゼロにしたのではなく、直すべきものを直し、意図的なものを意図的と記録した結果のゼロです。機械は事実を数える。意図は人間が判定する。その判定を道具が覚える——三日目にして、健診はやっとその分担に落ち着きました。
数字のまとめ
初日の改修から、三日目の検査の大掃除まで含めた結果です(実測)。
| 項目 | 内容 |
|---|---|
| 修正したバグ | 重大5・中9・小物多数(+作業中に踏んで直したもの数件) |
| 追加した主なコマンド | grep / code-replace / restore / batch / setup-check / docs / call-graph / checkup(健康診断)/ impact(影響範囲)ほか |
| vba_manager.py | 8,782 行・67 コマンド(+日本語別名 健康診断・影響範囲) |
| ツール一式(フォーム4ツール+テスト含む) | 12,000 行 |
| 自動テスト | pytest 39件(レイアウト計算・文字コードガード・診断の誤検知固定等) |
| batch の実測 | Excel 自動起動込み6コマンド約1秒 |
| 実ブック診断の成果 | 10年物のブックから死んだコード7件を検出・掃除/別ブックで「存在しないマクロを呼ぶメニューボタン」を発見→一語修理(診断の比較欄が「解消1」と自動報告)/三日目にフォーム26枚を総点検、死んだコード10件を掃除して2ブックとも所見0件・総合判定Aへ |
余談: Google が覚えてくれたらしい
記事を書く前に、ふと「しゅう VBAマネージャー」で検索してみたら、Google の AI による概要が出てきました。Qiita の記事と GitHub のリポジトリを根拠に、このツールを私の作品として説明してくれています。連載を続けてきた成果が検索エンジンの知識に組み込まれたようで、これはちょっと嬉しい出来事でした。
ただし、私はエンジニアではありません。ただの事務員です。
ただ、一つだけ引っかかったことがあります。検索まわりの説明では「導入には Python が必要で、その知識がないと難しい」という趣旨のことを言われがちなのです。これは違う、と作者として言っておきたい。
導入の壁は本当に高いのか
「会話するだけでマクロが直る」環境を作るのに、何が必要か。壁を正直に数えてみます。
壁1: Python のインストール。 公式サイトからインストーラをダウンロードして実行するだけです。Python の知識は一切要りません。コードを書くのも読むのも AI の仕事で、人間がやるのは日本語で話すことだけ。この連載のタイトルが「会話するだけで」なのはそういう意味です。「Python を使う」のではなく「Python が置いてある」だけ、と言ったほうが実態に近い。
壁2: pywin32 の追加。 コマンドを1行打つだけです(pip install pywin32)。それすら、導入した AI に「pywin32 を入れて」と言えばやってくれます。
壁3: GitHub からのダウンロード。 これは確かに、初心者にとっては一番「知らない世界」かもしれません。ただ実際にやることは、リポジトリのページで緑の「Code」ボタンを押して「Download ZIP」を選ぶ——それだけです。Git のコマンドは要りません。分からなければ YouTube で「GitHub 使い方」と検索すれば初心者向けの解説動画がいくらでも出てきますし、なんなら AI に「このページからファイルをダウンロードしたい」と聞けば手順を教えてくれます。
壁4: Excel の設定。 「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」にチェックを1個入れます(トラストセンターの中にあります)。ここが一番知られていないので README に書いてあります。
壁5: Claude Code の導入。 正直に言えば、一番「新しいことをする感」があるのはここです。ただ公式の手順が整備されているので、順番にやれば入ります。
数えてみると分かりますが、どの壁も必要なのは「知識」ではなく「初めての操作を一回やること」です。全部合わせても休日の午前中で終わる作業量だと思います。そして一番の安心材料は、詰まったら、これから導入しようとしているその AI に聞けばいいということです。導入の質問から、もう会話は始まっています。
これは想像で書いているのではありません。私自身がそうだったからです。Python の環境も GitHub の操作も、私は Claude に聞きながら、あるいはやってもらいながら越えてきました。壁の高さを自分の足で測ったことがないまま、気づいたら向こう側にいた、というのが正直なところです。
余談その2: 風向きの話
もう一つ、本筋からは外れますが、書き残しておきたいことがあります。
この2か月ほどで、日本の大手 SIer が相次いで Anthropic(Claude の開発元)との提携を発表しました。4月に NEC(日本企業初のグローバルパートナー、グループ約3万人に Claude を展開)、5月に日立(グループ約29万人)と富士通(約10万人)。しかも対象領域として、官公庁・金融・重要インフラといった「間違いが許されない」分野が名指しされています。さらに Microsoft 自身が、Microsoft 365 Copilot に Claude を統合し、Excel の中から使えるようにしました。
「Excel/VBA は古い、属人化する、だからクラウドへ」という話を、この10年ずっと聞かされてきました。属人化と言われる根拠は「作った本人にしかコードが読めない」ことだったはずです。ところが AI が VBA を読んで直せるようになった今、その根拠のほうが消えつつあります。この連載でやってきたことは、期せずしてその実演だったのだと思います。
大げさなことは言いたくないので、事実と見立てを分けておきます。提携の規模と時期は公表されている事実です。「AI は Excel を置き換えるのではなく、Excel を作り直す側に回った」——こちらは私の見立てです。答え合わせは数年後にしたいと思います。
おわりに
前回の更新記事で「一番変わったのは会話の中身」と書きました。今回はさらに一段変わって、会話が「これを直して」ではなく「点検してくれ」「もっと良くできないか」「全部やってくれ」になっています。何を直すかを私が指定していないのに、私の道具の穴が見つかって塞がっていく。道具の点検と改修を道具に任せる、という循環がようやく回り始めた気がします。
もっとも、直したのも AI なら、バグを仕込んだのも AI です。そこはお互い様ということで。
最後に一つだけ。私は事務員です。プログラマーではありません。その事務員の手元に、いま 1万行を超える Python 製のツール一式があります。本職の方が見ればどう評価するかは分かりませんが、少なくとも私一人では一生かかっても書けなかったものです。書いたのは AI で、私がやったのは「直したい」「もっと良くしたい」と日本語で言い続けることだけでした。
今まで「プログラミングは自分には関係ない」と壁の手前で止まっていた人こそ、一度試してみる時代になったと思います。壁は、AI が一緒に越えてくれます。
いま、自分がやっていることがそのことの証明なのかもしれません。
次回は、新たなクイック系ツールの話を書く予定です。



