问题:
从非托管代码进入CLR的线程中的未处理异常不会触发“正常”未处理异常 CLR 处理。
CSSimpleObject.GetstringLength()
在下面从 C++调用的代码中
- "1" 在调用线程(非 CLR 创建的线程)中引发异常,
- “2”在 new Thread()(CLR 创建的线程)中引发异常。
在“1”的情况下
- CurrentDomain_UnhandledException() 永远不会被调用。
- 应用程序域和进程将保持加载和运行,您只会得到一个 FAILED。
情况“2”(预期行为)
- CurrentDomain_UnhandledException() 被调用。
- 进程被杀死。
问题:
必须做什么才能获得“正常”行为?
示例代码:
下面的代码基于“所有互操作和融合示例”中的 Visual Studio 2010“ CppHostCLR ”代码示例。
运行时主机(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 创建的内部线程中,线程中止和应用程序域卸载异常的处理方式不同。在非 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 而不是内存不足 - 不知道原始错误来自何处)!