循環参照を注意深くチェックして削除した後でも、推奨されるVBAのみの方法を使用してゾンビの問題を解決できませんでした。
追加の検索により、ラップするコードを使用しReleaseCom
てメソッドを呼び出し、 VBAから呼び出すことができるCOM表示可能なdllを作成する方法が見つかりました。System.Runtime.InteropServices.Marshal.FinalReleaseComObject
チュートリアル「vs2008を使用してcomオブジェクトを作成し、vb6.0クライアントから使用する方法」と新しくインストールしたVS2010 Expressのコピーを使用して、VBAから呼び出し可能なCOM可視dllを作成することができました。
わずかに変更されたラッパーとdllのビルド方法は次のとおりです。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
namespace ComDisposerLib
{
[ClassInterface(ClassInterfaceType.None)]
[ComponentAccessControl(false)]
public class ComDisposer : System.EnterpriseServices.ServicedComponent, IDisposable, ComDisposerLib.IComDispose
{
private List<Object> _comObjs;
public ComDisposer()
{
_comObjs = new List<Object>();
}
~ComDisposer()
{
Dispose(false);
}
public Object Add(Object o)
{
if (o != null && o.GetType().IsCOMObject)
_comObjs.Add(o);
return o;
}
public void Clear()
{
Dispose(true);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
for (int i = _comObjs.Count - 1; i >= 0; --i)
Marshal.FinalReleaseComObject(_comObjs[i]);
_comObjs.Clear();
}
}
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
およびインターフェース:
using System;
namespace ComDisposerLib
{
public interface IComDispose
{
Object Add(Object o);
void Clear();
void Dispose();
}
}
ビルドするには、新しいクラスライブラリプロジェクトを作成し、への参照を追加しSystem.Runtime.InteropServices
、System.EnterpriseServices
[アセンブリに署名する]を有効にして(プロジェクト/プロパティ/署名の下のメニューで)、キーファイルを選択または作成します。クラスとインターフェイスのコードファイルを追加します。AssemblyInfo.csファイル(プロパティの下にあります)に追加します
using System.Runtime.InteropServices;
using System.EnterpriseServices;
と
[assembly: ComVisible(true)]
[assembly: ApplicationName("ComDisposer")]
[assembly: ApplicationActivation(ActivationOption.Library)]
ビルドします。すべてがうまくいけば、次のようにdllを登録できます。
regsvcs "C:\Documents and Settings\username\My Documents\Visual Studio 2010\Projects\ComDispose\ComDispose\obj\Release\ComDisposer.dll"
VBAでは、新しいCOMライブラリへの参照を追加した後、次のように使用します。
Sub disposeGlobalComObjects()
' global scope objects used only to simplify example
Dim cd As ComDisposer
Set cd = New ComDisposer
If Not SomeGlobalComObject Is Nothing Then
cd.Add SomeGlobalComObject
Set SomeGlobalComObject = Nothing
End If
If Not AnotherGlobalComObject Is Nothing Then
cd.Add AnotherGlobalComObject
Set AnotherGlobalComObject = Nothing
End If
cd.Dispose
End Sub
初期のテストでは、Excelが正常に閉じ、ゾンビプロセスが作成されなくなったことが示されています。
興味深いことに、最初に登録しなくてもVBAからdllを使用するためのこの方法に出くわしました。これは、クライアントマシンのレジストリにアクセスできない場合に便利です。