カテゴリー名: [VBEの自動操作によるExcelマクロの組込み]
当シリーズではマクロの組込みを取り上げてきましたが、組み込んだものは削除・消去できないと困ることがあります。
そこで、標準モジュール、クラスモジュール、ユーザーフォームを削除し、
また、イベントプロシージャを消去する方法を見ます。
既存のワークブックのコンポーネントを削除するには VBComponents.Remove
を用います。
ただ、既存のワークブックにはどのようなコンポーネントがいくつあるか分かりません。
ここでは、それらを把握して、全コンポーネントを削除する方法を模索します。
また、削除の前に、念のためマクロに割り当てられているショートカットキーを解除した上でコンポーネントを削除します。
それから、シートモジュールとブックモジュールですが、
イベントプロシージャが組み込まれているこれらモジュールは、
Addメソッドで追加したものではなく元々あったものです。
なので、Removeメソッドで削除したりはしません。
ではどうするかというと、
コンポーネントそのものを削除するのでなく、その中身を消去します。
つまり、何も書かれていないまっさらな初期状態に戻します。
削除したり消去するためには、まずマクロが組み込まれたワークブックを用意する必要があります。
それをやるのが UnsetMacroPrepare.vbs です。zip圧縮ファイルに同梱してあります。
これを実行すると、二つのマクロ Macro1, Macro2 からなる標準モジュール、
シートモジュール(Sheet2)、ブックモジュール(ThisWorkbook)
これらにマクロが組み込まれたBook1.xlsが生成されます。
そして、以降で取り上げる UnsetVariousMacro.vbs を実行すると、
すべてのマクロが削除または消去されます。
UnsetVariousMacro.vbs には鍵となるノウハウがいくつか含まれているので、まずは、それらを個別に取り上げます。
プロシージャ名というのは Macro1, Macro2 といった名前です。一つのコンポーネントに複数含まれていることしばしばです。
プロシージャ名を得るには CodeModule.ProcOfLine(i,0)
を用います。
変数iには行番号を代入します。1以上の数値です。
コンポーネントのソースコードが10行あり、1〜5行がMacro1、6〜10行がMacro2だとしましょう。
ProcOfLine(1,0)
は “Macro1” を返します。
ProcOfLine(2,0)
も “Macro1” です。
ProcOfLine(6,0)
は “Macro2” を返します。
変数iに 1〜10 を代入して、各々の戻り値をチェックすると、”Macro1” が5回、”Macro2” が5回返ってくることになります。
煩雑ですが、プロシージャ名のリストを簡単に取得する方法はないようです。
重複して得られるプロシージャ名をどう記録するかは工夫次第ですが、UnsetVariousMacro.vbs では HashTable を使っています。
プロシージャ名をHashTableに登録し、既に取得済みの名前かどうかをチェックするやり方です。
プログラムの該当箇所だけ示すと次のとおり。
Set HashTbl = CreateObject("System.Collections.Hashtable")
Set CPobj = WBobj.VBProject.VBComponents("Module1")
Set CMod = CPobj.CodeModule
For i = 1 To CMod.CountOfLines
ProcName = CMod.ProcOfLine(i,0) ' プロシージャ名を取得
If ProcName <> "" And HashTbl.ContainsKey(ProcName) = False Then
HashTbl.Add ProcName, i
EXLapp.MacroOptions CPobj.Name & "." & ProcName,,,,True,""
End If
Next
ここでプロシージャ名の取得を取り上げたのは、マクロに割り当てられているショートカットキーを解除するためです。
一つのプロシージャにショートカットキーを割り当てるのは MacroOptions で行います。
EXLapp.MacroOptions "Macro1",,,,True,"j"
とすれば、Macro1に Control + j
のショートカットキーを割り当てます。
では、この割り当てを解除するにはどうするかというと、最後のパラメータ “j” を空文字列の “” にして指定しなおします。
つまり、EXLapp.MacroOptions "Macro1",,,,True,""
とします。
マイクロソフトのWeb解説を読んだ感じだと
EXLapp.MacroOptions "Macro1",,,,False
とすれば割り当てを解除できそうに思いますが、実際にやってみると、私のところでは駄目でした。
なので、前述したような方法をとることにしました。
コンポーネントを削除するに当たって、わざわざショートカットキーを解除しなくてもいいのかもしれませんが、UnsetVariousMacro.vbsでは念のため行っています。
コンポーネントの削除は Removeメソッドで行います。
標準モジュール(Type=1)、クラスモジュール(Type=2)、ユーザーフォーム(Type=3)をすべて削除するには下のようにします。
仮にコンポーネントが5個あったとすれば、5, 4, 3, 2, 1の順番でたどって削除します。
1番からたどるのでもいいかもしれませんが、1番を削除すると、それまで2番だったものが1番に変化してしまうので、念のため最後からたどります。
CPCount = WBobj.VBProject.VBComponents.Count
For i = CPCount To 1 Step -1
Set CPobj = WBobj.VBProject.VBComponents(i)
If CPobj.Type >= 1 And CPobj.Type <= 3 Then
WBobj.VBProject.VBComponents.Remove CPobj
End If
Next
イベントプロシージャが組み込まれているシートモジュール、ブックモジュールは、削除するわけにいかないので中身を消去します。
簡単に消去するためのClearメソッドがあればいいのですが、残念ながらそうはいきません。
コンポーネントのソースコードが全部で難行かを調べ、1行目から最終行までをDeleteするという方法を採ります。
プログラムの該当箇所だけ示すと次のとおり。
CPCount = WBobj.VBProject.VBComponents.Count
For i = CPCount To 1 Step -1
Set CPobj = WBobj.VBProject.VBComponents(i)
If CPobj.Type < 1 Or CPobj.Type > 3 Then
LastNum = CPobj.CodeModule.CountOfLines
If LastNum <> 0 Then
CPobj.CodeModule.DeleteLines 1, LastNum ' 全消去
End If
End If
Next
上の全消去の方法は、標準モジュールなどにも適用できます。
イベントプロシージャを消去する場合、それらが起動してしまわないように
EXLapp.EnableEvents = False
としてイベントプロシージャを無効化しておくのが無難です。
もちろん、イベントプロシージャの消去がおわったら EXLapp.EnableEvents = True
として元に戻します。
既存のマクロの削除と消去を行うプログラムは次のとおり。
1Option Explicit 2Dim FSO, BookPath, HashTbl, CPCount, ProcName, LastNum, i, j 3Dim EXLapp, WBobj, CPobj, CMod 4 5Set FSO = CreateObject("Scripting.FileSystemObject") 6BookPath = FSO.GetAbsolutePathName("Book1.xls") 7If (FSO.FileExists(BookPath) = False) Then 8 WScript.Echo "Book1.xlsがありません。" 9 WScript.Quit 10End If 11Set HashTbl = CreateObject("System.Collections.Hashtable") 12Set EXLapp = CreateObject("Excel.Application") ' Excelの起動 13EXLapp.Visible = True ' Excelを見える状態に 14EXLapp.EnableEvents = False ' イベントプロシージャを無効化 15Set WBobj = EXLapp.Workbooks.Open(BookPath) 16CPCount = WBobj.VBProject.VBComponents.Count 17For i = CPCount To 1 Step -1 18 Set CPobj = WBobj.VBProject.VBComponents(i) 19 If CPobj.Type >= 1 And CPobj.Type <= 3 Then 20 Set CMod = CPobj.CodeModule 21 For j = 1 To CMod.CountOfLines 22 ProcName = CMod.ProcOfLine(j,0) ' プロシージャ名を取得 23 If ProcName <> "" And HashTbl.ContainsKey(ProcName) = False Then 24 HashTbl.Add ProcName, j 25 EXLapp.MacroOptions CPobj.Name & "." & ProcName,,,,True,"" 26 End If 27 Next 28 HashTbl.Clear 29 WBobj.VBProject.VBComponents.Remove CPobj 30 Else ' イベントプロシージャ 31 LastNum = CPobj.CodeModule.CountOfLines 32 If LastNum <> 0 Then 33 CPobj.CodeModule.DeleteLines 1, LastNum 34 End If 35 End If 36Next 37WBobj.Save ' ワークブックの上書き保存 38EXLapp.EnableEvents = True ' イベントプロシージャを有効化 39EXLapp.quit
既存のマクロの削除と消去を行うプログラムは次のとおり。
1var fso, exlApp, wb, cp, cMod; 2var bookPath, cpCount, procName, lastNum, i, j; 3 4fso = WScript.CreateObject("Scripting.FileSystemObject"); 5bookPath = fso.GetAbsolutePathName("Book1.xls"); 6if (fso.FileExists(bookPath) == false) { 7 WScript.Echo("Book1.xlsがありません。"); 8 WScript.Quit(); 9} 10var hashTbl = {}; 11exlApp = WScript.CreateObject("Excel.Application"); // Excelの起動 12exlApp.Visible = true; // Excelを見える状態に 13exlApp.EnableEvents = false; // イベントプロシージャを無効化 14wb = exlApp.Workbooks.Open(bookPath); 15cpCount = wb.VBProject.VBComponents.Count; 16for (i = cpCount; i > 0; i--) { 17 cp = wb.VBProject.VBComponents(i); 18 if (cp.Type >= 1 && cp.Type <= 3) { 19 cMod = cp.CodeModule; 20 for (j = 1; j <= cMod.CountOfLines; j++) { 21 procName = cMod.ProcOfLine(j,0); // プロシージャ名を取得 22 if (procName != "" && hashTbl[procName] == null) { 23 hashTbl[procName] = j; 24 exlApp.MacroOptions(cp.Name + "." + procName, 25 null, null, null, true, ""); 26 } 27 } 28 hashTbl = {}; 29 wb.VBProject.VBComponents.Remove(cp); 30 } 31 else { // イベントプロシージャ 32 lastNum = cp.CodeModule.CountOfLines 33 if (lastNum != 0) { 34 cp.CodeModule.DeleteLines(1, lastNum); 35 } 36 } 37} 38wb.Save(); // ワークブックの上書き保存 39exlApp.EnableEvents = true; // イベントプロシージャを有効化 40exlApp.Quit(); // Excel終了
〜 以上 〜