C#を利用してExcelVBAを書き出す

とあるプロジェクトでExcelマクロの改修が数多く発生。改修前と改修後のコード比較を行うためにファイルとして書き出す必要があった。VBAは表示用パスワードで保護されていたため、これをいちいち手動で解除・出力していては時間が掛かりすぎてしまう。そのためファイルを選べばあとは自動で出力するためのツールをC#で作成した。
自動化に際してすんなりといかなかったポイントがあったので記しておく。

STEP
ExcelVBAのプロジェクトへアクセスできるように設定を行う

Excelのオプション⇒トラストセンター⇒マクロの設定で表示される「VBAプロジェクトのオブジェクトモデルへのアクセスを信頼する」にチェックを入れる。
これをしておかないと外部からVBAにアクセスすることができない。

STEP
Excelマクロには表示用にパスワード保護がかけれていた

対象のファイルにはExcelVBAの表示用パスワードが掛けられていた。Office関係のアプリケーションはActieXオブジェクトとして外部から操作が可能だが、保護解除のためのメソッドやプロパティが用意されていない。これが少し面倒。

結局のところメソッドやプロパティがないため、キー操作を模倣して解除することになる。
Excelへの参照設定を行い、アプリケーションが操作できる状態を前提に下記コードを記述する。

Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
excel.Visible = true;
excel.EnableEvents = false;

Microsoft.Office.Interop.Excel.Workbooks books = excel.Workbooks;
Microsoft.Office.Interop.Excel.Workbook book = books.Open("★対象ファイル");

book.Activate();
//ロックされている場合は解除
if (book.VBProject.Protection == vbext_ProjectProtection.vbext_pp_locked)
{
    excel.OnKey("%{F11}"); //Alt+F11
    Thread.Sleep(1000);

    excel.SendKeys("%{F11}%TE" + "★パスワード" + "~~%{F11}", true);
    Thread.Sleep(1000);
}

【注意点】キー入力を行うため、途中でフォーカスが外れるとうまく機能しなくなる。フォーカスを奪うようなアプリや他のExcelが起動していたりすると動きがおかしくなるので要注意だ。
Sleepを挟んでいるのはキー入力の結果を少し待機するためだが、このあたりは適当に調整してもらえればと思う。

STEP
VBAコンポーネントの出力

保護が解除されていれば、モジュールの出力はメソッドが用意されているので書き出すだけでOK。

Microsoft.Vbe.Interop.VBComponents modules = book.VBProject.VBComponents;
string pathName;
foreach (Microsoft.Vbe.Interop.VBComponent module in modules)
{
    pathName = Path.Combine("★出力先フォルダ", module.Name);
    switch (module.Type)
    {
        case Microsoft.Vbe.Interop.vbext_ComponentType.vbext_ct_ClassModule:
            pathName += ".cls";
            break;
        case Microsoft.Vbe.Interop.vbext_ComponentType.vbext_ct_MSForm:
            pathName += ".frm";
            break;
        default:
            pathName += ".bas";
            break;
    }

    module.Export(pathName);
}
STEP
Excelオブジェクトの後始末

Excelオブジェクトを使用した後は決まった手順でリリースしなければならない。
ぱっとみ冗長的に感じられるが、段階的に確実にリリースする必要がある。

excel.DisplayAlerts = false;
book.Close();
System.Runtime.InteropServices.Marshal.ReleaseComObject(book);
book = null;
GC.Collect();
GC.WaitForPendingFinalizers(); 

books.Close();
System.Runtime.InteropServices.Marshal.ReleaseComObject(books);
books = null;
GC.Collect();
GC.WaitForPendingFinalizers();

excel.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
excel = null;
GC.Collect();
GC.WaitForPendingFinalizers();

と、こんな感じにコーディングすればマクロの書き出しができます。

  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次