即使在仔细检查和删除循环引用之后,我也无法使用推荐的仅 VBA 方法解决僵尸问题。
额外的搜索找到了一种调用方法的方法,该ReleaseCom
方法使用包装的代码System.Runtime.InteropServices.Marshal.FinalReleaseComObject
来创建可以从 VBA 调用的 COM 可见 dll
使用教程“如何使用 vs 2008 创建 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 的方法,而无需先注册它,如果您无法访问客户端计算机上的注册表,这可能会很有用。