5

C++ の例外は、COM モジュールの境界を超えることはできません。

したがって、COM メソッド body にいて、潜在的にスローするC++メソッド/関数が呼び出されたとします (たとえば、STL クラスが使用されているため、これがスローされる可能性があります)。

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

Q1. 上記のコードは実行可能な実装ですか? たとえば、そのコードがコンテキスト メニューのシェル拡張機能の一部である場合、C++CppDoSomething()関数が C++ 例外をスローすると、エクスプローラーはどうするでしょうか? C++ 例外をキャッチし、シェル拡張機能をアンロードしますか? フェイルファーストアプローチに従って、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()が呼び出されたり、プロセスが狂ったり、その他の素晴らしいボーナスが含まれる可能性があります。やらないでください。何らかの構成で「テスト」したとしても、まだ未定義の動作であり、環境や実装のマイナーな変更が発生すると、静かに壊れます。

HRESULTすべての C++ 例外をキャッチしてs に変換し、必要に応じIErrorInfoて詳細を設定する必要があります。これを行うには、各 COM サーバー メソッドの実装をラップするマクロを使用するか、このコードをどこにでもコピー アンド ペーストします。どちらが保守しやすいかを推測してください。

サーバーを「無効」状態にするという考えは、いくつかの極端な状況では理にかなっているかもしれませんが、現時点では想像できません。普遍的な解決策ではないと思います。一般に、例外セーフ コードがある場合、これはまったく必要ありません。

于 2013-08-14T08:22:10.600 に答える