2013-08-13 8 views
5

Wyjątki C++ nie mogą przekraczać granic modułu COM.C++ potencjalnie rzucający kod na granicy metody COM

Więc załóżmy jesteśmy w ciele metody COM, a niektóre C++ potencjalnie rzucających metoda/funkcja nazywa się (może to rzucić, ponieważ wykorzystywane są zajęcia np STL):

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

Q1. Czy powyższy kod jest wykonalną wersją? Na przykład, jeśli ten kod jest częścią rozszerzenia powłoki menu kontekstowego, jeśli funkcja C++ CppDoSomething() zgłasza wyjątek C++, co robi Eksplorator? Czy przechwytuje wyjątek C++ i rozładowuje rozszerzenie powłoki? Czy to po prostu awarię Eksploratora (umożliwia analizę problemu za pomocą zrzutu awaryjnego) po fail-szybko podejście fail?

Q2. Czy taka implementacja byłaby lepsza?

Q3. A może byłoby nawet lepiej, gdyby został zgłoszony wyjątek C++, aby ustawić flagę wewnętrzną (np. Element danych bool m_invalid), aby serwer COM był w stanie, który nie będzie już działał, a więc każde kolejne wywołanie do jego metoda zwraca kod błędu, np. E_FAIL lub inny konkretny błąd?

Q4. Na koniec, zakładając, że Q2/Q3 są dobrymi wytycznymi implementacyjnymi, możliwe jest ukrycie pełnej osłony prekomputerowej try/catch (która może być ponownie użyta w każdym korpusie metody COM), np.

#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 
} 

Stosując nowoczesne C++ 11/14, możliwe jest zastąpienie wspomnianych makr preprocesora z czegoś innego, wygodniejsze, bardziej elegancki, po prostu lepiej?

+3

* Nigdy * nie rzucaj wyjątków C++ (lub prawie żadnego innego typu) przez granicę COM. Do tego właśnie służy HRESULT. Jeśli chcesz więcej informacji, weź pod uwagę paradygmat 'IErrorInfo' i' ISupportsErrorInfo'. Jeśli chodzi o makra zawarte w twoim ostatnim fragmencie, to decyzja o wyborze z Twojej strony, ale w ten czy inny sposób * nie wyrzucaj członka COM *. – WhozCraig

+0

@WhozCraig: Jeśli wyjątek C++ (np. 'Std :: bad_alloc',' std :: invalid_argument', 'std :: runtime_error', ...) jest wyrzucany z "core" kodu C++ i jest przechwytywany w bloku 'try/catch' w ciele metody COM, czy umieściłbyś serwer COM w specjalnym _" stanie niepoprawnym "_ i sprawiłby, że następna metoda nie zadziała, czy po prostu zamapowałbyś wyjątek na błąd 'HRESULT' i kontynuowałeś obsługę swoich klientów? –

+0

Nasilenie złapanego wyjątku można ocenić tylko w kontekście. Niezależnie od tego, czy serwer COM będzie stanowił nieodwracalny tryb awaryjny, czy nie, należy wybrać dla każdego wywołania. Podczas korzystania z wyjątków C++ często pomocne jest zastosowanie * silnej gwarancji *, tzn. Operacja albo kończy się, albo wycofuje wszystkie przejściowe modyfikacje stanu w przypadku niepowodzenia. – IInspectable

Odpowiedz

2

Nigdy nie dopuszczaj do tego, aby wyjątki rozprzestrzeniały się na granicy COM, w przeciwnym razie zachowanie jest niezdefiniowane i może obejmować wywoływanie Twojego środowiska wykonawczego C++ terminate(), proces będzie się kręcić i inne miłe bonusy. Po prostu tego nie rób. Nawet jeśli "przetestujesz" to w pewnej konfiguracji - nadal będzie to niezdefiniowane zachowanie i będzie cicho łamać się w przypadku niewielkich zmian w środowisku lub implementacji.

Powinieneś wychwycić i przetłumaczyć wszystkie wyjątki C++ do HRESULT s i opcjonalnie ustawić IErrorInfo ze szczegółami. Możesz to zrobić za pomocą makr, owijając każdą implementację metody serwera COM lub kopiując ten kod wszędzie - zgadnij, który jest łatwiejszy w utrzymaniu.

Pomysł, który doprowadził serwer do stanu "nieprawidłowego", może mieć sens w niektórych ekstremalnych sytuacjach, ale nie mogę sobie tego teraz wyobrazić. Myślę, że to nie jest uniwersalne rozwiązanie. W ogólnych przypadkach, jeśli masz wyjątkowy bezpieczny kod, nie powinieneś tego wcale potrzebować.

+0

Tutaj jest interesująca lektura: [** Jak wyłączyć obsługę wyjątków, którą COM "pomaga" owija się wokół twojego serwera **] (http://blogs.msdn.com/b/oldnewthing/archive/2011/01/ 20/10117963.aspx); Wydaje się, że jest to podejście typu "szybki atak". –

+0

@ Mr.C64: Nie, to kolejna rzecz, to dotyczy tylko strukturalizowanych wyjątków, a nie wyjątków C++ - ten drugi spowoduje tylko wywołanie 'terminate()'. – sharptooth

+0

Nie jestem pewien, czy masz rację ... Zastanów się: _ "Z drugiej strony wyjątek" niekrytyczny "jest czymś w rodzaju wyjątku C++ lub wyjątku CLR. Prawdopodobnie potrzebujesz nieobsługiwanego wyjątku C++ lub CLR, aby zawiesić serwer też, to przecież spowodowałoby awarię twojego programu, gdyby nie działało jako serwer, dlatego moją osobistą rekomendacją jest używanie COMGLB_EXCEPTION_DONOT_HANDLE_ANY, gdy tylko jest to możliwe. "_ –

Powiązane problemy