2013-08-13 22 views
5

Ngoại lệ C++ không thể vượt qua ranh giới mô-đun COM.C++ mã có khả năng ném tại ranh giới phương thức COM

Vì vậy, giả sử chúng ta đang ở trong một cơ thể phương pháp COM, và một số C++ có khả năng ném phương pháp/hàm được gọi (điều này có thể ném vì các lớp học như STL được sử dụng):

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

Q1. Đoạn mã trên có thực hiện khả thi không? Ví dụ: nếu mã đó là một phần của tiện ích mở rộng trình đơn ngữ cảnh, nếu hàm C++ CppDoSomething() ném ngoại lệ C++, Explorer sẽ làm gì? Nó có bắt được ngoại lệ C++ và dỡ bỏ phần mở rộng của shell không? Có phải nó chỉ gặp sự cố Explorer (làm cho nó có thể phân tích vấn đề bằng cách sử dụng một bãi chứa sự cố) theo cách tiếp cận không không nhanh chóng?

Q2. Việc triển khai như thế có tốt hơn không?

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. Hoặc sẽ tốt hơn, nếu ngoại lệ C++ được ném, chỉ cần đặt cờ nội bộ (ví dụ: thành viên dữ liệu bool m_invalid) để đặt máy chủ COM ở trạng thái sao cho không thể hoạt động nữa, vì vậy mọi cuộc gọi liên tiếp đến phương thức của nó trả về một số mã lỗi, chẳng hạn như E_FAIL hoặc một số lỗi cụ thể khác?

Q4. Cuối cùng, giả sử Q2/Q3 là hướng dẫn triển khai tốt, có thể ẩn bảo vệ chi tiết try/catch trong một số macro tiền xử lý thuận tiện (có thể được sử dụng lại trong mọi phương thức COM), ví dụ:

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

Sử dụng C++ 11/14 hiện đại, có thể thay thế macro tiền xử lý nói trên bằng một thứ khác, thuận tiện hơn, thanh lịch hơn, tốt hơn không?

+3

* Không bao giờ * ném ngoại lệ C++ (hoặc gần bất kỳ loại nào khác) qua ranh giới COM. Đó là những gì mà HRESULT dành cho. Nếu bạn muốn biết thêm thông tin, hãy xem xét mô hình 'IErrorInfo' và' ISupportsErrorInfo'. Về các macro trong đoạn trích cuối cùng của bạn, đó là quyết định thiết kế về phía bạn, nhưng theo cách này hay cách khác, * không được ném ra khỏi thành viên COM *. – WhozCraig

+0

@WhozCraig: Nếu ngoại lệ C++ (ví dụ: 'std :: bad_alloc',' std :: invalid_argument', 'std :: runtime_error', ...) được ném từ mã "C++" cốt lõi và được bắt trong khối 'try/catch' trong thân phương thức COM, bạn sẽ đặt máy chủ COM trong một _" trạng thái không hợp lệ "_ đặc biệt và thực hiện các cuộc gọi phương thức tiếp theo không thành công, hoặc bạn sẽ chỉ vẽ bản đồ ngoại lệ cho một thất bại 'HRESULT' và tiếp tục phục vụ khách hàng của bạn? –

+0

Mức độ nghiêm trọng của ngoại lệ bị bắt chỉ có thể được đánh giá theo ngữ cảnh. Cho dù điều này đặt máy chủ COM vào một chế độ thất bại không thể phục hồi hoặc không phải được quyết định cho mỗi lời gọi. Khi sử dụng ngoại lệ C++, thường hữu ích khi triển khai * bảo đảm mạnh mẽ *, tức là một thao tác chạy hoặc hoàn thành hoặc cuộn lại tất cả các sửa đổi trạng thái tạm thời về lỗi. – IInspectable

Trả lời

2

Không bao giờ để ngoại lệ lan truyền qua ranh giới COM, nếu không hành vi không được xác định và có thể bao gồm thời gian chạy C++ terminate(), quá trình chuyển các loại hạt và các khoản tiền thưởng đẹp khác. Chỉ cần không làm điều đó. Ngay cả khi bạn "thử nghiệm" nó trong một số cấu hình - nó vẫn chưa được xác định hành vi và sẽ âm thầm phá vỡ nên môi trường nhỏ hoặc thay đổi thực hiện xảy ra.

Bạn nên bắt và dịch tất cả ngoại lệ C++ thành HRESULT s và tùy chọn đặt IErrorInfo với chi tiết. Bạn có thể làm như vậy với các macro bao gồm mỗi phương thức thực hiện phương thức COM server hoặc bằng cách sao chép mã này ở mọi nơi - đoán đó là duy trì được nhiều hơn.

Ý tưởng với việc đưa máy chủ vào trạng thái "không hợp lệ" có thể có ý nghĩa trong một số trường hợp cực đoan nhưng tôi không thể tưởng tượng chúng vào lúc này. Tôi đoán nó không phải là một giải pháp phổ quát. Trong trường hợp chung nếu bạn có mã an toàn ngoại lệ, bạn không nên cần điều này chút nào.

+0

Có một cách đọc thú vị ở đây: [** Cách tắt trình xử lý ngoại lệ COM "hữu ích" bao quanh máy chủ của bạn **] (http://blogs.msdn.com/b/oldnewthing/archive/2011/01/ 20/10117963.aspx); có vẻ như một cách tiếp cận _fail fast_. –

+0

@ Mr.C64: Không, đó là một thứ khác, nó chỉ khởi động cho các trường hợp ngoại lệ có cấu trúc, không phải cho các ngoại lệ C++ - cái sau sẽ chỉ gây ra 'terminate()' được gọi. – sharptooth

+0

Tôi không chắc chắn bạn đang phải ... Xem xét: _ "Mặt khác, một ngoại lệ 'nonfatal' là một cái gì đó giống như một ngoại lệ C + + hoặc một ngoại lệ CLR.Bạn có thể muốn có một ngoại lệ C++ hoặc CLR unhandled để sụp đổ máy chủ của bạn , sau khi tất cả, nó sẽ làm hỏng chương trình của bạn nếu nó không chạy như một máy chủ. Vì vậy, khuyến cáo cá nhân của tôi là sử dụng COMGLB_EXCEPTION_DONOT_HANDLE_ANY bất cứ khi nào có thể. "_ –

Các vấn đề liên quan