在HoloLens上捕获UWP编译原生代码抛出的SEH异常
我尝试在HoloLens的C#函数中捕获由编译为UWP的C++原生代码抛出的SEH异常。开发环境为Visual Studio,通过Interface Definition (IDL)文件生成.winmd库,无需使用dll import或extern "C"即可与C#代码链接。
我已在VS中配置原生代码的"Enable C++ Exception"为"Yes with SEH Exceptions (/EHa)",但在HoloLens上运行应用时,仅index == 1的异常能被捕获,其余异常会导致应用崩溃。我希望能像在C++中用catch (...)或.NET 4.0之前版本那样捕获这些异常,请问为何无法实现?
原生C++代码
// h file namespace NativeSEHException { void Foo(int index); } // cpp file #include "NativeSEHException.h" #include <iostream> #include <vector> #include <opencv2/opencv.hpp> void NativeSEHException::Foo(int index) { static int i = index; // first call will be with index == 0 if (index == 0) { std::cout << "throw std::exception\n"; throw std::bad_exception(); // This section works, since it's not an SEH exception } else if (index == 1) { std::cout << "divide by zero\n"; int y = 8 / i; std::cout << y << '\n'; } else if (index == 2) { std::cout << "Access nullptr\n"; int* p = nullptr; int y = 8 + *p; std::cout << y << '\n'; } else if (index == 3) { std::cout << "access empty cv::Mat\n"; cv::Mat mat; std::cout << mat.at<float>(0, 3) << '\n'; } else if (index == 4) { std::cout << "vector out of range access\n"; std::vector<float> v; std::cout << v[500000] << '\n'; } }
C#代码
using System; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Security; using UnityEngine; public class MyClass : MonoBehaviour { [HandleProcessCorruptedStateExceptions] [SecurityCritical] public void ExceptionCatcher() { for (int i = 0; i < 5; i++) { try { NativeSEHException.Foo(i); } catch (SEHException e) { Debug.Log($"Caught a SEH exception: {e}"); } catch (Exception e) { Debug.Log($"Caught a general Exception: {e}"); } catch { Debug.Log($"Caught something else"); } } } }
核心原因:UWP平台的安全限制与异常处理机制差异
UWP进程保护机制拦截严重异常
HoloLens运行的UWP应用有严格的进程完整性保护,空指针访问、内存越界这类SEH异常属于进程损坏状态异常(CSEs)。UWP默认会直接终止进程,不会允许托管代码捕获这类可能导致进程不稳定的异常——即使添加[HandleProcessCorruptedStateExceptions]和[SecurityCritical]也无效,这两个特性在UWP平台被限制,无法生效。/EHa的作用范围仅限C++内部
配置的/EHa仅让C编译器能同时处理C异常和SEH异常,但该特性仅在C代码内部有效。当异常跨越C原生代码到C#托管代码的边界时,UWP运行时会拦截严重异常,禁止其传递到托管层。不同异常的兼容性差异
- index=0:抛出的
std::bad_exception属于C++标准异常,是可序列化的托管兼容异常,能通过winmd边界传递到C#,因此可以被捕获。 - index=1:除零错误是UWP中少数允许传递的SEH异常,属于特例,并非所有SEH异常都能通过。
- index=2/3/4:空指针访问、OpenCV空Mat访问、vector越界触发的是
STATUS_ACCESS_VIOLATION这类严重异常,UWP运行时会直接终止进程,不会让托管代码捕获。
可行解决方案
- 在C++层统一处理所有异常
既然UWP不允许托管层捕获严重SEH异常,最可靠的方式是在C代码内部用catch (...)捕获所有异常,将异常信息转换为C/CX的Exception或自定义错误码后传递给C#:
#include <Windows.h> #include <exception> #include <stdexcept> #include <platform.h> void NativeSEHException::Foo(int index) { try { static int i = index; // 原有逻辑代码 } catch (const std::exception& e) { throw ref new Platform::Exception(E_FAIL, ref new Platform::String(e.what())); } catch (...) { DWORD errorCode = GetLastError(); throw ref new Platform::Exception(errorCode, L"Native SEH exception occurred"); } }
这样C#就能通过捕获System.Exception接收所有异常信息。
- 从源头避免严重异常
在C++代码中提前做参数校验与状态检查:
- 访问指针前判空
- vector访问前检查索引是否在有效范围内
- OpenCV操作前检查Mat是否非空(
mat.empty())
从源头避免严重SEH异常的发生,比事后捕获更可靠。
内容的提问来源于stack exchange,提问作者Idan Cohen

