C++可能在COM方法边界抛出代码

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,是否可以用其他东西替换上述预处理器宏,更方便,更优雅,更好?

sha*_*oth 4

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

您应该捕获所有 C++ 异常并将其转换为HRESULTs 并可选择设置IErrorInfo详细信息。您可以通过包装每个 COM 服务器方法实现的宏或通过在各处复制粘贴此代码来实现此目的 - 猜猜哪个更易于维护。

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