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への道を踏み出せるのではないか、と思っています。