Excelマクロ抽出スクリプト

# 概要
Excelからマクロの全モジュールを外部に吸い出すスクリプトです。

仕事の関係でExcel VBAマクロを触る必要があり、バージョン管理したいなと思ったけどExcel自体を管理下においてもあまり意味はなかった・・・。（ソースコードの差分とか取れないですからね・・・）
というわけでマクロだけ引っこ抜いてテキスト化してくれるスクリプトを組みました。WSHで動きます。

書いていて気になったが、Excel VBAとマクロの違いってなに？？ちゃんと区別すべきだろうか・・・。

# 利用準備
(Excel2003以前の場合)
ツール＞マクロ＞セキュリティ＞信頼できる発行元＞[Visual Basic プロジェクトへのアクセスを信頼する]をオン
(Excel2007以降の場合)
リボンの「開発」タブ＞マクロのセキュリティ＞[VBAプロジェクトオブジェクトモデルへのアクセスを信頼する]をオン

# 使い方
1. 下記/* --- 設定 --- */の抽出元ディレクトリに、抽出するファイルが置いてあるフォルダの絶対パスを指定する。ファイルではなくフォルダを必ず指定し、抽出対象以外のファイルは格納しておかないこと。エスケープシーケンスに気をつけること。
1. 当スクリプトを実行する。実行するとエクセル画面が開いたり閉じたりする。実行中は何も操作しないこと。
1. 終了後、デスクトップに抽出されたソースコードがある事を確認。抽出対象のフォルダ名で保存されている。

# 参考
sakura エディタでのコメント空白行を除いたGREP正規表現
`\s*[\S&&[^('|\r\n)]]`

# ソースコード

```js:ExtractExcelMacro.js

/* -------------------------------- 設定 -------------------------------- */

// 抽出元ディレクトリ(エスケープシーケンスに注意。例)「\」⇒「\\」）
var src_dir = "C:\\ExcelMacro";

// 抽出先ディレクトリ
var ext_dir = WScript.CreateObject("WScript.Shell").SpecialFolders("Desktop");

// Excelファイルのパスワードを，SendKeys用の記法で記述
var pass_string = "p@ssword";

/* -------------------------------- 処理 -------------------------------- */

var objFSO = WScript.CreateObject("Scripting.FileSystemObject");

access_dir(src_dir, ext_dir + "\\" + objFSO.GetFileName(src_dir));

// ディレクトリに再帰的にアクセス
function access_dir(src_path, vcm_path)
{
	if (!objFSO.FolderExists(vcm_path)) objFSO.CreateFolder(vcm_path);

	var folder = objFSO.GetFolder(src_path);

	var e1 = new Enumerator(folder.Files);
	for (e1.moveFirst(); !e1.atEnd(); e1.moveNext())
	{
		var file_path = src_path + "\\" + objFSO.GetFileName(e1.item().Name);
		var vacuum_dir = vcm_path + "\\" + objFSO.GetBaseName(e1.item().Name);

		if (objFSO.GetExtensionName(file_path) == "xls" || objFSO.GetExtensionName(file_path) == "xlsm")
		{
			extract_vba(file_path, vacuum_dir);
		}
	}

	var e2 = new Enumerator(folder.SubFolders);
	for (e2.moveFirst(); !e2.atEnd(); e2.moveNext())
	{
		var file_path = src_path + "\\" + objFSO.GetFileName(e2.item().Name);
		var vacuum_dir = vcm_path + "\\" + objFSO.GetFileName(e2.item().Name);

		access_dir(file_path, vacuum_dir);
	}
}

// VBAの抽出
function extract_vba(file_path, vacuum_dir_path)
{
	if (!objFSO.FolderExists(vacuum_dir_path)) objFSO.CreateFolder(vacuum_dir_path);

	// ブックを開く
	var excel = WScript.CreateObject("Excel.Application");
	excel.Visible = true;
	excel.EnableEvents = false;
	excel.Workbooks.Open( file_path );
	var book = excel.Workbooks( excel.Workbooks.Count );
	WScript.CreateObject("WScript.Shell").AppActivate( excel.Caption );

	/* ---------- パスワード解除 ---------- */

	var ws = WScript.CreateObject("WScript.Shell");

	// Visual Basic Editorを開き，プロジェクトエクスプローラにフォーカス
	book.VBProject.VBE.Windows(1).SetFocus();
	WScript.Sleep( 1000 );

	// 一番上のプロジェクトにフォーカス
	ws.SendKeys( "{TAB}" );
	WScript.Sleep( 200 );

	// パスワード入力ダイアログを表示する
	ws.SendKeys( "{ENTER}" );
	WScript.Sleep( 500 );

	// パスワードを入力
	ws.SendKeys( pass_string );
	WScript.Sleep( 200 );
	ws.SendKeys( "{ENTER}" );
	WScript.Sleep( 500 );

	/* ---------- マクロ抽出 ---------- */

	// ブック内のマクロの全モジュールをスキャンする
	var e = new Enumerator( book.VBProject.VBComponents );
	for( e.moveFirst() ; ! e.atEnd() ;  e.moveNext() )
	{
		// モジュールを取得
		var vba_module = e.item();

		// このモジュールのエクスポート先パスを決定
		var bas_path = vacuum_dir_path + "\\" + vba_module.Name;
		switch( vba_module.Type )
		{
			case 2:
				bas_path = bas_path + ".cls";
				break;
			case 3:
				bas_path = bas_path + ".frm";
				break;
			default:
				bas_path = bas_path + ".bas";
				break;
		}

		// このモジュールをエクスポート
		vba_module.Export( bas_path );
	}

	// Excelを閉じて終了
	excel.DisplayAlerts = false;
	excel.Quit();
	excel = null;
}
```