既存のマクロの削除と消去

カテゴリー名: [VBEの自動操作によるExcelマクロの組込み

2016/08/13

 当シリーズではマクロの組込みを取り上げてきましたが、組み込んだものは削除・消去できないと困ることがあります。

 そこで、標準モジュール、クラスモジュール、ユーザーフォームを削除し、
また、イベントプロシージャを消去する方法を見ます。


《このページの目次》


    

1. 概略

 既存のワークブックのコンポーネントを削除するには VBComponents.Remove を用います。

 ただ、既存のワークブックにはどのようなコンポーネントがいくつあるか分かりません。

 ここでは、それらを把握して、全コンポーネントを削除する方法を模索します。

 また、削除の前に、念のためマクロに割り当てられているショートカットキーを解除した上でコンポーネントを削除します。

    

 それから、シートモジュールとブックモジュールですが、
イベントプロシージャが組み込まれているこれらモジュールは、
Addメソッドで追加したものではなく元々あったものです。

 なので、Removeメソッドで削除したりはしません。

 ではどうするかというと、
コンポーネントそのものを削除するのでなく、その中身を消去します。

 つまり、何も書かれていないまっさらな初期状態に戻します。

    

 削除したり消去するためには、まずマクロが組み込まれたワークブックを用意する必要があります。

 それをやるのが UnsetMacroPrepare.vbs です。zip圧縮ファイルに同梱してあります。

 これを実行すると、二つのマクロ Macro1, Macro2 からなる標準モジュール、
シートモジュール(Sheet2)、ブックモジュール(ThisWorkbook)
これらにマクロが組み込まれたBook1.xlsが生成されます。

 そして、以降で取り上げる UnsetVariousMacro.vbs を実行すると、
すべてのマクロが削除または消去されます。

 UnsetVariousMacro.vbs には鍵となるノウハウがいくつか含まれているので、まずは、それらを個別に取り上げます。

    

目次に戻る


    

2. プロシージャ名の把握

 プロシージャ名というのは 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

 ここでプロシージャ名の取得を取り上げたのは、マクロに割り当てられているショートカットキーを解除するためです。

    

目次に戻る


    

3. ショートカットキーの解除

 一つのプロシージャにショートカットキーを割り当てるのは MacroOptions で行います。

 EXLapp.MacroOptions "Macro1",,,,True,"j" とすれば、Macro1に Control + j のショートカットキーを割り当てます。

 では、この割り当てを解除するにはどうするかというと、最後のパラメータ “j” を空文字列の “” にして指定しなおします。

 つまり、EXLapp.MacroOptions "Macro1",,,,True,"" とします。

 マイクロソフトのWeb解説を読んだ感じだと
EXLapp.MacroOptions "Macro1",,,,False とすれば割り当てを解除できそうに思いますが、実際にやってみると、私のところでは駄目でした。

 なので、前述したような方法をとることにしました。

 コンポーネントを削除するに当たって、わざわざショートカットキーを解除しなくてもいいのかもしれませんが、UnsetVariousMacro.vbsでは念のため行っています。

    

目次に戻る


    

4. コンポーネントの削除

 コンポーネントの削除は 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

    

目次に戻る


    

5. イベントプロシージャの消去

 イベントプロシージャが組み込まれているシートモジュール、ブックモジュールは、削除するわけにいかないので中身を消去します。

 簡単に消去するための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 として元に戻します。

    

目次に戻る


    

6. VBScript

 既存のマクロの削除と消去を行うプログラムは次のとおり。

△ UnsetVariousMacro.vbs

 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

    

目次に戻る


    

7. JScript

 既存のマクロの削除と消去を行うプログラムは次のとおり。

△ UnsetVariousMacro.js

 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終了

    

〜 以上 〜