5

C++ 异常不能跨越 COM 模块边界。

所以,假设我们在一个 COM 方法体中,并且调用了一些 C++可能抛出的方法/函数(这可能会抛出,因为例如使用了 STL 类):

STDMETHODIMP CSomeComServer::DoSomething()
{
    CppDoSomething(); // <--- This may throw C++ exceptions
    return S_OK;
}

Q1。上面的代码是一个可行的实现吗?例如,如果该代码是上下文菜单外壳扩展的一部分,如果 C++CppDoSomething()函数抛出 C++ 异常,Explorer 会做什么?它是否捕获 C++ 异常并卸载 shell 扩展?它是否只是按照快速失败的方法使 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,是否可以用其他更方便、更优雅、更好的东西替换上述预处理器宏?

4

1 回答 1

3

永远不要让异常跨 COM 边界传播,否则行为是未定义的,可能包括您的 C++ 运行时terminate()被调用、进程发疯和其他不错的好处。只是不要这样做。即使您在某些配置中“测试”了它 - 它仍然是未定义的行为,并且如果发生较小的环境或实现更改,它将默默地中断。

您应该捕获所有 C++ 异常并将其转换为HRESULTs 并可选择设置IErrorInfo详细信息。您可以使用包装每个 COM 服务器方法实现的宏来执行此操作,或者在任何地方复制粘贴此代码 - 猜猜哪个更易于维护。

将服务器驱动到“无效”状态的想法在某些极端情况下可能是有意义的,但我现在无法想象它们。我想这不是一个通用的解决方案。在一般情况下,如果您有异常安全代码,则根本不需要它。

于 2013-08-14T08:22:10.600 回答