1

我想从我的工作簿关闭事件中对我在 VBA 代码中实例化的一些第 3 方 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 2007 Zombie Process not COM 自动化,但通过确保在 Excel 退出之前正确处理这些第 3 方 COM 对象来引用第 3 方 com 对象。

4

2 回答 2

1

我没有使用过 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 对象的引用?如果是这样,将创建循环引用并且永远不会释放对象。我正在搜索其他内容,发现一个与您的帖子非常相似的帖子:

强制垃圾回收

如果这是问题所在,您将真的需要一种类似于 Marshal.ReleaseComObject 的手动释放 COM 对象的方法(并且多次释放直到引用计数为零),但我不知道如何在 VBA 中做到这一点。

其他几个类似的线程:

如何从 Excel VBA 中释放进程内 COM 服务器对象 Excel 进程在互操作后保持打开状态;传统方法不起作用

于 2012-11-27T00:44:51.293 回答
0

即使在仔细检查和删除循环引用之后,我也无法使用推荐的仅 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 的方法,而无需先注册它,如果您无法访问客户端计算机上的注册表,这可能会很有用。

于 2012-11-29T19:12:30.327 回答