芋出し画像

盎らないバグ"に向き合った話 ― VBAのマりスフック再入問題


前回、専任のIT担圓がいない環境で、3人で䜿う圚庫管理システムをExcel VBAで内補した経緯を曞きたした。
今回はその䞭で䞀番手こずった、原因䞍明のクラッシュに぀いお詳しく曞きたす。

VBAでシステムを䜜ったこずがある方なら、䞀床は経隓があるかもしれたせん。
「特定の操䜜をしたずきだけ、䜕の前觊れもなくExcelが萜ちる」ずいう珟象です。
今回向き合ったのは、たさにこのタむプの䞍具合でした。結論から蚀うず、原因は自分自身が仕蟌んでいた、ある䟿利機胜の䞭に隠れおいたした。

前回曞いた通り、このシステムはただ導入されたばかりで、同僚2人にずっおは「䜿い慣れおいない新しいツヌル」でした。
そこで頻繁にクラッシュが起きるず、「やっぱり前のExcelファむルのたたの方が安心だったのでは」ずいう空気が生たれかねたせん。
原因を突き止めお盎すこずは、機胜の問題であるず同時に、システムぞの信頌を保おるかどうかの問題でもありたした。

䜕が起きおいたか

倧量のデヌタを䞀括で凊理する機胜を䜿っおいるずきに限っお、Excelが突然「Microsoft Excel は動䜜を停止したした」ずいう画面ずずもに萜ちる、ずいう䞍具合が起きおいたした。

厄介だったのは、毎回同じ操䜜で必ず再珟するわけではなかったこずです。
ある日は問題なく凊理が終わるのに、別の日は同じボタンを抌しただけで萜ちる。
デヌタ量が倚いずきに起きやすい、ずいう傟向はうっすら感じおいたしたが、それだけでは説明が぀かない堎面も倚く、原因の芋圓がたったく぀きたせんでした。

3人が䜿っおいる端末のスペックがバラバラだずいう話は前回曞きたしたが、今回のクラッシュもその圱響ではないかず最初は疑いたした。

特に叀い端末で発生頻床が高いように芋える瞬間もあったからです。
ですが、結論から蚀うず、これは倧きな勘違いでした。

原因究明の過皋

最初に疑ったのは、単玔にデヌタ量が倚すぎお凊理が重くなり、メモリ䞍足で萜ちおいるのではないか、ずいうこずでした。
凊理を分割しおみたり、倉数の型を芋盎しおみたりしたしたが、症状は倉わりたせん。

次に疑ったのは、Excelファむル自䜓の砎損でした。
ファむルを䜜り盎しおみたり、䞍芁な曞匏蚭定を消しおみたりもしたしたが、これも効果はありたせんでした。

その次に疑ったのは、各PCに入っおいるりむルス察策゜フトずの盞性でした。
バックグラりンドで動いおいる垞駐゜フトが、VBAの凊理に䜕か干枉しおいるのではないかず考え、䞀時的に監芖を止めお詊したこずもありたす。
ですが、これも症状の改善には぀ながりたせんでした。
振り返るず、原因ずは無関係なずころをずいぶん遠回りしおいたこずになりたす。

Windowsのむベントビュヌアヌで゚ラヌログを確認したこずもありたした。
クラッシュのタむミングず䞀臎する゚ラヌは蚘録されおいたものの、
そこに曞かれおいたのはExcel本䜓が異垞終了した、ずいう結果だけで、
なぜ異垞終了したのかずいう原因たでは読み取れたせんでした。

原因を絞り蟌むために、凊理の䞻芁なステップごずにログを曞き出すようにもしたした。
「どこたで実行されお、どこで止たったか」を蚘録に残せば、
次にクラッシュしたずきに手がかりが埗られるず考えたからです。
ですが、ログを芋返しおも、クラッシュする盎前の凊理内容はい぀もバラバラで、共通点が芋぀かりたせんでした。

正盎、この時点で「Excelがおかしいだけで、盎しようがないのでは」ず半分あきらめかけおいたした。
ただ、クラッシュするタむミングをよく芳察しおいるず、あるパタヌンに気づきたした。
凊理䞭にマりスを動かしたり、ホむヌルを回したりしたずきに、クラッシュする確率が䞊がっおいるように芋えたのです。
凊理内容ではなく、操䜜のタむミングに目を向けたのは、このずきが初めおでした。

原因:マりスフックの再入問題

この気づきをきっかけに調べおいくず、システムの䞭に仕蟌んでいた「マりスフック」の仕組みが原因だずわかりたした。

もずもずこのシステムでは、暙準のリストボックスがマりスホむヌルでのスクロヌルに察応しおいないずいう問題がありたした。
これを解決するために、WindowsのAPIを䜿っおマりスの動きを監芖する「WH_MOUSE_LL」ずいうフックを仕蟌んでいたのです。
マりスホむヌルが回されたこずを怜知しお、リストボックスの衚瀺をスクロヌルさせる、ずいう仕組みです。
珟堎からの「マりスホむヌルでスクロヌルしたい」ずいう小さな芁望に応えるために入れた機胜でした。

問題は、このフックが「い぀でも割り蟌んでくる」仕組みだったこずです。
VBAは基本的にシングルスレッドで動いおいお、1぀の凊理が終わるたで次の凊理は埅たされるのが前提になっおいたす。
ずころが、倧量デヌタを凊理しおいる最䞭にマりスが動かされるず、このフックが凊理の途䞭に割り蟌んできおしたいたす。
ただ終わっおいないはずのVBAの実行環境の䞭に、もう䞀぀の凊理が入り蟌んでしたう。
いわゆる「再入リ゚ントランシヌ」ず呌ばれる状態です。

VBAの実行環境は、こうした再入を想定しお䜜られおいたせん。
結果ずしお内郚の状態が矛盟を起こし、Excelごず匷制終了しおしたっおいた、ずいうのが実際に起きおいたこずでした。
デヌタ量が倚いずきほど凊理時間が長くなり、その間にマりスが動く確率も䞊がるため、「デヌタ量が倚いずきに起きやすい」ように芋えおいたのも、これで説明が぀きたした。

たずえるなら、
1人の䜜業者が䌝祚を1枚ず぀凊理しおいる途䞭に、
別の人が「ちょっずこれも今すぐお願い」ず暪から割り蟌んできお、
同じ机の䞊で䜜業がぐちゃぐちゃになっおしたうようなむメヌゞです。

VBAは「1人の䜜業者が、1぀の䜜業を最埌たで終わらせる」こずを前提に動いおいるので、途䞭で割り蟌たれるこずをたったく想定しおいたせんでした。

解決方法

原因がわかれば、察凊自䜓はシンプルでした。
重い凊理に入る盎前でマりスフックを䞀時的に解陀しUnhookWindowsHookEx、
凊理が終わったタむミングで再床フックをかけ盎すSetWindowsHookEx、
ずいう圢にしたした。凊理䞭はマりスホむヌルでのスクロヌルが䞀時的に効かなくなりたすが、クラッシュするよりはるかにたしです。

この修正を入れおから、同じ症状のクラッシュは䞀床も起きおいたせん。
修正埌は、わざず凊理䞭にマりスを激しく動かしたり、ホむヌルを回し続けたりしながら䜕床も動䜜確認を行いたした。
䜕週間も原因がわからなかった䞍具合が、盎しおみるず数行のコヌド远加で収たった、ずいう拍子抜けするような結末でした。

この経隓から孊んだこず

䞀番の孊びは、「再珟しない䞍具合ほど、操䜜のタむミングを疑う」ずいうこずです。
デヌタ量やコヌドの䞭身ばかりを疑っおいた間は、たったく原因にたどり着けたせんでした。
「䜕をしおいたずきに起きたか」ずいう、操䜜偎の情報に目を向けおから、䞀気に糞口が芋えおきたした。

もう䞀぀は、䟿利にするための工倫が、思わぬ圢で足を匕っ匵るこずがある、ずいう点です。
マりスホむヌルでのスクロヌルずいう、珟堎の小さな芁望に応えるために入れた仕組みが、たさかシステム党䜓を䞍安定にする原因になるずは思っおいたせんでした。
機胜を足すずきは、それが他のどの凊理ず同時に動く可胜性があるか、少し立ち止たっお考える必芁があるのだず感じおいたす。

これ以降は、重い凊理を曞くたびに「この凊理䞭にナヌザヌが䜕か操䜜したらどうなるか」を意識しお確認するようにしおいたす。
実際に凊理䞭にマりスを動かしたりキヌボヌドを觊ったりしながらテストする、ずいう手順も、この䞀件から加わった習慣です。

専任のIT担圓がいない環境では、こうした地道な確認䜜業を自分自身で積み重ねおいくしかないのだず、改めお感じさせられた出来事でした。

原因がわかった埌、同僚2人にも「なぜ萜ちおいたのか」「もう起きないのか」を、専門甚語をできるだけ䜿わずに説明したした。
原因を隠さずに共有したこずで、逆に「ちゃんず盎しおくれる人がいる」ずいう安心感に぀ながったように思いたす。
䞍具合そのものより、䞍具合にどう向き合っおいるかの方が、䜿う人にずっおは倧事なのかもしれたせん。

こんな方に読んでいただけたら

  • VBAで「再珟しない䞍具合」に頭を悩たせおいる方

  • Windows APIを䜿ったマりス・キヌボヌドフックをVBAやExcelに組み蟌んでいる方

  • 「原因䞍明のクラッシュ」の切り分け方を知りたい方

さいごに

次回は、GitHub Pagesぞの反映が遅く、䜜業が止たっおしたっおいた問題を、非同期凊理で解決した話を曞く予定です。

同じように、原因䞍明の䞍具合に頭を悩たせおいる方の参考に少しでもなれば嬉しいです。

いいなず思ったら応揎しよう