Обработка исключений в CLR-хостинге в потоке, не созданном CLR - PullRequest
17 голосов
/ 25 мая 2011

Проблема:

Необработанное исключение в потоке, вводящем CLR из неуправляемого кода, не вызывает "обычную" обработку CLR необработанного исключения.

ВПриведенный ниже код вызывает CSSimpleObject.GetstringLength() из C ++ с

  • «1» создает исключение в вызывающем потоке (не созданный CLR поток),
  • «2»исключение в новом потоке () (поток, созданный в CLR).

В случае, если "1"

  • CurrentDomain_UnhandledException () никогда не вызывается.
  • Домен приложения и процесс останется загруженным и запущенным, вы получите только СБОЙ.

В случае «2» (ожидаемое поведение)

  • CurrentDomain_UnhandledException () вызывается.
  • Процесс убит.

Вопрос:

Что нужно сделать, чтобы получить "«нормальное поведение»?

Пример кода:

Приведенный ниже код основан на Visual Studio 2010 " CppHostCLR " code-образец из « всех образцов взаимодействия и слияния ».

RuntimeHost (C ++):

PCWSTR pszStaticMethodName = L"GetStringLength";
PCWSTR pszStringArg = L"1";
//PCWSTR pszStringArg = L"2";

hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(pszAssemblyPath,
    pszClassName, pszStaticMethodName, pszStringArg, &dwLengthRet);
if (FAILED(hr))
{
    wprintf(L"Failed to call GetStringLength w/hr 0x%08lx\n", hr);
    goto Cleanup;
}

Управляемый код (C #):

public class CSSimpleObject
{
    public CSSimpleObject()
    {
    }
    //------8<----------
    public static int GetStringLength(string str)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        switch (str)
        {
            case "1":
                throw new Exception("exception in non-CLR-created thread");
            case "2":
                Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
                thread.Start();
                break;
        }
        return str.Length;

    }
    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine("CurrentDomain_UnhandledException:" + e.ToString());
        Console.ReadKey();
    }
    public static void WorkThreadFunction()
    {
        throw new Exception(""exception in CLR-created thread"");
    }

Исследования на данный момент:

Изначально MSDN подразумевает, что необработанные исключения в потоке, не созданном CLR, должны вести себя более или менее "естественно" - см. " Исключения в управляемых потоках "

Общеязыковая среда выполнения позволяет большинству необработанных исключений в потоках работать естественным образом.В большинстве случаев это означает, что необработанное исключение приводит к завершению работы приложения. "

" Большинство "означает, что в потоках, созданных CLR, внутренние, прерывание потока и выгруженные исключения Application Domain обрабатываются по-разному.-CLR потоков

"они работают нормально, что приводит к завершению приложения."

Дальнейшие исследования привели меня к " Обработка необработанных исключений в CLR"где я обнаружил следующее:

" если исключение не было обработано ... в управляемом методе исключение выйдет из CLR, но продолжит распространяться вверх по стеку как собственныйИсключение SEH (управляемые исключения представлены как собственные исключения SEH) ... Механизм фильтра необработанных исключений (UEF) ОС не всегда может привести к запуску обработки необработанных исключений CLR.При нормальных обстоятельствах это будет работать так, как ожидается, и обработка необработанного исключения CLR будет запущена.Однако в некоторых случаях это может не произойти. "

Что не так с приведенным выше кодом или как его можно изменить, чтобы запустить обработку необработанных исключений CLR?

Обновить(2011-05-31):

Я только что обнаружил старый отчет об ошибке: «UnhandledExceptionEventHandler не вызывается, когда управляемый код вызывается из неуправляемого и выдает исключение - http://tinyurl.com/44j6gvu",, когда Microsoft подтверждает, что это«глючное» поведение:

Спасибо, что нашли время сообщить об этой проблеме. Это действительно ошибка, вызванная механизмом исполнения CLR и CRT, конкурирующим за UnhandledExceptionFilter .Архитектура CLR была пересмотрена в версии 4.0, поддерживающей этот сценарий.

Обновление (2011-06-06):

Почему важно сделать это правильно?

  • если вы создаете хостинг-среду, ваши разработчики ожидают последовательного поведения при обработке исключений
  • , если в собственном потоке нет способа запустить «обычную обработку исключений CLR»это означает, что вам всегда нужно передавать выполнение в управляемый поток (например, в очередь в пуле потоков)
  • все еще есть крошечный кусочек кода, передающий исключение из собственного потока в управляемый поток ... который должен перехватить всеисключения и каким-то образом обрабатывать эту ситуацию по-разному

Примечание: изменение поведения CLR с помощью SetActionOnFailure() усугубляет ситуацию в том смысле, что в конечном итоге он маскирует исходное исключение (т.е. вместо нехватки памяти вы в конечном итоге видите threadAborts - безПодсказка, откуда исходная ошибка камеры)

1 Ответ

2 голосов
/ 04 июня 2011

Обновление подразумевает, что у вас, возможно, есть решение здесь. Тем не менее, его решение не будет работать во всех случаях, поэтому здесь есть дополнительная информация.

Если вы предпочитаете поведение CLR Unhandled Exception, сделайте это самой внешней программой и вызывайте собственный код только для выполнения определенных функций. Это гарантирует, что CLR получит контроль над фильтром необработанных исключений.

Если вы хотите сохранить свою текущую структуру и у вас небольшой код C ++, вы можете вообще отказаться от использования CRT (что лишит вас кучу полезных вещей, включая статические конструкторы и обработку исключений C ++). Это гарантирует, что CLR получит фильтр необработанных исключений.

И, конечно, вы можете просто вызвать SetUnhandledExceptionFilter самостоятельно и получить желаемое поведение.

Однако я думаю, что лучшая рекомендация в этой ситуации - поместить реальную функцию с блоком перехвата в стек вызовов любого потока, где вы хотите что-то делать, когда происходит исключение, и не полагаться на механизм UEF - потому что в контексте системы компонентов она всегда хрупкая, поскольку за нее борются несколько пользователей.

Martyn

...