Определить, когда COM-объект выходит из области видимости - PullRequest
1 голос
/ 21 апреля 2011

У меня есть компонент .NET, который подвергается воздействию COM.

Этот компонент имеет внутренний поток, который подключается к именованному каналу, обрабатывает отключения от канала и при необходимости восстанавливает соединение.

Поток работает в течение всего времени существования экземпляра компонента, который был создан внутри программы 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 ]

1 голос
/ 21 апреля 2011

В потоке установлено значение ISBackground = true.

COM ничего не знает о вашем потоке, VB6, тем более что он вообще не поддерживает потоки. Установка для свойства Thread.IsBackground значения true указывает CLR, что можно прервать поток при выходе из основного потока программы. Поэтому неудивительно, что его внезапное прекращение без диагностики.

Вероятно, проблема с вашей переменной mRunning, ее нужно объявить volatile . То, как вы используете его в своем потоке, повышает вероятность того, что он никогда не увидит изменения в переменной в сборке Release. Оптимизатор джиттера должен оптимизировать код и загрузить значение переменной в регистр ЦП. Ключевое слово volatile предотвращает это.

Это все еще недоделанное решение, вам действительно следует использовать ManualResetEvent, чтобы сообщить о потоке. Вызовите его метод Set () в вашем методе Close (), WaitOne (0) в потоке, чтобы проверить его. Затем вам также придется ждать завершения потока, чтобы утилизация канала не бомбила код потока. Остерегайтесь тупика.

Теперь вам больше не нужен Thread.IsBackground, и вы получили лучший шанс отладить этот код. Следующее, что вам нужно сделать, это реализовать IDisposable и написать финализатор для вашего класса, чтобы гарантировать, что ваш код завершит свою работу должным образом, даже если клиентский код не вызвал метод Close (). Если не считать того, что вы просто забыли это сделать, это также необходимо, если клиентский код взорвался. Пусть ваш метод Close () вызовет Dispose (). Возможно, вы захотите сделать две разные вещи с конвейером в зависимости от того, нормально ли завершилось приложение. Реализуя шаблон Disposing, вы можете определить разницу между «хорошим» завершением (было вызвано «Close») и «плохим» (был вызван финализатор) с помощью аргумента располагающего .

Теперь ваш код устойчив в любом случае. Узнать, почему клиентский код не вызвал Close (), должно быть просто.

0 голосов
/ 21 апреля 2011

Я исследовал это некоторое время назад и пришел к выводу, что невозможно определить момент выпуска COM-объектов .Net.

Самым близким, что я нашел, был кто-то, кто пытался заменить IUnknown.Releaseметод путем изменения vtable в CCW (в классе Marshal есть несколько методов, которые позволяют вам делать такие вещи).Были некоторые серьезные проблемы, которые я не помню.

Вы просто должны построить свой класс, чтобы он мог справиться с ситуацией, используя внутренние счетчики ссылок и финализаторы.

Мне это кажется страннымчто COM-взаимодействие не позволяет вам добавлять какое-либо событие OnFinalRelease.

0 голосов
/ 21 апреля 2011

Возможно, использовать одно из событий AppDomain .«DomainUnload» и «ProcessExit» выглядят как хорошие кандидаты.

На самом деле, звучит так, будто вы намереваетесь, чтобы управляемый код продолжал работать, даже если приложение COM не работает.Возможно, затем измените WaitOne, чтобы параметр TimeSpan периодически вызывал пробуждение, и проверьте, все ли еще есть клиентское COM-приложение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...