0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Excel VBA × Claude Code で「マクロを書いたらそのまま使える」アドインを作った話

0
Posted at

はじめに ― 40年程前の Lotus 1-2-3 と、シェル本社の社員

シリーズ3本目です。前2本は「Claude Code で どう作るか」の話でした。今回は趣を変えて、「作ったマクロを どう使い続けるか」、つまりアドイン本体(秀.xlsm)側の構造の話を書きます。

その前に、40年程前の小さな話を1つさせてください。

私が表計算ソフトの Lotus 1-2-3 を使い始めたのは昭和シェル石油(日本の系列で、今は出光に吸収されてもう無い会社です)の広島支店に勤めていた頃でした。会社のパソコンは富士通製で、最初は「メニューからマクロを呼ぶ」機能は無かったように記憶しています。バージョンアップでその機能が使えるようになりました。数字キーを押して選ぶだけの素朴なメニュー でしたが、当時としては結構画期的だった。これを取り入れたとたん、仕事が一気に楽になりました。

マクロを書くだけじゃ足りなくて、書いたマクロをメニューから呼べる までセットになって初めて道具になる——その手応えを、当時のあの瞬間に掴んでいたんだと、今になって思います。

その後に東京の本社で勤務した頃、シェル本社——当時は世界2位の石油メジャー(今は1位)——から出向してきていた社員の方のアシスタントを少しやらせてもらった時期がありました。ガソリンスタンドの立地条件を Lotus 1-2-3 に詳細に打ち込んで分析する業務で、「1週間くらいでやってほしい」と頼まれた。

私はもちろん、その「メニューからマクロ実行」を使って作業しました。作業はパターン化できたので、パターンごとにマクロを書いてメニューから呼べるようにして、ささっと回していた。1週間と言われた業務が1日で終わったので、あとは何をすればいいかたずねました。

すると画面にメニューが出ているのを見た本社の方が、顔を真っ赤にして 怒ったんですね。「何を勝手なことをやっているんだ、指示したこと以外はするな」と。

私はたどたどしい英語で説明しました。この作業はだいたい数パターンに分かれる。パターンに合った処理をメニューから選んで実行している。バックアップは取ってある。これはあくまで作業用のファイルだと。

すると、それまで怒っていた彼は黙って、それから日本語で「よくわかりました、あなたとっても優秀ね」と一言だけ言ってくれました。

後でそれを見ていた課長から「本社のシェル社員に褒められたぞ」と冷やかされて、ちょっとした笑い話になりました。当時はシェル本社から来た社員=超エリート、という扱いだったので。

これが、私が「メニューからマクロを呼ぶ」というやり方にこだわってきた原体験です。便利だったから、というのもありますが、たぶんあのとき本社の社員に「優秀ね」と認められた、というのが結構効いている気がします。「このやり方で正しい」という確信は、便利さの実感だけじゃなく、あの一言に裏で支えられている。

40年程経って、AI が来て、ソフトが Lotus から Excel になって、マクロの本数が桁違いに増えても、やっていることの本質は当時と変わっていません。今回はその、現代版の話を書きます。


きっかけは、ちゃんと困っていたからです。40歳をすぎた頃、マクロを大量に作ったら、自分で書いたものを自分で探せなくなりました

そこで応急処置として全マクロのコードを Excel シートに貼り付けて「マクロデータベース」を作りました。それをフィルタで検索することによって、新たにコードを作るときに、参考にしたいコードがすぐに見つかるので便利にはなりました。しかし、見つかっても結局コピペするだけなので実行はできない。検索だけできる墓場でした。

この記事は、その状態から抜け出すために 秀.xlsm がやっていることの中身です。専門用語で書くと「リフレクションでメニューを動的生成している」になりますが、本質は 「Sub を1つ足すだけでメニューに出る」——それだけです。

長年お世話になっている Qiita に置く、私なりのお返しの3本目として書きます。

なぜ今これを書いているか — マクロの管理という、誰も話題にしないテーマ

前2本(マクロが直るフォームが作れる)は どう作るか の話でした。今回書きたいのは 「作ったマクロをどう管理するか」 です。

これ、たぶん あまり真剣に考えられていないテーマ だと思います。「マクロを書く方法」「業務を自動化する方法」の記事は山ほどありますが、書いたマクロをどう探して、どう呼ぶか はあまり扱われない。本数が少ないうちは Alt+F8 のデフォルトのメニューでも回るので、問題として表面化しないのです。

私の場合は Lotus 1-2-3 時代から、本数が増えるたびに自分なりの管理方法を試してきました。Excel に移ってからの遍歴をかいつまむと、こんな流れです:

  • マクロデータベース:全マクロのコードを Excel のシートに貼り付けて、キーワード検索できるようにする。早い段階から 日本語の名前を付けて、日本語で検索 していました。「日本語でマクロを管理する」という発想は、この時点ですでに私の中にあった
  • でもこれは VBE 本体とは別の管理場所 です。見つけてもそこからコピペして VBA に戻す手間が要る。実物と管理側が分離している のが、ずっと違和感としてあった
  • だんだん「VBE 本体に直接管理させたい」と思うようになって、最初に作ったのが PERSONAL.XLSB のマクロを一覧表示するフォーム でした。日本語名で検索もできる。これが今の 秀.xlsm のリフレクションエンジンの起源です

そして AI が来て、マクロを作る速度が一段上がりました。本数はもっと増える。管理の話は前より重要になっています。

ただし、ここで一つ強調しておきたいことがあります。「マクロ管理」には2つの意味があって、これを混ぜると話がぼやけます

  1. 開発者目線の管理:自分が書いたコードをどう探し、どう直すか
  2. 利用者目線の管理:作ったマクロを、コードなんて見たくない事務の現場の人 が呼び出せるか

「マクロ管理」というと普通は1の話で終わります。でも本当に大事なのは2の方です。利用者にとっての要件はシンプル——VBA を見せない・簡単に呼べる・日本語であること。ここを外すと、いくら立派なマクロを書いても現場では使われません。開発者で完結してしまう、自分しか使わないマクロになる。

(この3つのうち、特に「日本語であること」が決定的に重要 だと私は思っているのですが、その話は次回の記事で深掘りします。今回はリフレクションの仕組みの方に集中させてください。)

そして AI と前2本で作った道具立て(vba_manager.py / form_builder.py)によって、これからマクロは いくらでも作れる時代 になりました。だからこそ、作る前に「現場の人がどう呼ぶか」を先に考えておかないと、量だけ増えて誰も使わない という事故が起きます。

大事なのは、現場の人がいかに簡単に、わかりやすく呼べるか

秀.xlsm はその問いに対する私なりの答えです。両方の管理を1枚で解く 道具立てになっていて、もちろんもっと簡単な方法もあるのかもしれませんが、「簡単さ」の一点では、たぶんこれがほぼ答えだと思っています。技術的には素朴です。だからこそ書いて出す価値があると思いました。

TL;DR

  • 「マクロ管理」には2つある:開発者目線(コードをどう探す)と、利用者目線(現場の人がどう呼ぶ)。本当に大事なのは後者で、要件は VBA を見せない・簡単に呼べる・日本語であること
  • AI で マクロは作るだけならいくらでも作れる時代 になった。だからこそ 先に「現場の人がどう呼ぶか」を考えておかないと、量だけ増えて使われない
  • 解は「並べ替える」のではなく「並べ替えるのをやめる」こと。願いの方向と正解の方向が真逆だった話
  • 秀.xlsm は VBA が 自分自身のソースコードを実行時に読んで メニューを生成する。登録表もマニフェストも無い。Sub を1つ足すと、そのままメニューに並ぶ。並び順は「ソースに書いた順」——よく使うものを上に「書く」と上に出る、それだけ
  • 結果、応急処置で使っていた「マクロデータベース」は 使わなくなって引退 しました

GitHub: **https://github.com/shu1551/shu-vba-manager**(`秀.xlsm` はリポジトリ直下に同梱)

前回までのあらすじ

前2本では、Claude Code から VBA を直接触れるようにする道具立て(vba_manager.py / form_builder.py)を作った話を書きました。会話するだけでマクロが直り、フォームが作れる。これは個人的に革命でした。

ただし、革命にはもれなく 副作用 が付いてきます。作れる速度が上がるほど、本数は増える。気づくと開発者の自分ですら、書いたものを探せなくなる。そして、ここから先が今回の話の入口ですが——開発者が探せないなら、現場の利用者はもっと探せません。「作ったマクロを呼んで使ってもらう」までを設計しないと、増やしただけで終わる。

ここから先は、その手当てとして 秀.xlsm 本体がやっていることの話です。


ここから本題:マクロを増やすほど使いにくくなる、を解いた話

「はじめに」で触れたとおり、本数が増えるにつれて応急処置として「マクロデータベース」を作って凌いでいました。全マクロのコードを Excel シートに貼り付けて、日本語の名前を付けて検索できる——これで「見つける」ことは一応できた。が、根本的な不便がまだ2つ残っていました。

マクロデータベース①:全体一覧.png
マクロデータベース②:絞り込み検索(Subヘッダー絞り込み).png
↑ マクロデータベース。上:全マクロのコードを貼り付けた一覧。下:「Sub」で絞り込んで237本のマクロが見えている状態

痛み①:Alt+F8 は勝手に五十音順に並べる

VBA の標準マクロ実行ダイアログ(Alt+F8)は、登録されているマクロを 勝手に五十音順に並べて 表示します。書いた順でも、よく使う順でもなく、ただ五十音順。

これがなぜ困るかというと、位置に意味が乗らない のです。「あれ、確かに作ったマクロがあったはず…」と探しても、表示位置はソートで毎回変動する。「いつもの場所にいない」状態が常態化する。挙げ句、「あれ、ないぞ」と焦って、よく見たら五十音的に1段ずれていただけ、なんてことを毎回やる羽目になります。

普通の対策は マクロ名に番号を振る01_集計実行02_帳票出力…)。私もやりました。が、これが続きません。間に1本入れたいときに番号を振り直し、関連マクロが分散したら振り直し。メンテナンスの方が本業より重い。だんだん番号を振らなくなって、また五十音地獄に戻る。

痛み②:マクロを足すたびに「配線」が要る、しかも腐る

もうひとつの痛み。マクロを書くだけでは、利用者には使えません。呼び出すための配線 が要ります:

  • ボタン:シートに図形やフォームコントロールを置いて、マクロを登録
  • メニュー:Quick Access Toolbar や Ribbon にカスタム追加
  • ショートカットApplication.OnKeyVB_Invoke_Func 属性で割り当て

これがマクロを足すたびに発生する。1個ならいい。100個になると、配線そのものが管理対象 になります。「あのマクロ、どこに配線したっけ」「このボタンに紐付いてたマクロ、別のに付け替えたっけ」——配線が腐る

そして配線が腐ると、利用者は 使えなくなったマクロを「壊れた」と感じる。本当はコードは生きているのに、入口が消えているだけなのに、です。これは利用者の信頼を一気に削ります。「このマクロ、また壊れてるよ」と現場で言われた経験のある方は、私と同じ穴に落ちたことがある人だと思います。

気づき:「並べ替える」のではなく「並べ替えるのをやめる」

ここで一度、自分が何を望んでいたのか言語化してみました。

よく使うマクロを上に出したい

これが私の願いでした。素直すぎる願いで、誰でも思いつく。

普通、これを叶える解決方向は次の2つです:

  1. 手動で順序を決める:マクロ名に番号を振る(前述の通り、メンテ地獄)
  2. 使用頻度で自動ソート:呼び出し回数を記録して降順に並べる

2 は実装してみたこともあるのですが、これも続きませんでした。新しく書いたマクロが下に沈む(回数ゼロだから)。「最近触っていないけど大事なやつ」も下に沈む「頻度」は「重要度」の代理指標として正確ではない のです。

両方やる気が起きずにしばらく寝かせていたある日、ふと気付きました。

並べ替える、んじゃない。並べ替えるのをやめるんだ。

Alt+F8 は わざわざ五十音にソートしてくれている。だったら、自分のメニューでは ソートをしない と決めればいい。ソートしなければ、VBE のソース上に 書いた順 でそのまま出てくる。書いた順は私が決めた順なのだから、よく使うものを上に「書く」だけ でいい。

「使う頻度」を Excel に教えなくていい。「番号」も振らなくていい。「ソースに書いた順」が、そのまま「私の優先順位」になる

願いの方向(「並べ替えたい」)と正解の方向(「並べ替えるのをやめろ」)は 真逆 でした。これがこの記事で一番伝えたい話です

after:VBA に「自分自身のソースコード」を読ませる

「ソートを外して、ソースに書いた順で並べる」と決めたら、あとは実装するだけです。仕組みを言葉にするとこうなります:

VBA が自分自身のソースコードを実行時に読んで、そこに書いてある Sub の名前をリストに並べる

これがリフレクションです。Java や C# でいう Reflection と同じ発想を、VBA でやっているだけ。登録表もマニフェストもプラグイン API もありません。ソースコードそのものが「マクロの一覧」を兼ねています。

アクティブマクロフォーム(Ctrl+Shift+I メニュー).png
↑ 完成形:Ctrl+Shift+I で出てくるアクティブマクロフォーム。日本語のマクロ名が、ソースに書いた順でそのまま並んでいる

実コード(shu005 の「アドインのマクロ一覧」から、画面位置調整などの装飾を省いた抜粋):

' ThisWorkbook = アドイン本体(秀自身)の標準モジュールを名前順に集める
Dim comps() As String
Dim compCount As Long
compCount = 0
For i = 1 To ThisWorkbook.VBProject.VBComponents.count
    If ThisWorkbook.VBProject.VBComponents(i).Type = 1 Then
        compCount = compCount + 1
        ReDim Preserve comps(1 To compCount)
        comps(compCount) = ThisWorkbook.VBProject.VBComponents(i).name
    End If
Next i
' (comps をモジュール名順にソート、コード略)

' モジュール名順に、各モジュールのソースを「上から」読む
For i = 1 To compCount
  With ThisWorkbook.VBProject.VBComponents(comps(i)).CodeModule
    proc = ""
    For j = 1 To .CountOfLines
      If proc <> .ProcOfLine(j, 0) Then
        proc = .ProcOfLine(j, 0)
        fl = LCase(Trim(.lines(.ProcBodyLine(proc, 0), 1)))
        ' Function は除外
        If Not (fl Like "function *" Or fl Like "* function *") Then
          ' 引数なし(または Optional のみ)の Sub だけ拾う
          innerParam = ""
          If InStr(fl, "(") > 0 Then
            innerParam = Trim(Mid(fl, InStr(fl, "(") + 1))
            If InStr(innerParam, ")") > 0 Then _
              innerParam = Trim(Left(innerParam, InStr(innerParam, ")") - 1))
          End If
          If Len(innerParam) = 0 Or LCase(Left(innerParam, 8)) = "optional" Then
            マクロフォーム.ListBox1.AddItem proc
          End If
        End If
      End If
    Next j
  End With
Next i

ポイントは2つだけ:

  1. モジュールはモジュール名順、Sub はソースに書いた順:モジュール内では For j = 1 To .CountOfLines でソースを 上から下に読む ので、書いた順にそのまま AddItem される。気づきパートで言った「ソースに書いた順がそのまま優先順位」が、技術的にはここで実現されています
  2. 引数なし Sub だけ拾うFunction は除外、引数ありの Sub も除外。理由は単純で、「引数なしで呼べる=ボタン1つで実行できる」もの だけがメニューに出る価値がある から。VBA の内部処理用 Sub やヘルパー関数が、利用者のメニューに混ざりません

この Function と引数ありを弾くフィルタが、「メニューに出すべきもの/出すべきでないもの」の判定を自動でやってくれている のが地味に効きます。利用者目線で「呼んでも何も起きないものが並んでいる」のはストレスなので、ここを自動で排除できているのは大きい。

並び順をどう動かすか

「よく使うものを上に書けば上に出る」と言いましたが、実際にやるのは VBE 上でその Sub を物理的に上に動かす ことです。手作業でやってもいいですが、私は前回までで作った vba_manager.pyreorder-macro というコマンドを足してあって、Claude Code から「カレンダー表示shu003 の先頭に持ってきて」と話すだけでソースの並びが動きます。

並べ替えとは、ソースコードを編集すること です。「データ」を並べ替えるのではなく、「ソース」を並べ替える。これが「並べ替えるのをやめる」と整合する形での並び順制御です。

3つの宛先・同じエンジン

shu005 には、ほぼ同じリフレクションを実装した Sub が 3つ あります:

Sub 名 対象 ショートカット
パーソナルのマクロ一覧 PERSONAL.XLSB(パーソナルマクロブック) Ctrl+Shift+P
アドインのマクロ一覧 ThisWorkbook秀.xlsm 自身) Ctrl+Shift+O
アクティブのマクロ一覧 ActiveWorkbook(今開いているファイル) Ctrl+Shift+I

中のリフレクション処理(45行ほど)は 3つの Sub で一字一句同じ です。違うのは「対象ブック」と「貼り先のフォーム」だけ。AI なら共通関数に括り出したくなる場面ですが、意図的にそうしていません。理由は3つあります:

  1. 入口の明確さ:3つの宛先がある、という構造を コードの見た目のまま 保つ。共通化するとこの構造が見えなくなる
  2. 移植性:たとえば「アクティブのマクロ一覧」だけを別の Excel ファイルに移植したくなったとき、共通関数に依存していると「あれも要る、これも要る」になってややこしい。マクロが単体で完結している ことが、切り離して使える性質を担保する
  3. ブートストラップ耐性:これは特に効きます。秀.xlsm をダウンロードした人が アドイン化する前の初回起動 のとき、Ctrl+Shift+I(アクティブのマクロ一覧)はちゃんと動きます。今開いている 秀.xlsm 自身を ActiveWorkbook として読むから。そして表示された一覧の中に「アドインの更新登録」があるので、それを実行するとアドイン化が完了する。3つが独立しているから、どれか1つが動けば全部に進める

少し脱線しますが、AI と一緒に開発するようになって、この「コードの重複を許容する」判断は改めて意識的に守るようになりました。AI はよく出来た存在で、コードの重複を見つけると反射的に共通化したくなる。が、共通化は 依存 を生みます。依存はモジュールの境界を曖昧にして、「ここだけ別の場所に持っていく」を難しくする秀.xlsm の設計原則は「いつでも切り離せること」で、この原則と「共通化」は両立しにくい。だから AI に「ここ DRY にしましょうか?」と言われても、毎回断っています。

組み込みと自前の区別がない

秀.xlsm の公開版には、私が10年以上かけて作ってきた 約100本のマクロ(カレンダー、印刷、シート操作、ファイル一覧表示など)が最初から積まれています。shu001 から shu005 までの標準モジュールに分かれて入っていて、Ctrl+Shift+O でメニューを開くと、それらが全部リフレクションで並びます。

shu002 だけが意図的に にしてあって、ここが 利用者の作業場 です。shu002 に好きな Sub を1つ書いて Excel を再起動すると、メニューにそれが追加されます。プラグイン API も、登録の手続きも、設定ファイルも、何も要りません。ただ Sub を1つ書くだけ。

そして書き加えた Sub は、組み込みの約100本と 見分けなく メニューに並びます。「ここから先は自前」「ここまでが組み込み」みたいな仕切りはありません。101本目を足すやり方は、私が100本を作ったやり方と同一 です。これがタイトルにした 「マクロを書いたらそのまま使える」——もっと噛み砕けば 「Sub を1つ足すだけでメニューに出る」——この記事の中身です。

shu002 に Sub テスト用マクロ() を1本書いた VBE 画面.png
Ctrl+Shift+O のメニューにそれが追加されている画面.png

↑ 上:shu002 モジュールに Sub テスト用マクロ() を1本書いただけの VBE 画面。下:Ctrl+Shift+O を押すと、書いたばかりの「テスト用マクロ」がメニューの先頭に現れている。登録の手続きも、設定ファイルも、何も間に挟まっていません。書いた、それだけです

秀.xlsmExcel アドインとして配布する設計 になっています。なぜアドイン形式なのか、そのアドインを「いつでも切り離せる」ようにわざわざ作ったのはなぜか——その話は次回の記事で扱います。

before / after の対比

両者を並べると、面白い対比が浮かびます:

マクロデータベース(before) 秀.xlsm(after)
戦略 マクロのテキストを VBA の 写し出す マクロは VBA の中に置いたまま、自分のコードを読ませる
出力 検索できる テキスト 生きた メニュー
比喩 抜き出した 内省させた

同じ「マクロを管理する」問題を、正反対の戦略で解いていることになります。マクロデータベースは「データを外に出して検索する」発想。秀.xlsm は「データはそのまま、解釈する側を VBA 自身にする」発想。本来は同じ問題のはずなのに、解の向きが180度違う。これも私には興味深いポイントです。

「効いた」ことの証明

ベンチマークは取っていません。が、効いたかどうかの判定は意外と簡単で、マクロデータベースを使わなくなりました

10年以上、応急処置として使い続けてきたものが、秀.xlsm を作り上げたあと、ふと開かなくなった。古い道具が新しい道具に引退させられた——これが何より明確な証拠だと思っています。


まとめと次回予告

長くなったのでまとめます。

  • マクロを増やすほど 使いにくくなる のは、「書く」と「使う」のあいだに 探す・呼ぶ の溝があるから。この溝は、書く速度が上がるほど大きくなる
  • 普通の対策(番号を振る/頻度ソート)はメンテ地獄か精度不良で続かない
  • 解は「並べ替えるのをやめる」こと。ソートを外せば、ソースに書いた順でそのまま出る。よく使うものを上に「書く」と上に出る
  • 秀.xlsm は VBA に自分自身のソースを読ませる リフレクション でこれを実装している。Sub を1つ足すと、それがそのままメニューに並ぶ。プラグイン API も、登録の手続きも、設定ファイルも無い
  • AI で作る速度が上がった今こそ、先に「現場の人がどう呼ぶか」を決めておくこと が決定的に重要

そして次回(記事4)は、私が「マクロ管理の本丸」だと思っている 「日本語でマクロを管理する」 という話と、それと地続きで生まれた 「アドインをいつでも切り離せるように作る」 設計の話を書きます。

実は、Microsoft が Excel のアップデートで私の PERSONAL.XLSB2回壊した ことがあって、それが「日本語マクロ名」と無関係ではなかった、という origin story を中心に書く予定です。ドラマにする話ではなく、淡々と「だからこの作り方にしている」 の説明として。


GitHub:https://github.com/shu1551/shu-vba-manager

ここまで読んでくださってありがとうございます。「Sub を1つ足すだけでメニューに出る」を試してみたい方は、リポジトリから 秀.xlsm をダウンロードして、Excel で開いて Ctrl+Shift+I(アクティブのマクロ一覧)を押してみてください。表示されたメニューの一番下に「アドインの更新登録」があります。それを実行するとアドイン化が完了して、以降は Ctrl+Shift+O(アドインのマクロ一覧)から 秀.xlsm のマクロが呼べるようになります。

ぜひ shu002 に好きな日本語名の Sub を1つ書いて、メニューに並ぶのを見てみてください。書いた瞬間、自分のマクロが組み込みと見分けなく並ぶ感覚は、たぶん想像より地味で、想像より気持ちいいです。

リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?