Должны ли быть установлены параметры out, даже если функция COM не работает? - PullRequest
3 голосов
/ 01 апреля 2009

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

HRESULT CDemo::Div(/*[in]*/ LONG a, /*[in]*/LONG b, /*[out,retval]*/ LONG* pRet)
{
    if (pRet == NULL)
        return E_POINTER;

    if (b == 0)
    {
        *pRet = 0; // is this redundant?
        return E_INVALIDARG;
    }

    *pRet = a/b;
    return S_OK;
}

Когда-то я был немного утомлен, не инициализируя выходной параметр и предполагая, что если я инициализирую переменную, она останется тем же значением, если я не изменю его внутри метода. Однако я использовал этот метод из .NET, и поскольку маршаллер видит, что это параметр [out], он отбрасывает первоначальное значение, которое я поместил на сайт вызова, и помещает в мусор после возврата функции (это было забавно отлаживать, но не) ,

Является ли присвоение параметру out даже при сверхкомпенсации отказа или мне действительно нужно это сделать?


Редактировать: Несмотря на то, что формально не следует обращаться к параметрам в случае сбоя функции, я часто вижу (и иногда пишу) код, подобный этому (используя пример из сообщения sharptooth ):

ISmth *pSmth = NULL; 
pObj->GetSmth(&pSmth); // HRES is ignored
if (pSmth) // Assumes that if GetSmth failed then pSmth is still NULL
{ 
    pSmth->Foo();
    pSmth->Release();
}  

Это прекрасно работает в коде без маршалинга (то же самое квартира потока ), но если маршаллер участвует, достаточно ли он умен, чтобы установить возвращаемое значение, только если функция выполнена успешно?

Ответы [ 3 ]

3 голосов
/ 04 мая 2009

Хотя другие ответы не являются неправильными, они упускают очень важный момент - COM-сервер, который намеревается вернуть ошибку HRESULT MUST , задает для всех параметров [out] значение NULL. Это не просто вопрос хорошего стиля, это требуется COM, и несоблюдение этого правила может привести к случайным сбоям при использовании маршалинга.

Тем не менее, * pRet = 0; в исходном коде не является избыточным, но правильным и обязательным.

3 голосов
/ 01 апреля 2009

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

Например, если у вас есть

HRESULT GetSmth( [out] ISmth** );

метод, то ожидается, что сервер вызывает AddRef() для переменной ISmth** до возврата. Он не должен вызывать AddRef(), если он собирается вернуть код ошибки, поскольку клиенту не разрешено использовать возвращенное значение параметра out, и поэтому он не будет вызывать Release(), и вы получите утечку памяти.

1 голос
/ 01 апреля 2009

Я не уверен, что на 100% согласен с sharptooth. Я, безусловно, согласен с тем, что при неудачном вызове COM вы не можете и не должны назначать владение ресурсами каким-либо выходным параметрам. Это включает выделение памяти или AddRef'ing COM-объект.

Однако я не вижу ничего плохого (и на самом деле обнадеживающего) в том, что установка пустых значений только для параметров, поскольку это не передает владение ресурсами. Например, нет ничего технически незаконного в том, что ваш код устанавливает pRet в значение 0. Это не переносит владение ресурсами на pRet и является просто помощником для некоторого вызывающего абонента, который должным образом не проверил успешность вызова.

...