检测COM对象何时超出范围

时间:2011-04-21 05:58:34

标签: .net multithreading com

我有一个暴露给COM的.NET组件。

此组件在内部运行一个连接到命名管道的线程,并处理与管道的断开连接并根据需要重新连接。

线程运行在COM程序中实例化的组件实例的生命周期(在本例中为vb6)。

如果COM应用程序正在关闭,但没有调用我正好关闭线程(和管道)的“关闭”方法,有没有办法检测到他们正在关闭应用程序,这样我就不需要依靠他们正确遵守规则?

线程有ISBackground = true set。

这是我的线程的基本部分

private System.IO.Pipes.NamedPipeClientStream mPipe;
private System.Threading.AutoResetEvent mConnectedHold;

private void ConnectPipe()
{
    while (mRunning)
    {
        try
        {
            mConnecting = true;
            mConnected = false;
            if (mPipe != null)
            {
                mPipe.Close();
                mPipe.Dispose();
            }
            mPipe = new System.IO.Pipes.NamedPipeClientStream(@".", @"MyPipe", System.IO.Pipes.PipeDirection.InOut, System.IO.Pipes.PipeOptions.Asynchronous);
            mPipe.Connect(1000);

            mConnecting = false;
            mConnected = true;

            mPipe.BeginRead(mBuffer, 0, mBuffer.Length, new AsyncCallback(ReadPipe), null);

            mConnectedHold.WaitOne();
        }
        catch (TimeoutException ex)
        {
        System.Diagnostics.Debug.Print("Not connected yet");
        }
        catch (Exception ex)
        {
            mRunning = false;
        }
    }
}

并且收盘相当基本。

public void Close()
{
    mRunning = false;
    if (mPipe != null )
    {
        mPipe.Close()
        mPipe.Dispose()
    }
    mConnected = false;
}

3 个答案:

答案 0 :(得分:1)

  

线程有ISBackground = true set。

COM对你的线程VB6一无所知,特别是因为它根本不支持线程。将Thread.IsBackground属性设置为true告诉CLR可以在程序的主线程退出时中止线程。因此,看到它在没有诊断的情况下突然终止并不足为奇。

您的mRunning变量可能存在问题,需要将其声明为 volatile 。在线程中使用它的方式使得它可能永远不会在Release版本中看到对变量的更改。抖动优化器可以优化代码并将变量值加载到CPU寄存器中。 volatile 关键字会阻止此操作。

这仍然是一个半生不熟的解决方案,你真的应该使用ManualResetEvent来通知线程。在Close()方法中调用其Set()方法,在线程中调用WaitOne(0)来测试它。然后你还必须等待线程退出,以便处理管道不会轰炸线程代码。注意僵局。

现在你不再需要Thread.IsBackground了,并且更好地调试了这段代码。接下来你需要做的是实现IDisposable并为你的类编写一个终结器,以确保你的代码正确关闭,即使客户端代码没有很好地调用Close()方法。如果忘记这样做,如果客户端代码被轰炸,这也是必要的。让Close()方法调用Dispose()。您可能希望使用管道执行两项不同的操作,具体取决于应用程序是否以正常方式关闭。实现Disposing模式,您可以通过 disposing 参数来区分“nice”关闭(Close被调用)与讨厌的(调用finalizer)之间的区别。

现在你的代码都有弹性。找出客户端代码没有调用Close()的原因应该很简单。

答案 1 :(得分:0)

也许使用其中一个AppDomain events。 “DomainUnload”和“ProcessExit”看起来很不错。

实际上,即使COM应用程序出现故障,您仍然希望托管代码保持运行。也许然后更改你的WaitOne以使TimeSpan参数定期唤醒,并测试客户端COM应用程序是否仍在那里。

答案 2 :(得分:0)

前一段时间我对它进行了调查,得出的结论是,无法检测何时释放.Net COM对象。

我发现最接近的是试图通过改变CCW的vtable来替换IUnknown.Release方法的人(Marshal类中有一些方法可以让你做这些事情)。有一些我不记得的严重问题。

您只需要构建您的类,以便它可以使用内部引用计数和终结器来处理这种情况。

对我来说,COM互操作不允许你添加某种OnFinalRelease事件似乎很奇怪。