Excel VBA 逆引き集 | COM Add-in

Excel VBA
スポンサーリンク

ねらい:COM アドインで「Excel常駐の拡張」を作り、現場で確実に動かす

COM アドインは、Excelのプロセスに常駐して機能を提供する拡張です。XLAM(VBAアドイン)が「ブックに近い」存在なのに対し、COM アドインは「Excelアプリに近い」ため、起動時ロード、リボンUI、外部連携、セキュアな配布に強みがあります。初心者でも実装の全体像が掴めるように、最小サンプルからリボン、VBA連携、ロギング、配布までをかみ砕いて解説します。

重要ポイントの深掘り

COM アドインはVBA単体では作れず、.NET(VSTO/IDTExtensibility2)、VB6、C++などの「COM対応言語」で実装します。設計の要は「Excelオブジェクトへの安全な参照」「起動と終了の枠」「例外を飲み込まずログへ流す」「UIからの操作を公開メソッドへ集約」です。ここが固まれば、後は機能を載せるだけで安定運用できます。


概要と選び方:COM アドインと XLAM の違い

使い分けの考え方

XLAMは配布が簡単で、VBAで完結します。COM アドインは、リボン統合、ヘッドレス連携、外部ライブラリ活用、コード署名や企業配布(MSI)などの「製品的な運用」に向きます。現場の要件がUI一体・広範囲配布・他システム連携ならCOM アドイン、スピード重視の社内配布ならXLAMが適します。

実装パスの選択肢

  • VB.NET/C# + IDTExtensibility2(軽量で理解しやすい最小構成)
  • VSTO Excel Add-in(Visual Studioテンプレ+Ribbon設計が楽)
  • VB6/C++(レガシー互換。新規は.NET推奨)

最小 COM アドインの実装例(VB.NET、IDTExtensibility2)

起動と終了にフックし、Excelオブジェクトを握る

' MyExcelAddin.vbproj(VB.NET)
Imports System.Runtime.InteropServices
Imports Extensibility
Imports Microsoft.Office.Interop.Excel

<ComVisible(True), Guid("YOUR-GUID-HERE"), ProgId("MyCompany.MyExcelAddin")>
Public Class Connect
    Implements IDTExtensibility2

    Private app As Application

    Public Sub OnConnection(Application As Object, ConnectMode As ext_ConnectMode, AddInInst As Object, ByRef custom As Array) Implements IDTExtensibility2.OnConnection
        app = CType(Application, Application)
        Log("OnConnection", "Excel version=" & app.Version)
        ' ここでボタン登録やリボンロードを行う(後述)
    End Sub

    Public Sub OnDisconnection(RemoveMode As ext_DisconnectMode, ByRef custom As Array) Implements IDTExtensibility2.OnDisconnection
        Log("OnDisconnection", "Goodbye")
        app = Nothing
    End Sub

    Public Sub OnAddInsUpdate(ByRef custom As Array) Implements IDTExtensibility2.OnAddInsUpdate
    End Sub

    Public Sub OnStartupComplete(ByRef custom As Array) Implements IDTExtensibility2.OnStartupComplete
        Log("OnStartupComplete", "Ready")
    End Sub

    Public Sub OnBeginShutdown(ByRef custom As Array) Implements IDTExtensibility2.OnBeginShutdown
    End Sub

    <ComVisible(True)>
    Public Sub RunNormalizeSelection()
        Try
            Dim rng As Range = app.Selection
            If rng Is Nothing Then Throw New Exception("選択がありません")
            Dim arr As Object(,) = rng.Value2
            For r = arr.GetLowerBound(0) To arr.GetUpperBound(0)
                For c = arr.GetLowerBound(1) To arr.GetUpperBound(1)
                    Dim s = If(arr(r, c), "")
                    arr(r, c) = s.ToString().Trim()
                Next
            Next
            rng.Value2 = arr
            Log("Normalize", "OK")
        Catch ex As Exception
            Log("NormalizeError", ex.Message)
        End Try
    End Sub

    Private Sub Log(action As String, detail As String)
        Try
            Dim path = IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyExcelAddin.log")
            IO.File.AppendAllText(path, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{action}] {detail}{Environment.NewLine}")
        Catch
        End Try
    End Sub
End Class
VB

レジストレーションと起動

  • ビルド後、管理者権限で regasm MyExcelAddin.dll /codebase を実行し、COM登録します。
  • Excelの「アドイン」から「COM アドイン」を開き、一覧に表示されたらチェックを入れてロードします。

重要ポイントの深掘り

OnConnectionでExcelのApplicationを握り、OnDisconnectionで後片付けします。公開メソッド(RunNormalizeSelection)はリボンやメニューから呼ぶための「入口」です。Excelオブジェクトは長く保持しすぎず、例外はログへ流してUIで止めないのが基本です。


リボンボタンを追加し、公開メソッドを呼ぶ(VSTO/RibbonX)

VSTOのRibbon設計(C#の例)

// Ribbon1.cs(VSTO)
public partial class Ribbon1
{
    private void Ribbon1_Load(object sender, RibbonUIEventArgs e) { }

    private void btnNormalize_Click(object sender, RibbonControlEventArgs e)
    {
        try
        {
            var app = Globals.ThisAddIn.Application;
            var rng = app.Selection as Microsoft.Office.Interop.Excel.Range;
            if (rng == null) throw new Exception("選択がありません");

            object[,] arr = rng.Value2;
            for (int r = arr.GetLowerBound(0); r <= arr.GetUpperBound(0); r++)
            for (int c = arr.GetLowerBound(1); c <= arr.GetUpperBound(1); c++)
            {
                string s = Convert.ToString(arr[r, c] ?? "");
                arr[r, c] = s.Trim();
            }
            rng.Value2 = arr;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Trace.WriteLine(ex.Message);
        }
    }
}
C#

Ribbon XMLでボタンを結びつける

<!-- customUI14.xml -->
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
  <ribbon>
    <tabs>
      <tab id="tabTools" label="業務ツール">
        <group id="grpNormalize" label="整形">
          <button id="btnNormalize" label="Normalize" onAction="RunNormalizeSelection"/>
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

重要ポイントの深掘り

リボンは「誰でも押せる入口」。ボタンは公開メソッドに直結し、実処理は配列I/Oで高速・安全に行います。VSTOならテンプレが整っており、学習コストが低いです。IDTExtensibility2でも、コマンドバーやRibbonXを組み合わせれば同様に実現できます。


VBAとの連携:既存マクロを呼ぶ、逆にCOM機能を呼ばせる

COM から VBA を呼ぶ

' COM側:既存VBAのPublic Subを呼ぶ
Public Sub RunVbaMacro(bookName As String, macroName As String)
    Try
        Dim fullName = $"{bookName}!{macroName}"
        app.Run(fullName) ' 例:Book1.xlsm!Run_DailyExport
        Log("RunVBA", fullName)
    Catch ex As Exception
        Log("RunVBAError", ex.Message)
    End Try
End Sub
VB

VBA から COM を呼ぶ

' VBA側:COMアドインの公開メソッドを呼ぶ(CallByNameやCreateObject)
Sub CallComAddin()
    Dim obj As Object
    Set obj = CreateObject("MyCompany.MyExcelAddin")
    Call CallByName(obj, "RunNormalizeSelection", VbMethod)
End Sub
VB

重要ポイントの深掘り

既存VBA資産を活かす場合、COMは「司令塔」としてVBAを呼び、逆にVBAからCOMのAPIを叩けます。契約(メソッド名、引数)を固定し、例外はUIに出さずログへ。相互参照の循環は避け、呼ぶ方向を整理すると保守性が上がります。


ログ、例外、設定:製品的な運用の最低限

ログの方針

ファイル(%LocalAppData%配下)へ時刻・アクション・詳細を追記します。業務上の重要イベント(開始、終了、エラー)を残すだけでも、現場の復旧が速くなります。

例外の扱い

UIを止めるダイアログは避け、例外はキャッチしてログへ。致命的なケースは「処理を中断→後片付け→状態復帰」を優先します。Excelオブジェクトの参照解放は、終了イベントで確実に。

設定管理

JSON/TXT/レジストリ、あるいは特定ブックのConfigシートなど、読み取り方法を一箇所に寄せます。しきい値や出力場所の差異は設定で吸収し、コード配布なしの運用変更を可能にします。


配布と登録:regasm、MSI、署名、信頼性

開発から配布までの最短手順

  • ビルド(Release)
  • 管理者権限で regasm YourAddin.dll /codebase で登録
  • Excelの「COM アドイン」に表示されることを確認
  • 組織配布なら、MSIでインストール・アンインストール/アップデートを提供
  • 署名(Strong name、コード署名)で信頼性を担保

アップデートのポイント

公開APIの互換性(メソッド名・引数)を守り、内部だけ更新。アンレジストは regasm YourAddin.dll /unregister。バージョン番号をログや設定へ出しておくと、現場確認が容易になります。

重要ポイントの深掘り

COM登録は「環境依存」の山です。権限、Bitness(x86/x64)、依存ランタイムを揃え、失敗時のロールバック(解除→再登録)手順を準備します。VSTOならClickOnce/MSIが用意されており、配布はさらに楽になります。


例題シナリオ:選択整形、日次CSV出力、VBA連携を一体化

流れの確認

  • Excel起動→COM アドインロード→リボンの「Normalize」ボタンを押下→選択範囲が整形される。
  • リボンの「Export」ボタン(公開メソッド)で当日分をCSV出力、ログへ記録。
  • 既存VBAの集計をCOMから呼ぶ(Application.Run)ことで、資産を活かしつつUI一体に。

確認ポイント

選択なしの例外がログへ出るか、進捗や終了がログへ出るか、COM解除後にExcelが安定しているか。メソッド名や引数を変えずに内部修正してもUIから同じ操作で動くか。


落とし穴と対策(深掘り)

Bitnessと参照エラー

x86/x64のExcelとAdd-inのビット数不一致でロード失敗します。ターゲットに合わせたビルド、Interop参照の整合、AnyCPUの慎重な運用が必要です。

オブジェクトリーク

Excel COM参照はリークしやすいです。短命スコープ、Nothing/解放、Try-Finallyで後片付けを徹底。巨大Rangeは配列I/Oで短時間に処理して戻します。

多重起動・競合

複数アドインが同名ボタンや同じショートカットを使うと競合します。IDやキャプションをユニークにし、登録前の存在チェックを入れると事故が減ります。


導入手順のガイド

最初の一歩

  • IDTExtensibility2で「Excelへ接続してログを書く」最小Add-inを作る
  • 公開メソッドを1つ用意し、選択範囲整形を配列I/Oで実装
  • リボンボタンからそのメソッドを呼ぶ

次の拡張

  • 設定読み取り(JSONなど)
  • ログの整備(ローテーション、レベル)
  • VBA連携(Application.Run)と既存ロジックの活用
  • 配布パッケージ(MSI)と署名

まとめ:COM アドインは「起動枠+公開API+UI+運用基盤」で現場に強い

Excelの常駐拡張として、起動・終了の枠、公開メソッド、リボンUI、ログ・設定・配布の基盤を整えるだけで、あとは機能を載せるだけ。VBA資産を活かしつつ、より堅牢で広範囲に配布できるのがCOM アドインの価値です。

タイトルとURLをコピーしました