VBAのコードでVBAのモジュールを生成する

VBAのコードでVBAのモジュールを生成する

VBAのコードを実行することによって、プロジェクトに標準モジュールやらクラスモジュールやらを挿入することができます。

手順の紹介

次のような手順で行います。

なお、ホストアプリケーションはExcelとします。

  • トラスト センターの「開発者向けのマクロ設定」で、「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」にチェックを入れる
  • Workbookオブジェクトにぶら下がるVBProjectオブジェクトを取得する
  • VBProjectオブジェクトにぶら下がるVBComponentsコレクションのAdd()メソッドを叩いてVBComponentオブジェクトを取得する
  • VBComponentオブジェクトにぶら下がるCodeModuleオブジェクトを取得する
  • CodeModuleオブジェクトにコードを追加する

実装

開発者向けのマクロ設定

Excelの「ファイル」->「オプション」メニューから「トラスト センター」を開き、「開発者向けのマクロ設定」のセクションで「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」にチェックを入れましょう。

これで、VBAからVBEをごにょごにょできるようになります。

ここからはVBEでの作業です。

VBProjectオブジェクトの取得

リスト1-1
Private Sub InsertStandardModuleTest()
    Dim vp As Object
    Set vp = ThisWorkbook.VBProject

End Sub

まず、WorkbookオブジェクトのVBProjectプロパティを叩くことにより、VBProjectオブジェクトを取得し、変数vpに叩き込みます。

「Microsoft Visual Basic for Applications Extensibility X.X」を参照設定していれば、

このようにヒントが出ますが、今回は参照設定をせずに進めるので、Object型の変数を用います。

VBComponentオブジェクトの取得

リスト1-2
Private Const vbext_ct_StdModule As Long = 1

Private Sub InsertStandardModuleTest()
    Dim vp As Object
    Set vp = ThisWorkbook.VBProject

    Dim vc As Object
    Set vc = _
        vp.VBComponents.Add( _
            ComponentType:=vbext_ct_StdModule _
        )
    vc.Name = "Aho"

End Sub

さきほど取得したVBProjectオブジェクトのVBComponentsプロパティを叩いてVBComponentsコレクションオブジェクトを取得し、そのAdd()メソッドを叩いてVBComponentオブジェクトを取得します。

取得したVBComponentオブジェクトは即座に変数vcに叩き込みます。

今回は、VBComponentオブジェクトの種類として標準モジュールを指定したいので、Add()メソッドの引数ComponentTypeには1を渡します。

ただ、1では何のことかわからないので、「Microsoft Visual Basic for Applications Extensibility」にある列挙体vbext_ComponentType列挙体のメンバを調べて、モジュールの宣言セクションで

Private Const vbext_ct_StdModule As Long = 1

このように、定数として設定しています。

また、取得後のVBComponentオブジェクト、すなわち標準モジュールには、

vc.Name = "Aho"

このように、即座にAhoと名前を付けています。

CodeModuleオブジェクトの取得

リスト1-3
Private Const vbext_ct_StdModule As Long = 1

Private Sub InsertStandardModuleTest()
    Dim vp As Object
    Set vp = ThisWorkbook.VBProject
    
    Dim vc As Object
    Set vc = _
        vp.VBComponents.Add( _
            vbext_ct_StdModule _
        )
    vc.Name = "Aho"

    Dim cm As Object
    Set cm = vc.CodeModule

End Sub

VBComponentオブジェクトのCodeModuleプロパティを叩いてCodeModuleオブジェクトを取得し、即座に変数cmに叩き込んでいます。

先ほどのVBComponentオブジェクトが標準モジュールの〝外側〟だとしたら、このCodeModuleオブジェクトは標準モジュールの中身、すなわちコード編集機能に相当する、とでも考えたら良いでしょうか。

CodeModuleオブジェクトに闘魂注入!

あとは、CodeModuleオブジェクトにプロシージャのソースコードを闘魂注入しましょう。

リスト1-4
Private Const vbext_ct_StdModule As Long = 1

Private Sub InsertStandardModuleTest()
    Dim vp As Object
    Set vp = ThisWorkbook.VBProject
    
    Dim vc As Object
    Set vc = _
        vp.VBComponents.Add( _
            vbext_ct_StdModule _
        )

    vc.Name = "Aho"

    Dim cm As Object
    Set cm = vc.CodeModule
    
    Dim codeText As String
    codeText = _
        Join( _
            Array( _
                "Private Sub Aori()", _
                vbTab & "Debug.Print ""ち~ん(笑)""", _
                "End Sub" _
            ), _
            vbNewLine _
        )
    Call cm.InsertLines( _
        Line:=cm.CountOfLines + 1, _
        String:=codeText)
End Sub

まず、Join()関数とArray()関数を組み合わせて、プロシージャの文字列を組み立てます。

Private Sub Aori()
    Debug.Print "ち~ん(笑)"
End Sub

最終的にこのような文字列が吐き出されるよう工夫しています。

組み立てた文字列を変数codeTextに叩き込んだら、あとはCodeModuleオブジェクトのInsertLines()メソッドを用いて、文字列を闘魂注入します。

第1引数Lineには、cm.CountOfLinesを渡しています。

CodeModuleオブジェクトのCountOfLinesプロパティは、文字通り当該モジュールに何行あるかを返します。それに1を足すことで、すでにある記述の次の行に闘魂注入することになります。

第2引数Stringには、闘魂注入したい文字列を指定します。

動作確認

では、出来上がったInsertStandardModuleTest()プロシージャを実行してみましょう。

このとおり、Ahoという名前の標準モジュールが生えてきて、しかもちゃんと実行できます。

おわりに

これは、かなり面白いことができそうな予感がします。

個人的には、これを使ってTDDへの道を踏み出せるのではないか、と思っています。