1

ワークブックのクローズイベントから、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 qualifierSystem強調表示されたが表示されます。検索では、VBAマクロからSystem.Runtimeメソッドを呼び出すVBAコードが返されないようです。Excelを自動化するVB.Netしか見つかりません。それが可能かどうかはわかりません。

私はこの問題を解決しようとしています:Excel 2007ゾンビプロセスはCOM自動化ではなく、 Excelが終了する前にこれらのサードパーティCOMオブジェクトが適切に破棄されるようにすることで、サードパーティCOMオブジェクトへの参照を使用します。

4

2 に答える 2

1

私はVBAを使用していませんが、.NET言語ではないと思います。そのため、当然、VBAでSystem.Runtime.InteropServices.Marshalのような.NETFrameworkクラスを使用することはできません。

Excelを終了するときに、COMオブジェクトへのすべての参照を削除してもよろしいですか?COMオブジェクトへのすべての参照に対して、次のような行を配置していることを確認してください。

obj = Nothing ' Where "obj" is a reference to the COM object

それでも解決しない場合は、循環参照が問題である可能性もあります。COMオブジェクトは、COMオブジェクトへの参照を保持するVBAオブジェクトへの参照を格納しますか?その場合、循環参照が作成され、オブジェクトが解放されることはありません。私は何か他のものを探していて、あなたのものと非常によく似た投稿を見つけました:

ガベージコレクションを強制する

それが問題である場合は、Marshal.ReleaseComObjectと同様に、COMオブジェクトを手動で(参照カウントがゼロになるまで数回)解放する方法が本当に必要ですが、VBAでそれを行う方法がわかりません。

他のいくつかの同様のスレッド:

インプロセスCOMサーバーオブジェクトをExcelから解放する方法VBAExcel プロセスは、相互運用後も開いたままです。従来の方法が機能しない

于 2012-11-27T00:44:51.293 に答える
0

循環参照を注意深くチェックして削除した後でも、推奨される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.InteropServicesSystem.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 に答える