はじめに
前に、二十年探していた番号にたどり着いた話を書きました。記事に付いたコメントで手順を教わって、その日のうちに動かせた、という内容です。
その記事のコメント欄に、また続きが来ました。
今度は手順ではありません。ライブラリごとでした。
この記事は、そのライブラリを自分の道具に載せ替えて、足りない部分を足して、電卓でもう一度通すまでの一日の記録です。ただ、書きたいのはその作業そのものではありません。一日が終わったあとに私がふと言ったことと、それに対して返ってきた一言のほうです。タイトルは、その一言をそのまま持ってきました。
TL;DR
- コメント欄で、また教わりました。今度は手順ではなくライブラリごとです(uia_rap・MIT・参照設定なしの版つき)
- 自分の RPA マクロ3本を、そのライブラリに載せ替えました。866行が541行になり、後始末のコードが丸ごと消えました
- そのうえで「粘る/手がかりを段階で落とす/壊れたら似た候補を出す」を足して、電卓で通し直しました。5手とも成功、わざと壊した行はちゃんと候補を返してきました
- 途中、見る役を別に立てて検分させたら、本物のバグが1本出ました。載せ替えのときにだけ生まれる種類の穴で、しかも症状が「別の窓の同じ名前のボタンを実際に押す」でした
- 一日が終わってから数え直したら、コードの全文が人の手を通った回数がゼロでした。私は貼っていないし、探してもいません
- 前の記事で「二十年届かなかったのは道順が無かったからだ」と書きました。今回もう一つ見つかりました。確かめる一回が高すぎた、というほうです
また、コメント欄から来た
前の記事に、@fenblen_puyo さんからコメントが付きました。書き出しがこうです。
私も触発されまして、参照設定無しで全機能を使える(はず)のライブラリを公開してみました。
そして、そのコメントには編集済みの印が付いていました。後半が後から足されています。「使う人なんていないと思って真面目に公開していなかったものがある、これを機会に公開しようと思う」と書いたあとに、**「↑ 公開しました」**と続いていました。書いている途中で本当に公開作業をして、戻ってきて追記した、ということです。
出てきたのは2本です。
1本目は、UI Automation を参照設定なしで丸ごと叩けるようにしたものでした。
67インターフェース・1,907メソッドと書いてあります。前回私が電卓でやったのは、この中の4つの番号を Windows の開発キットのヘッダから数えて、定数として書く、というものでした。それを全面展開して、しかもヘッダをプログラムで解析して機械で生成してあります。私が「推測を入れずに機械で数える」と言ってやったことが、丸ごと自動化された形です。
2本目が本命でした。 uia_rap という、よく使う操作を短く書くためのラッパです。
素の UI Automation で二十行以上かかる「電卓の7を押す」が、こうなります。
e.getRoot.ffChildren(c.Name1_Sub("電卓")).ffDescendants(c.Type_(Button).Name4_Full("7")).ptInvoke
一行です。掴む・探す・押すが、そのまま並んでいます。
MIT ライセンスで GitHub に置いてあり、しかも参照設定がいる版といらない版が両方入っています。うちは参照設定を使わない決めでやっているので、そのまま入れられる形でした。ほかに、Edge の IE モードのタブから HTML の中身が直接取れる、という話も書かれています。そちらは今回は触っていません。
前回の記事で私は「あの一発は自分にとっては二十年ぶんの到達点だった」と書きました。ところが同じものが、この方の中ではとっくに工業化されていました。ただ、公開されていなかっただけです。記事を出したことで、棚の在庫が降りてきた形になります。
載せ替えた
その場で頼みました。「じゃあ早速、私のやつに適応させてくれないか」と。
取り込んだのは6モジュールです。標準が3つ、クラスが3つ。入れた直後に、日本語が壊れていないか・変な名前が混じっていないかを道具のほうで一通り検査して、全部通りました。文字コードの話は、この家で何度も焼かれているところです。
そのうえで、自分の RPA マクロ3本を書き換えました。
| マクロ | 前 | 後 |
|---|---|---|
| 窓と部品一覧表の作成 | 339行 | 222行 |
| 選んだ部品を操作 | 232行 | 129行 |
| 手順を順番に流す | 295行 | 190行 |
| 合計 | 866行 | 541行 |
減った分は、ほぼ全部が配管です。COM の識別子、機能が何番目にあるかという番号の一覧、呼び出しのための内部処理、そして最後に一つずつ解放していく行列。そのへんが丸ごと消えました。
書き味はこうなりました。窓を掴むのが e().getHandle(hw)、条件が c().AutomationId(自動ID)、探すのが 窓.ffDescendants(条件)、読むのが 相手.prValue。後始末はクラス側が自動でやるので、解放の処理は一行も書いていません。
動きと画面とメッセージは、一文字も変えていません。中身の配管だけを入れ替えて、外から見て同じものが出る状態にしています。
そのあと私が聞いたのは、速くなったのかどうかでした。答えは「ほぼ変わらない」です。下で動いているのは前と同じ仕組みで、時間を食っているのは相手の画面を走査する部分、つまり向こう側の仕事だからです。
変わるのは、次を作るときの速さでした。前は「スクロールさせたい」と思った時点で、ヘッダを開いて番号を掘り直すところからでした。いまは押す・入れる・読む・広げる・畳む・スクロールする、あたりが最初から名前で呼べる状態にあります。番号を数える作業は、もう一生やらなくてよくなりました。
メニューに、新顔が二つ出た
ここで、うちの都合の話が一つ起きました。
私のアドインは、引数のないマクロを自動でメニューに並べる作りになっています。ライブラリの中にその条件に当てはまるものが2本あったので、見慣れない名前がメニューに並びました。保守用のもので、普段は呼びません。
邪魔なので消してくれ、と言いました。ここから3回やり直しています。
- 頭の一語を
PublicからPrivateに変えれば消えるはずだ、と言いました。消えませんでした - 調べたら、うちのメニューを作っている側が
PublicとPrivateを区別していませんでした。そこでFunctionに変える手が出てきて、消えました。ただし私は前に「Function は作らない」と決めています。その決めごとを忘れていました - 撤回して、2本とも丸ごと削除。呼び元がどこにも無いことを先に確かめたうえで消しました
書いてしまえば三行ですが、この節が、この記事でいちばん大事なところかもしれません。
一つ目の手は外れています。二つ目は、私の決めごとを踏み越えています。それでも、この三往復にかかった時間は数分です。マクロを取り出して、直して、書き戻して、コンパイルを通して、また取り出す——そこに私の手は入っていません。私がやったのは「消えてませんけど」と言ったことと、「それは駄目だ」と言ったことだけです。
外れた手が二回あっても、道具が安ければ、それは事故になりません。高い道具の上で外れると、その日はもう終わります。
難しいのは、相手が待ってくれないこと
載せ替えが終わったあと、こういう話をしました。RPA が本当に難しいのはどこか、という話です。
答えは「相手の画面が思ったとおりに出てこないこと」でした。読み込みが遅れる。ボタンの名前が微妙に違う。前回と画面の形が変わっている。ここは道具では消えません。
消えないなら潰せないのか、と聞きました。返ってきたのは、こういう整理です。ゼロにはできないが、壊れ方は変えられる。「たまに黙って失敗する」を「待つ→駄目なら何が変わったかを自分で報告する→十秒で教え直せる」に変えられれば、実務では解決と同じだ、と。
三つに分けて、全部入れました。
① 粘る。 部品を探すところを、0.25秒おきに最大20回、ただし重い画面でも五秒で見切る、という形にしました。相手の画面が少し遅れて出てくる場合は、これでほぼ吸収されます。
② 手がかりを段階で落とす。 いままでは識別子と名前で一発勝負でした。これを、識別子 → 名前の完全一致 → 種類との合わせ技 → 部分一致、の順に落としていく形にしました。そしてどの手がかりで見つけたかを結果の列に書き添えます。「(部分一致で特定)」と出ていれば、本命では見つからなかったということです。ボタンの文言が少し変わった程度では死ななくなりました。
③ 壊れたら、自分で診断する。 見つからなかったときに、その窓の部品をもう一度取り直して、似た名前の候補を最大3つ、結果の列に書くようにしました。
三つ目が本丸です。自動化の道具でいちばん困るのは、止まったときに「何が変わったのか」が分からないことです。名前が変わったのか、画面が出ていなかったのか、そもそも窓が違うのか。それを人間が調べ直すのに一番時間がかかります。候補が出てくれば、表の行を選び直すだけで教え直しが終わります。
ここで、行数を数え直しておきます。載せ替えで541行まで落ちた3本は、この三つを足した結果、いま807行です。
つまり、こういうことになりました。
| 行数 | |
|---|---|
| 載せ替える前 | 866行 |
| 載せ替えた直後 | 541行 |
| 粘り・段階落とし・自己診断を足した後(いま) | 807行 |
消えたのは配管で、増えたのは段取りです。 差し引きでは六十行ほど減っただけですが、中身は入れ替わっています。番号と解放処理で埋まっていた三百行ぶんの場所が空いて、そこに「相手が思ったとおりに出てこなかったときにどうするか」が入りました。書く場所が空かなければ、たぶん足していません。
見る役を、別に立てた
ここで一つ、前の記事に書いた宿題を消化していました。
私は前回に、ループエンジニアリングという考え方について調べた記事を書きました。その中で「次にやること」を四つ挙げていて、二つ目が**「見る役を、別に立てる」**でした。自分の答案を自分で採点させても精度は上がらない、という調べ物からの宿題です。
今回、オーパスが書き上げたコードを、そのまま別の目のフェーブルに掛けました。経緯を知らない側に、制御の流れ・ライブラリの使い方・前の版との挙動の違い、という三つの見方で検分させています。
本物が1本出ました。
症状はこうです。二手目以降で相手の窓が閉じていた場合、変数に前の手の窓が残ったまま「掴めた」と判定して、別の窓の同じ名前のボタンを実際に押しに行く。
原因は一行です。ライブラリの窓を掴む関数は、失敗したときに 0 を返すのではなく、例外を投げる作りでした。うちのコードはその手前で「駄目でも止まらない」形にしていたので、失敗しても代入が起きません。すると変数には前の手の窓が残ります。旧版は毎回そこを 0 に戻していたので、無事だった部分でした。
つまりこれは、載せ替えのときにだけ生まれる種類の穴です。前の版にも新しい版にも無く、乗り換えの継ぎ目にだけできる。しかも壊れ方が最悪で、静かに失敗するのではなく、別の窓のボタンを押します。RPA でいちばんやってはいけない壊れ方です。
ほかに小粒が4件出ました。読んだ値が「=」で始まっていると数式と誤解釈されて全体が止まる、押す操作だけ失敗を成功として記録していた、窓番号のセルが壊れている行で全体が中断する、重い画面で見切りの時間が伸びる——全部塞いでから入れました。
前の記事では、これを「これからやること」として書いていました。今回、名前を意識せずに同じことをやっていて、しかも一回目で本物を掴んでいます。宿題の消化としては、悪くない結果でした。
正直に線を引くと、机の上で読んで見つけた話です。動かして見つけたのではありません。 実際に動くかどうかの判定は、このあとの実機に預けています。
電卓で、もう一度
最後は電卓です。前回と同じ 7+3 を通しました。
一覧表を作ると、電卓の部品は識別子まできれいに取れました。num7Button、plusButton、equalButton、CalculatorResults。ここに順番と操作を書いて流します。
| 順番 | 部品 | 結果 |
|---|---|---|
| 1 | 7 | 押した |
| 2 | プラス | 押した |
| 3 | 3 | 押した |
| 4 | 等号 | 押した |
| 5 | 表示 | 表示は 10 です |
画面のほうも合っています。
そのあと、③の自己診断をわざと壊して試しました。表の名前を「7777」、識別子を「num7Button_なくなった」に書き換えて実行します。
NG 部品が無い(似た候補: 7)
見つからないと言うだけでなく、窓を調べ直して「7 という部品ならいますよ」と教え返してきました。 狙いどおりです。ここで行を選び直せば、教え直しは終わりです。
それで、冒頭の話に戻ります
一日が終わって、私はこう言いました。
これだけ複雑な VBA で、コードもそこそこ長い。こういうものをチャットだけで、コピペでやろうとしたら結構大変だよな、と。
返ってきた答えは「大変どころか、ほぼ無理だと思います」でした。理由が三つ並びました。
一つ目。効いてくるのは書く時間ではなく、直す時間だという話です。
たとえば 339行のマクロで、一行の入れ忘れが見つかったとします。チャットだけなら、また 339行を頭から出してもらうのを待って、選んで、古いのを消して、貼り直す。一行のために毎回これをやることになります。今日の三往復——外れた手が二回あったあの場面が、そのまま一日分の作業になります。
二つ目。今のコードを読めないこと。 これがたぶん本当に大きいところです。
今日、載せ替えの途中で「ほかに同じライブラリを使っているところは無いか」を調べています。ブック全体を検索して、実物のヒットを見てから判断しました。チャットだけなら、これができません。私が貼ったものしか向こうには見えないので、貼り忘れた部分は、存在しないことになります。存在しないことになっているものと食い違うコードが、平気で出てきます。
規模を書いておきます。今日いじった3本が入っているモジュールは、それ一つで 3,144行。ブック全体では 446本のマクロがあります。この上で「今どうなっていますか」を毎回貼って渡すのは、現実的ではありません。
三つ目。細かい落とし穴が全部、人の手作業だということ。宣言は先頭、本体はその下、という貼る場所の決まり。日本語の変数名が経路のどこかで壊れること。全角の空白が紛れ込むこと。エラーの番号と行を人間が読んで、チャットに書き写すこと。ひとつずつは小さいのですが、全部「人間が運ぶ」作業です。
数え直しました。今日一日で、コードの全文が人の手を通った回数はゼロです。私は一度も貼っていないし、検索もしていないし、エラーの番号を書き写してもいません。
言い換えると、こういうことです。私は二十年、自分で荷物を運んでいました。
二十年、届かなかった理由
前の記事で、私は届かなかった理由を「道順が無かったからだ」と書きました。技術は最初からそこにあって、ただそこへ行く道を知らなかった。だから自動化のソフトが売っていたのは、たぶん道順だったのだろう、と。
その見立ては、いまも変えていません。ただ、今回もう一つ出てきました。
道順が分かっても、一回の確かめが高いと、確かめないんです。
思いつきというのは、たいてい大したものではありません。十回思いついて、九回は外れます。外れるのが普通なので、一回あたりが安ければ、そのまま十回撃てばいい。ところが一回に十五分かかるなら、撃つ前に「これは当たりそうか」を考え始めます。そして考えているうちに、たいてい面倒になって、やめます。
私が二十年やっていたのは、そういう時間だったと思います。思いついて、手間を見積もって、そこで置く。翌日には忘れている。届かなかったのは、たどり着けなかったからというより、途中で降りたからです。
前の記事に、私はこう書きました。「確かめるのが面倒でなくなると、確かめる回数が桁で変わります」と。同じことの裏側です。面倒なら、桁で減ります。
そして今日いちばん効いたのは、たぶん外れた手が二回あった、あの場面です。Private にして消えず、Function にして私の決めごとを踏み、そこから戻って削除した。三回やって、数分でした。運ぶ距離が長い方式なら、あそこで一日が終わっていたはずです。
前に、AI が「半日から一日はかかります」と言った作業が九分で終わった、という話を書きました。あのときの中身も、結局は同じところでした。人が手で運ぶ前提の相場が、そのまま残っていただけです。
AI に言われた一言は、これでした。
二十年届かなかった理由の一部は、たぶんそこにもあったんだと思います。
事実と見立ての仕分け
例によって仕分けます。
事実。前回の記事のコメントで @fenblen_puyo さんが2本のライブラリを公開したこと(本人の記事と GitHub は公開されています)。私のマクロ3本を uia_rap の参照設定なし版に載せ替えて、866行が541行になったこと。取り込んだのは6モジュールで、動き・画面・メッセージは変えていないこと。粘り(0.25秒×最大20回・五秒で見切り)と手がかりの段階落としと自己診断を足したこと。検分で本物のバグが1件と小粒が4件出て、全部塞いでから入れたこと。電卓で5手を流して成功5・失敗0、結果欄に「表示は 10 です」が返り、わざと壊した行が「NG 部品が無い(似た候補: 7)」を返したこと。確かめたのは Windows 11・64bit の Excel の一台のみです。
見立て。「二十年届かなかった理由の一部は、確かめる一回が高かったからだ」は、私の実感です。チャットだけで同じ作業をやったら何時間かかるかを、私は測っていません。 比べていない以上、これは比較ではなく心当たりです。「一回が安ければ十回撃つ」も、自分の癖の話であって、誰にでも当てはまる話ではありません。「載せ替えの継ぎ目にだけできる穴がある」も、今回そういう一本が出たというだけで、一般にそうだと確かめたわけではありません。
しかし、VBAマネージャーとAIという強力なツールを手に入れてから、確かめる1回がとても低くなっています。
正直な線引き
- 速さは変わっていません。載せ替えで速くなったのは「次に何かを作るとき」であって、いま動かしているものは前と同じ速さです
- 検分で見つけたバグは、机の上で読んで見つけたものです。動かして踏んだわけではありません。逆に、動かさないと出ない種類の穴は、まだ残っている可能性があります
- 電卓で通したのは、前回と同じ5手の一周だけです。粘りと段階落としが実際の遅い画面で効くかは、電卓では試せていません。相手が待ってくれない場面こそ本番ですが、そこはまだ通していません
- 自己診断が返すのは似た名前の候補です。名前も識別子も無い部品は、そもそも候補に出ません
- 載せ替えたマクロは、私のアドインの中にあります。前回の記事に置いたコードは、取り込むものが何も無い一枚として、あのまま残してあります(差し替えると「貼るだけで動く」が嘘になるためです)
- ライブラリそのものは @fenblen_puyo さんのもので、MIT です。私が作ったものではありません
- ここで書いた「一回が安い」は、私が自分で作った道具の上での話です。道具の作り方の記事ではありませんし、同じ形が誰にでも要るとも思っていません
おわりに
今回いちばん驚いたのは、ライブラリの中身ではありませんでした。自分が二十年、何に足を取られていたのかが、今ごろになって一つ増えたことです。
知識が足りなかった、という話なら分かりやすい。実際そうでしたし、それはコメント一本で埋まりました。ただ、埋まったあとに残っていたものがあって、それは「試すのが面倒だ」という、記録にも残らない種類の障害でした。面倒でやめたことは、やめた記録すら残りません。二十年ぶんの、何も残っていない時間です。
いま、道具のほうが荷物を運ぶようになって、その時間が見えるようになりました。見えたのは、埋まったからです。埋まる前は、それが障害だとすら思っていませんでした。そういうものだと思っていたからです。
もう一つ書いておきます。今日の出発点は、やはりコメント欄でした。私が書いて、読んだ人が触発されて、公開して、それが返ってきて、私の道具が薄くなった。前に「一番外側のループは、もう回っていた」と書きましたが、二周目はずいぶん早く来ました。
荷物は、置いていけます。運ぶのをやめた分だけ、手が空きます。
今日は、一度も運びませんでした。



