Какой код ошибки возвращать, когда переданный объект не реализует необходимый интерфейс? - PullRequest
1 голос
/ 15 января 2010

В COM, когда у меня есть известный интерфейс, который я не могу изменить:

interface IWellKnownInterface {
     HRESULT DoStuff( IUnknown* );
};

и моя реализация IWellKnownInterface :: DoStuff () может работать, только когда переданный объект реализует какой-то определенный интерфейс, как мне справиться с этой ситуацией?

HRESULT CWellKnownInterfaceImpl::DoStuff( IUnknown* param )
{
    //this will QI for the specific interface        
    ATL::CComQIPtr<ISpecificInterface> object( param );
    if( object == 0 )
        //clearly the specifil interface is not supported
        return E_INVALIDARG;
    }
    // proceed with implementation
}

Если конкретный интерфейс не поддерживается, какой код ошибки я должен вернуть? Соответствует ли возврат E_INVALIDARG?

Ответы [ 4 ]

2 голосов
/ 15 января 2010

E_INVALIDARG - хороший выбор, но самое главное, чтобы точные условия для каждого кода возврата, который вы используете, были хорошо документированы.

Кроме того, вы можете рассмотреть возможность реализации ISupportErrorInfo и затем вернуть расширенную информацию об ошибках через CreateErrorInfo и SetErrorInfo , Это особенно полезно в тех случаях, когда, по вашему мнению, для вызывающих абонентов может быть полезно создание настраиваемого сообщения об ошибке в точке сбоя со всем соответствующим контекстом, содержащимся в нем. В вашем случае это может быть связано с определением того, какой аргумент является недопустимым, и какой интерфейс был не реализован, чтобы это было так. Хотя такое сообщение вряд ли будет иметь ценность для конечного пользователя, оно может оказаться неоценимым для разработчика, если оно будет отображаться в файле журнала или в средстве просмотра событий.

1 голос
/ 21 января 2010

Вот еще одна возможность, которая вам может понравиться или не понравиться.

В Visual Basic 6 есть много объектов с методами, которые принимают аргумент VARIANT и ищут несколько альтернативных интерфейсов, которые они могут использовать (например, член DataSource элементов управления с привязкой к данным). Это очень похоже на вашу ситуацию. Эти объекты возвращают DISP_E_TYPEMISMATCH, что является понятным для большинства людей возвращаемым значением.

С другой стороны, я полностью понимаю, если вы не хотите возвращать код ошибки DISP_xxxx из интерфейсов, не основанных на IDISPATCH. Есть веские аргументы в пользу того, что DISP_xxxx коды ошибок предназначены только для VARIANT параметров, и метод, который принимает IUnknown*, не должен возвращать ошибку.

Я нахожусь на заборе на этом.

1 голос
/ 19 января 2010

Прошу отличаться от @ nobugz . E_NOINTERFACE имеет очень конкретное значение, что ваш объект не поддерживает определенный интерфейс, который только что запросил ваш клиент.

В общем случае вы должны возвращать E_NOINTERFACE только из своих IUnknown::QueryInterface(). Возвращение E_NOINTERFACE из DoStuff() удивит как конечных пользователей, так и инструменты. Если бы я увидел, что E_NOINTERFACE приходит откуда-то, кроме QueryInterface(), моей непосредственной мыслью будет «утечка абстракции!».

На первый взгляд, E_INVALIDARG выглядит как лучшие из доступных вариантов - в конце концов, вам был передан аргумент, который не работает для вас. Но, на мой взгляд, здесь есть одна тонкость, поэтому я не уверен, смогу ли я сделать из этого универсальное правило.

В идеальном мире коды ошибок, возвращаемые методом COM, больше относятся к интерфейсу, чем к объекту. Конкретно, IWellKnownInterface::DoStuff() не определяет (обязательно), что передаваемый объект должен реализовывать ISpecificInterface, иначе у него будет другой тип аргумента. Таким образом, технически вы обманываете потенциальных пользователей вашего объекта, не полностью реализуя семантику вашего интерфейса. Конечно, в реальном мире очень сложно создать значимую открытую систему, если вы превратите это в абсолютное правило, а интерфейсы будут такими жесткими.

Итак: если вы можете предоставить ограниченную функциональность, которая по-прежнему соответствует семантике интерфейса, даже если ваш аргумент не реализует ISpecificInterface, вам следует подумать об этом.

Предполагая, что вы должны вернуться с ошибкой: если это хорошо известный, хорошо документированный ( хорошо спроектированный? ) интерфейс, я, вероятно, сначала загляну в документацию по интерфейсу, чтобы увидеть, дают ли они вам любое руководство. Не имея этого, я, вероятно, пошел бы и использовал E_INVALIDARG.

@ Phil Booth дает несколько хороших рекомендаций о том, как предоставить пользователю больше информации о природе ошибки.

Очевидно, что если бы у вас была возможность, вы бы хотели изменить интерфейс, чтобы вместо него взять ISpecificInterface* param.

1 голос
/ 15 января 2010

Да. По крайней мере, это то, чему я научился ожидать возврата вызова API от Microsoft в таком случае.

...