我想在我的VBA代码中实例化的一些第三方COM对象上调用System.Runtime.InteropServices.Marshal.ReleaseComObject(obj),来自我的工作簿关闭事件,如下所示:
Public Sub disconnect(obj As Variant)
Dim refs As Long
refs = 0
If Not obj Is Nothing Then
Do
refs = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
Loop While (refs > 0)
End If
End Sub
但是,我使用上面的代码突出显示compile error: invalid qualifier
System
。搜索似乎没有返回从VBA宏调用System.Runtime方法的任何VBA代码 - 我只能找到VB.Net自动化Excel。我不确定它是否可能。
我正在尝试通过确保在Excel退出之前妥善处理这些第三方COM对象来解决此问题:Excel 2007 Zombie Process not COM automation but w/ references to 3rd party com objects。
答案 0 :(得分:1)
我没有使用过VBA,但我认为它不是.NET语言,所以很自然地你不能在VBA中使用.NET Framework类,如System.Runtime.InteropServices.Marshal。
当您希望Excel退出时,您确定要删除对COM对象的所有引用吗?确保您为COM对象的每个引用放置如下所示的行:
obj = Nothing ' Where "obj" is a reference to the COM object
如果这不能解决问题,那么问题也可能是循环引用。 COM对象是否将refenrece存储到您的VBA对象中,而VBA对象又保存对COM对象的引用?如果是这样,将创建循环引用,并且永远不会释放对象。我正在寻找别的东西,发现了一个与你非常相似的帖子:
如果这是问题,你真的需要一种方法来手动释放COM对象(并且几次直到引用计数为零)类似于Marshal.ReleaseComObject,但我不知道你怎么能这样做VBA。
其他几个相似的主题:
How to release inprocess COM Server object from Excel VBA Excel process remains open after interop; traditional method not working
答案 1 :(得分:0)
即使在仔细检查和删除循环引用后,我也无法使用推荐的仅使用VBA的方法来解决僵尸问题。
其他搜索方式可以调用ReleaseCom
方法using code that wraps System.Runtime.InteropServices.Marshal.FinalReleaseComObject
to create a COM visible dll that can be called from VBA
使用教程"how to create a com object using vs 2008 and consume it from vb6.0 client"和新安装的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干净利落地关闭,不再创建僵尸进程。
有趣的是,我只是遇到了这个method for using your dll from VBA without having to register it first,如果您无法访问客户端计算机上的注册表,这可能会很有用。