C++ 异常不能跨越 COM 模块边界。
因此,假设我们在 COM 方法主体中,并且调用了一些 C++ 可能会抛出 的方法/函数(这可能会抛出,因为例如使用了 STL 类) :
STDMETHODIMP CSomeComServer::DoSomething()
{
CppDoSomething(); // <--- This may throw C++ exceptions
return S_OK;
}
Q1. 上面的代码是可行的实现吗?
例如,如果该代码是上下文菜单外壳扩展的一部分,如果 C++ CppDoSomething()
函数抛出 C++ 异常,Explorer 会做什么?它是否捕获 C++ 异常并卸载 shell 扩展?它是否只是按照 fail-fast 方法使 Explorer 崩溃(从而可以使用故障转储分析问题)?
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;
}
}
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
}
使用现代 C++11/14,是否可以用其他更方便、更优雅、更好的东西替换上述预处理器宏?
最佳答案
永远不要让异常传播到 COM 边界,否则行为是未定义的,并且可能包括您的 C++ 运行时 terminate()
被调用、过程变得疯狂和其他好处。只是不要这样做。即使您在某些配置中“测试”了它 - 它仍然是未定义的行为,并且会在发生微小的环境或实现更改时悄无声息地中断。
您应该捕获所有 C++ 异常并将其转换为 HRESULT
,并可选择设置包含详细信息的 IErrorInfo
。您可以使用宏来包装每个 COM 服务器方法实现,或者通过将此代码复制粘贴到各处 - 猜猜哪个更易于维护。
将服务器驱动到“无效”状态的想法在某些极端情况下可能有意义,但我目前无法想象。我想这不是一个通用的解决方案。在一般情况下,如果您有异常安全代码,则根本不需要它。
关于C++ 可能在 COM 方法边界处抛出代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18213452/