Обработка ошибок исключения CLR в потоке, создаваемом не CLR

17

Проблема:

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

В приведенном ниже коде вызов CSSimpleObject.GetstringLength() из C ++ с

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

В случае «1»

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

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

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

Вопрос:

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

Пример кода:

Нижеприведенный код основан на примере Visual Studio 2010 CppHostCLR " из всех interop и fusion samples ".

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 не вызывается, когда управляемый код вызывается из неуправляемого и генерирует исключение - Ссылка " , где Microsoft подтверждает, что это «багги»:

  

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

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

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

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

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

    
задан archgl 25.05.2011 в 14:43
источник
  • интересно, если вы смогли найти решение проблемы № 2 выше, я столкнулся с аналогичной ситуацией –  np-hard 29.10.2011 в 20:43

1 ответ

2

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

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

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

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

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

Мартын     

ответ дан Martyn Lovell 04.06.2011 в 20:21
  • Извините, что обновление было введено в заблуждение - я просто добавил из-за подтверждения microsoft, что поведение является ошибкой. И если вы видите исходный вопрос, то я ищу, как изменить код, так что запускается нормальный процесс завершения clr (с вызовом необработанного исключения). В «Обработка необработанных исключений в среде CLR» автор объясняет, что это «неправильное» поведение связано с механизмом UEF и способом настройки фильтров. Итак, вопрос в том, как мне их устанавливать? –  archgl 06.06.2011 в 09:58
  • На самом деле у нас есть обходное решение: 1) try / catch all в вызове метода по собственному потоку 2) enqueue как действие в workthreadpool. Только минимальный код может пойти не так и не вызвать «нормальный clr» процесс завершения. Остальная часть кода трактуется «normaly» от clr ». –  archgl 06.06.2011 в 09:58
  • Мне нравится ваш товар один. Просто не __try / __ поймать все в / код EHa C ++. –  Martyn Lovell 07.06.2011 в 05:52
  • Ловушка все находится в управляемом коде, вызываемом собственным потоком. В c ++ нет даже try / catch, который выполняется com plumbing, где вы проверяете неудачу / успех на код HRESULT. Захват в управляемом случае необходим, потому что нет другого способа получить исключение ... если кто-то не скажет мне, как :). –  archgl 07.06.2011 в 10:25