如何从Excel 2007 VBA模块中调用System.Runtime.InteropServices.Marshal.ReleaseComObject
我想调用System.Runtime.InteropServices.Marshal.ReleaseComObject(obj),调用我在VBA代码中从工作簿关闭事件中实例化的某些第三方COM对象,如下所示:如何从Excel 2007 VBA模块中调用System.Runtime.InteropServices.Marshal.ReleaseComObject,com,excel,com-interop,excel-interop,vba,Com,Excel,Com Interop,Excel Interop,Vba,我想调用System.Runtime.InteropServices.Marshal.ReleaseComObject(obj),调用我在VBA代码中从工作簿关闭事件中实例化的某些第三方COM对象,如下所示: Public Sub disconnect(obj As Variant) Dim refs As Long refs = 0 If Not obj Is Nothing Then Do
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
但是,我得到了一个编译错误:使用上面代码突出显示的系统的限定符无效。搜索似乎不会返回任何从VBA宏调用System.Runtime方法的VBA代码-我只能找到VB.Net自动化Excel。我甚至不确定这是否可能
我试图解决这个问题:通过确保在Excel退出之前正确处理这些第三方COM对象 我没有使用VBA,但我不认为它是.NET语言,因此自然不能在VBA中使用System.Runtime.InteropServices.Marshal之类的.NET Framework类
确实要退出Excel时删除对COM对象的所有引用吗?对于COM对象的每个引用,请确保按如下方式放置线:
obj = Nothing ' Where "obj" is a reference to the COM object
如果这不能解决问题,那么问题也可能是循环引用。COM对象是否存储对您的VBA对象的引用,而VBA对象又保存对COM对象的引用?如果是这样,将创建一个循环引用,并且永远不会释放对象。我在搜索其他内容,发现了一篇与你非常相似的帖子:
如果这就是问题所在,那么您确实需要一种手动释放COM对象的方法(并多次释放,直到引用计数为零),类似于Marshal.ReleaseComObject,但我不知道如何在VBA中做到这一点
两个其他类似的线程:
我没有使用VBA,但我认为它不是.NET语言,因此自然不能在VBA中使用System.Runtime.InteropServices.Marshall之类的.NET Framework类
确实要退出Excel时删除对COM对象的所有引用吗?对于COM对象的每个引用,请确保按如下方式放置线:
obj = Nothing ' Where "obj" is a reference to the COM object
如果这不能解决问题,那么问题也可能是循环引用。COM对象是否存储对您的VBA对象的引用,而VBA对象又保存对COM对象的引用?如果是这样,将创建一个循环引用,并且永远不会释放对象。我在搜索其他内容,发现了一篇与你非常相似的帖子:
如果这就是问题所在,那么您确实需要一种手动释放COM对象的方法(并多次释放,直到引用计数为零),类似于Marshal.ReleaseComObject,但我不知道如何在VBA中做到这一点
两个其他类似的线程:
即使在仔细检查和删除循环引用之后,我也无法使用推荐的仅使用VBA的方法解决僵尸问题
额外的搜索找到了一种调用ReleaseCom
方法的方法
使用本教程和新安装的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);
}
}
}
要生成、创建新的类库项目,请添加对System.Runtime.InteropServices
和System.enterpriseseservices
的引用,启用“对程序集进行签名”(在“项目/属性/签名”下的菜单中),然后选择或创建密钥文件。添加类和接口代码文件。在AssemblyInfo.cs文件(位于“属性”下)中,添加
及
建造。如果一切顺利,您可以按如下方式注册dll:
regsvcs "C:\Documents and Settings\username\My Documents\Visual Studio 2010\Projects\ComDispose\ComDispose\obj\Release\ComDisposer.dll"
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
在VBA中,添加对新COM库的引用后,按如下方式使用它:
regsvcs "C:\Documents and Settings\username\My Documents\Visual Studio 2010\Projects\ComDispose\ComDispose\obj\Release\ComDisposer.dll"
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的方法解决僵尸问题
额外的搜索找到了一种调用ReleaseCom
方法的方法
使用本教程和新安装的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);
}
}
}
要生成、创建新的类库项目,请添加对System.Runtime.InteropServices
和System.enterpriseseservices
的引用,启用“对程序集进行签名”(在“项目/属性/签名”下的菜单中),然后选择或创建密钥文件。添加类和接口代码文件。在AssemblyInfo.cs文件(位于“属性”下)中,添加
及
建造。如果一切顺利,您可以按如下方式注册dll:
regsvcs "C:\Documents and Settings\username\My Documents\Visual Studio 2010\Projects\ComDispose\ComDispose\obj\Release\ComDisposer.dll"
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
在VBA中,添加对新COM库的引用后,按如下方式使用它:
regsvcs "C:\Documents and Settings\username\My Documents\Visual Studio 2010\Projects\ComDispose\ComDispose\obj\Release\ComDisposer.dll"
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干净地关闭,不再创建僵尸进程
有趣的是,我刚刚遇到了这个问题,如果您无法访问客户端计算机上的注册表,它可能会很有用。谢谢。我将寻找循环引用,因为它是最有可能的罪魁祸首。顺便说一句,你需要一个相邻的URL之间的空行,否则只有第一个将被转换为一个实时链接。(我会为您修复它,但还没有必要的编辑权限。)不得不求助于创建COM包装。我发布了我的解决方案并移动了“接受”标志。谢谢。我将寻找循环引用,因为它是最有可能的罪魁祸首。顺便说一句,你需要一个相邻的URL之间的空行,否则只有第一个将被转换为一个实时链接。(我会为您修复它,但还没有必要的编辑权限。)不得不求助于创建COM包装。我发布了我的解决方案并移动了“接受”标志。