結論
モジュール名とプロシージャ名を同一にするのは避けましょう。
(インデックスの分かり易さから)試みる方が多いと思うので、注意喚起いたします。
理由
VBAがプロシージャを探す際、同名のモジュールと名前衝突して、プロシージャを見つけられなくなることが特定の条件下で発生する為です。
特定の条件
「文字列」でプロシージャ名を指定する場合です。
' 1. 指定した時刻にマクロを実行予約する
Application.OnTime Now + TimeValue("00:00:05"), "MyTask"
' 2. マクロ名を文字列で指定して動的に実行する
Application.Run "MyTask"
' 3. 図形やボタンにクリック時の実行マクロを割り当てる
ActiveSheet.Shapes("Button 1").OnAction = "MyTask"
モジュール名 = プロシージャ名で上記コードを実行すると、「マクロ 'MyTask' を実行できません。このブックでマクロが使用できないか、またはすべてのマクロが無効になっている可能性があります」というエラーが出ます。(エラーメッセージもクリティカルなこと教えてくれない)
名前衝突が起こっています。
私は1番(Application.Ontime)で思いっきり症例に嵌り、トラストセンター等のセキュリティ設定を犯人認定して色々調べていました。(冤罪
原理
Appliction.OnTime "MyTask" (「MyTask」を実行せよ!)と命令が飛ぶと、VBAは以下の順で名前を探します。
-
モジュール名(外側の「箱」の名前)をチェック
-
プロシージャ名(中の「命令」の名前)をチェック
【名前が重複している場合】
[ MyTask ] (←モジュール名)
└── [ MyTask ] (←プロシージャ名)
VBAの動き:
「おっ、"MyTask" という『箱』を見つけたぞ!」
↓
「でもこれは『箱』であって『実行できる命令』じゃないな…」
↓
「実行できるマクロが見つかりません!」(終了)
外側の「箱(モジュール)」が、中の「中身(Sub)」を覆い隠してしまうのが原因です。
解決策
1. 別名にする (推奨)
モジュール名:modMyTask
プロシージャ名:MyTask
モジュール名に mod や M_ などの接頭辞をつけるの良いかもです。
2. 完全修飾で呼び出す
どうしても名前を同一にしたい場合は、シングルクォーテーションで囲って「どのモジュールのプロシージャか」を明示します。
Application.OnTime Now + TimeValue("00:00:01"), "'MyTask.MyTask'"
以上です。
所感
当記事のような、なぜか「すんなり進んでくれない点」はExcelやVBA多い気がします。(隠れてる仕様がそれなりにある)
エラーメッセージも事象そのままな内容の為、原因究明に時間かかることも輪をかけていたりします。
ただ、その分だけトラブルシューティングが面白かったりもします。