这个 InteropServices.SEHException 的警告怎么可能是真的?
How can this warning of InteropServices.SEHException be real?
我通过用我捕获的异常帧包装非托管调用来防止非托管 C++ 异常从我的 C++/CLI 代码中转义 const std::exception&
。但是我有一个代码路径,其中非托管 C++ throw
立即触发 SEHException
的警告,即使在 C++/CLI 的堆栈框架上显然有一个 catch 子句拦截它.
我不明白 SEHException
会发生在哪里。我想知道它是不是真的。
这是 C++ 代码在调用堆栈中抛出异常的方式。
StringMap ParseTopLevelMap(std::istream& in)
{
StringMap yamlmap;
if (!TryParseTopLevelMap(in, yamlmap))
throw std::runtime_error("Unable to parse map"); // Causes SEHException warning.
return yamlmap;
}
我在上面做 throw
的那一刻,我立即在输出 window:
中得到这个输出
Exception thrown: 'System.Runtime.InteropServices.SEHException' in MyCompany.Sdk_v143.dll
但我非常清楚地在 C++/CLI 的调用框架中有一个 catch(const std::exception& ex)
更高层并且它 确实 被调用。在这里(注意catch
)
bool ScanContext::TryLoad(GsScan^ scan, String^ path, [SRI::Out]ScanContext^% ctx)
{
try
{
ctx = nullptr;
auto sPath= ToSdk(name);
ctx = gcnew ScanContext(scan, gs::LoadScanContext(scan->sdkScan(), sPath));
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl; // This DOES execute
}
return ctx != nullptr; // If we did not throw, this is non-null and we succeeded.
}
因此 上面的 catch
子句 被调用,错误输出消息被转储,并且控制 returns 通常返回到我的托管 C# 代码调用 C++/CLI。
那么 SEHException
去哪儿了?
为了仔细检查,我将“例外”对话框设置为在 SEHException
实际中断。它确实做到了。
这是中断点的调用堆栈。我的 C++ 代码正在抛出 std::runtime_error
ntdll.dll!NtWaitForSingleObject() Unknown
KernelBase.dll!WaitForSingleObjectEx() Unknown
ntdll.dll!RtlpExecuteHandlerForException() Unknown
ntdll.dll!RtlDispatchException() Unknown
ntdll.dll!KiUserExceptionDispatch() Unknown
KernelBase.dll!RaiseException() Unknown
> vcruntime140d.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 75 C++
gscored_v143.dll!gs::detail::YAML::ParseTopLevelMap(std::basic_istream<char,std::char_traits<char>> & in) Line 298 C++
gscored_v143.dll!gs::ScanContext::loadContent(std::basic_istream<char,std::char_traits<char>> & is, const std::string & loadFolder) Line 172 C++
gscored_v143.dll!gs::ScanContext::loadContent(std::basic_istream<char,std::char_traits<char>> & is) Line 63 C++
gscored_v143.dll!gs::ScanContext::Load(const std::shared_ptr<gs::Scan> & scan, const std::string & name) Line 884 C++
gscored_v143.dll!gs::LoadScanContext(const std::shared_ptr<gs::Scan> & scan, const std::string & name) Line 310 C++
[Managed to Native Transition]
MyCompany.Sdk_v143.dll!MyCompany::Sdk::ScanContext::TryLoad(MyCompany::Sdk::Scan^ scan, System::String^ name, MyCompany::Sdk::ScanContext^% ctx) Line 660 C++
MyCompany.Services.dll!MyCompany.Services.ScanService.GetScanContext(MyCompany.Sdk.Scan scan, string name) Line 1725 C#
这只是一个错误的警告吗?
有意思。
从 documentation exactly what's going on here, but there's an interesting article over at Code Project which explains how (unmanaged) C++ exceptions are handled by the Microsoft compiler. Basically, when you call throw
an SEH exception is generated (via RaiseException
) 中一点也不明显,然后被运行时库捕获并映射到 C++ 异常。
现在的问题是如何将其映射到托管 C++ 异常,以便您可以在托管代码中捕获它,其背后的机制一点也不清楚,但 SEHException
显然以某种方式适合它.
也许 .NET 框架通过它自己的 try
... catch
块捕获您的非托管 throw
并使用不同的异常代码再次调用 RaiseException
(一个对应于 / 结果 SEHException
),这就是你在调试器中捕获的内容。然后它会捕获(通过 __try
... __except
)并使用某种魔法生成托管代码异常。反正就是这样。
至于是否需要关心这个,文档接着说:
Note that the SEHException class does not cause unmanaged C++ exception destructors to be called. To ensure that unmanaged C++ exception destructors are called, use the following syntax in the catch block.
C#
catch
{
// Handle catch here.
}
现在我真的不确定 'C++ exception destructors' 是什么意思(我不知道有这样的东西)但可能是 yamlmap
没有被正确销毁。如果那是真的,我会感到惊讶,但它可能值得检查。在 managed 代码中处理异常对我来说也没有意义。也许这只是一个错字。
回到那篇代码项目文章,它引用了一个事实,即非托管 C++ throw
引发的 SEH 异常的异常代码是 0xE06D7363
。如果你突破了这一点,你可能会更多地了解这一切。再一次,也许不是。
我通过用我捕获的异常帧包装非托管调用来防止非托管 C++ 异常从我的 C++/CLI 代码中转义 const std::exception&
。但是我有一个代码路径,其中非托管 C++ throw
立即触发 SEHException
的警告,即使在 C++/CLI 的堆栈框架上显然有一个 catch 子句拦截它.
我不明白 SEHException
会发生在哪里。我想知道它是不是真的。
这是 C++ 代码在调用堆栈中抛出异常的方式。
StringMap ParseTopLevelMap(std::istream& in)
{
StringMap yamlmap;
if (!TryParseTopLevelMap(in, yamlmap))
throw std::runtime_error("Unable to parse map"); // Causes SEHException warning.
return yamlmap;
}
我在上面做 throw
的那一刻,我立即在输出 window:
Exception thrown: 'System.Runtime.InteropServices.SEHException' in MyCompany.Sdk_v143.dll
但我非常清楚地在 C++/CLI 的调用框架中有一个 catch(const std::exception& ex)
更高层并且它 确实 被调用。在这里(注意catch
)
bool ScanContext::TryLoad(GsScan^ scan, String^ path, [SRI::Out]ScanContext^% ctx)
{
try
{
ctx = nullptr;
auto sPath= ToSdk(name);
ctx = gcnew ScanContext(scan, gs::LoadScanContext(scan->sdkScan(), sPath));
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl; // This DOES execute
}
return ctx != nullptr; // If we did not throw, this is non-null and we succeeded.
}
因此 上面的 catch
子句 被调用,错误输出消息被转储,并且控制 returns 通常返回到我的托管 C# 代码调用 C++/CLI。
那么 SEHException
去哪儿了?
为了仔细检查,我将“例外”对话框设置为在 SEHException
实际中断。它确实做到了。
这是中断点的调用堆栈。我的 C++ 代码正在抛出 std::runtime_error
ntdll.dll!NtWaitForSingleObject() Unknown
KernelBase.dll!WaitForSingleObjectEx() Unknown
ntdll.dll!RtlpExecuteHandlerForException() Unknown
ntdll.dll!RtlDispatchException() Unknown
ntdll.dll!KiUserExceptionDispatch() Unknown
KernelBase.dll!RaiseException() Unknown
> vcruntime140d.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 75 C++
gscored_v143.dll!gs::detail::YAML::ParseTopLevelMap(std::basic_istream<char,std::char_traits<char>> & in) Line 298 C++
gscored_v143.dll!gs::ScanContext::loadContent(std::basic_istream<char,std::char_traits<char>> & is, const std::string & loadFolder) Line 172 C++
gscored_v143.dll!gs::ScanContext::loadContent(std::basic_istream<char,std::char_traits<char>> & is) Line 63 C++
gscored_v143.dll!gs::ScanContext::Load(const std::shared_ptr<gs::Scan> & scan, const std::string & name) Line 884 C++
gscored_v143.dll!gs::LoadScanContext(const std::shared_ptr<gs::Scan> & scan, const std::string & name) Line 310 C++
[Managed to Native Transition]
MyCompany.Sdk_v143.dll!MyCompany::Sdk::ScanContext::TryLoad(MyCompany::Sdk::Scan^ scan, System::String^ name, MyCompany::Sdk::ScanContext^% ctx) Line 660 C++
MyCompany.Services.dll!MyCompany.Services.ScanService.GetScanContext(MyCompany.Sdk.Scan scan, string name) Line 1725 C#
这只是一个错误的警告吗?
有意思。
从 documentation exactly what's going on here, but there's an interesting article over at Code Project which explains how (unmanaged) C++ exceptions are handled by the Microsoft compiler. Basically, when you call throw
an SEH exception is generated (via RaiseException
) 中一点也不明显,然后被运行时库捕获并映射到 C++ 异常。
现在的问题是如何将其映射到托管 C++ 异常,以便您可以在托管代码中捕获它,其背后的机制一点也不清楚,但 SEHException
显然以某种方式适合它.
也许 .NET 框架通过它自己的 try
... catch
块捕获您的非托管 throw
并使用不同的异常代码再次调用 RaiseException
(一个对应于 / 结果 SEHException
),这就是你在调试器中捕获的内容。然后它会捕获(通过 __try
... __except
)并使用某种魔法生成托管代码异常。反正就是这样。
至于是否需要关心这个,文档接着说:
Note that the SEHException class does not cause unmanaged C++ exception destructors to be called. To ensure that unmanaged C++ exception destructors are called, use the following syntax in the catch block.
C#
catch
{
// Handle catch here.
}
现在我真的不确定 'C++ exception destructors' 是什么意思(我不知道有这样的东西)但可能是 yamlmap
没有被正确销毁。如果那是真的,我会感到惊讶,但它可能值得检查。在 managed 代码中处理异常对我来说也没有意义。也许这只是一个错字。
回到那篇代码项目文章,它引用了一个事实,即非托管 C++ throw
引发的 SEH 异常的异常代码是 0xE06D7363
。如果你突破了这一点,你可能会更多地了解这一切。再一次,也许不是。