Mr.*_*C64 5 c++ com winapi exception windows-shell
C++异常不能跨越COM模块边界.
所以,假设我们在一个COM方法体中,并且调用一些C++ 潜在抛出的方法/函数(这可能因为例如使用STL类而抛出):
STDMETHODIMP CSomeComServer::DoSomething()
{
CppDoSomething(); // <--- This may throw C++ exceptions
return S_OK;
}
Run Code Online (Sandbox Code Playgroud)
Q1.上面的代码是可行的实现吗?例如,如果该代码是上下文菜单shell扩展的一部分,如果C++ CppDoSomething()
函数抛出C++异常,那么Explorer会做什么?它是否捕获C++异常并卸载shell扩展?在快速失败的方法之后,它是否只是崩溃资源管理器(使用崩溃转储分析问题成为可能)?
Q2.像这样的实现会更好吗?
STDMETHODIMP CSomeComServer::DoSomething()
{
//
// Wrap the potentially-throwing C++ code call in a safe try/catch block.
// C++ exceptions are caught and transformed to HRESULTs.
//
try
{
CppDoSomething(); // <--- This may throw C++ exceptions
return S_OK;
}
//
// Map C++ std::bad_alloc exception to E_OUTOFMEMORY HRESULT.
//
catch(const std::bad_alloc& ex)
{
// ... Log the exception what() message somewhere,
// e.g. using OutputDebugString().
....
return E_OUTOFMEMORY;
}
//
// Map C++ std::exception exception to generic E_FAIL.
//
catch(const std::exception& ex)
{
// ... Log the exception what() message somewhere,
// e.g. using OutputDebugString().
....
return E_FAIL;
}
}
Run Code Online (Sandbox Code Playgroud)
Q3.或者,如果抛出C++异常,只是设置一个内部标志(例如bool m_invalid
数据成员)将COM服务器置于一个不能再工作的状态,那么它会更好,所以每次连续调用它的方法返回一些错误代码,如E_FAIL
或其他一些特定的错误?
Q4.最后,假设Q2/Q3是良好的实现指南,可以try/catch
在一些方便的预处理器宏中隐藏详细的防护(可以在每个COM方法体中重用),例如
#define COM_EXCEPTION_GUARD_BEGIN try \
{
#define COM_EXCEPTION_GUARD_END return S_OK; \
} \
catch(const std::bad_alloc& ex) \
{ \
.... \
return E_OUTOFMEMORY; \
} \
catch(const std::exception& ex) \
{ \
.... \
return E_FAIL; \
}
//
// May also add other mappings, like std::invalid_argument --> E_INVALIDARG ...
//
STDMETHODIMP CSomeComServer::DoSomething()
{
COM_EXCEPTION_GUARD_BEGIN
CppDoSomething(); // <--- This may throw C++ exceptions
COM_EXCEPTION_GUARD_END
}
STDMETHODIMP CSomeComServer::DoSomethingElse()
{
COM_EXCEPTION_GUARD_BEGIN
CppDoSomethingElse(); // <--- This may throw C++ exceptions
COM_EXCEPTION_GUARD_END
}
Run Code Online (Sandbox Code Playgroud)
使用现代的C++ 11/14,是否可以用其他东西替换上述预处理器宏,更方便,更优雅,更好?
切勿让异常跨 COM 边界传播,否则行为未定义,并且可能包括您的 C++ 运行时terminate()
调用您的 C++ 运行时、进程发疯以及其他不错的好处。只是不要这样做。即使您在某些配置中“测试”它 - 它仍然是未定义的行为,并且如果发生较小的环境或实现更改,它会默默地中断。
您应该捕获所有 C++ 异常并将其转换为HRESULT
s 并可选择设置IErrorInfo
详细信息。您可以通过包装每个 COM 服务器方法实现的宏或通过在各处复制粘贴此代码来实现此目的 - 猜猜哪个更易于维护。
在某些极端情况下,将服务器驱动到“无效”状态的想法可能是有意义的,但我目前无法想象它们。我想这不是一个通用的解决方案。一般情况下,如果您有异常安全代码,则根本不需要它。