無効になったアドインを有効化してからExcelを起動する


はじめに

業務アプリケーションで、ExcelやWordのアドインをVSTOで開発しています。
ExcelやWordは他の.NETアプリケーションからコマンド起動するのですが、何らかの原因でアドインが無効になってしまう事があり、業務に支障が出ていました。

無効になったアドインを有効化してからExcelやWordを起動する方法をまとめます。

アドインが読み込まれない原因

アドインが読み込まれない場合、以下のいずれかに登録されてしまっている事が原因です。
ExcelやWordの[ファイル]-[オプション]-[アドイン]で確認することができます。

  • アクティブでないアプリケーションアドイン
  • 無効なアプリケーションアドイン

アクティブでないアプリケーションアドイン

以下が原因で、アクティブでないアプリケーションアドインに登録される事があります。

  • アドインの読み込み時にエラーがスローされた
  • アドインの動作に時間がかかった
  • アドインの動作時にエラーがスローされた

Microsoftのドキュメントでは、ソフトに無効化された VSTO アドインという表現が使われています。
https://docs.microsoft.com/ja-jp/visualstudio/vsto/how-to-re-enable-a-vsto-add-in-that-has-been-disabled#soft-disabled-vsto-add-ins

無効なアプリケーションアドイン

以下が原因で、無効なアプリケーションアドインに登録される事があります。
ExcelやWordがクラッシュする、よほどのエラーケースという事ですね。

  • アドインが原因でExcel/Wordが予期せず終了した

Microsoftのドキュメントでは、ハードに無効化された VSTO アドインという表現が使われています。
https://docs.microsoft.com/ja-jp/visualstudio/vsto/how-to-re-enable-a-vsto-add-in-that-has-been-disabled#hard-disabled-vsto-add-ins

アドインが管理されているレジストリ

それぞれ、以下のレジストリエントリで管理されています。

アクティブでないアプリケーションアドイン

アクティブでないアプリケーションアドインに登録されているかどうかは、以下のレジストリを調べることで判断できます。
「Excel」の部分は「Word」「Outlook」でも同様です。

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\Excel\Addins\<アドイン名>
LoadBehavior の値が "2"
(アクティブなアプリケーションアドインは"3")

LoadBehavior の値が "3"以外の場合、"3"に更新する、という処理で有効化できます。

参考:LoadBehavior の値
https://docs.microsoft.com/ja-jp/visualstudio/vsto/registry-entries-for-vsto-add-ins#loadbehavior-values

無効なアプリケーションアドイン

無効なアプリケーションアドインに登録されているかどうかは、以下のレジストリを調べることで判断できます。
「Excel」の部分は「Word」「Outlook」でも同様です。

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\<バージョン>\Excel\Resiliency\DisabledItems
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\<バージョン>\Excel\Resiliency\CrashingAddinList

※ <バージョン> には以下の数字が入ります
Outlook 2013 : 15.0
Microsoft 365 Apps / Outlook 2019 / Outlook 2016 : 16.0

これらのレジストリの値の名前はランダムで、値にはアドイン名がバイナリ値で格納されています。

アドイン名の判断が難しい場合は、CrashingAddinList や DisabledItems キーごと削除するでも良いかもしれません。

アドインを有効化するコード

上記のレジストリをチェックし、アドインを有効化するコードです。参考になれば幸いです。

using Microsoft.Win32;

private void checkAddin() {
    var app = "Excel";
    var addin = "hogeAddin";

    //
    // 「アクティブでないアプリケーションアドイン」のチェック
    //
    try
    {
        var regKey = $@"SOFTWARE\Microsoft\Office\{app}\Addins\{addin}";
        using (var key = Registry.CurrentUser.OpenSubKey(regKey, true))
        {
            if (key == null)
            {
                MessageBox.Show("アドインがインストールされていません。");
                return;
            }

            if (!int.Equals(key.GetValue("LoadBehavior", -1), 3)) {
                // アクティブになっていないので「3」を設定する
                key.SetValue("LoadBehavior", 3, RegistryValueKind.DWord);
            }
        }
    }
    catch (Exception e)
    {
        // エラーは無視
        Console.Error.WriteLine("レジストリ[LoadBehavior]チェック:Exception\n" + e.ToString());
    }

    //
    // 「無効なアプリケーションアドイン」のチェック
    //
    try
    {
        using (var key = Registry.CurrentUser.OpenSubKey($@"SOFTWARE\Microsoft\Office\{getOfficeVer()}\{app}\Resiliency\DisabledItems", true))
        {
            if (key != null)
            {
                key.GetValueNames()
                .ToList()
                .ForEach(val =>
                {
                    var data = (byte[])key.GetValue(val, null);
                    if (data != null)
                    {
                        // キーで読みだした値から「0」を除去して結合。ASCIIから文字列を取得し、アドイン名が含まれている場合は該当のレジストリを消去。
                        if (System.Text.Encoding.UTF8.GetString(data.ToList().Where(v => v != 0).ToArray()).Contains($"{addin}"))
                        {
                            key.DeleteValue(val);
                        }
                    }
                });
            }
        }
    }
    catch (Exception e)
    {
        // エラーは無視
        Console.Error.WriteLine("レジストリ[DisabledItems]チェック:Exception\n" + e.ToString());
    }

    try
    {
        using (var key = Registry.CurrentUser.OpenSubKey($@"SOFTWARE\Microsoft\Office\{getOfficeVer()}\{app}\Resiliency\CrashingAddinList", true))
        {
            if (key != null)
            {
                key.GetValueNames()
                .ToList()
                .ForEach(val =>
                {
                    var data = (byte[])key.GetValue(val, null);
                    if (data != null)
                    {
                        // キーで読みだした値から「0」を除去して結合。ASCIIから文字列を取得し、アドイン名が含まれている場合は該当のレジストリを消去。
                        if (System.Text.Encoding.UTF8.GetString(data.ToList().Where(v => v != 0).ToArray()).Contains($"{addin}"))
                        {
                            key.DeleteValue(val);
                        }
                    }
                });
            }
        }
    }
    catch (Exception e)
    {
        // エラーは無視
        Console.Error.WriteLine("レジストリ[CrashingAddinList]チェック:Exception\n" + e.ToString());
    }
}