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

标签 c++ winapi exception com windows-shell

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/

相关文章:

c++ - 如何检查窗口是否为 "Always on top"?

c# - 抛出 KeyNotFoundException 时,如何查看未找到哪个 key ?

php - 当 DB 返回错误语句时处理 PDOException

c++ - 如何使用 WinApi 从 MS SQL 表中检索大型二进制数据?

c++ - 获取最新的 Windows 更新检查 C++ wuapi

java - 运行方法时出现很多错误

c++ - 为什么我需要在这个函数中使用指向指针的指针而不是其他任何地方?

c++ 生成静态库,然后将其与 Clion/Cmake 一起使用

c++ - 基类之后的专用 type_traits = 未定义的行为?

c++ - 从 QT 中的 Q_PROPERTY 序列化嵌套的用户定义类