Допустимо ли начальное значение мусора для параметра `[out] BSTR*`?

Согласно MSDN:

Для параметров [out] метод или свойство выделяют память, а вызывающий объект отвечает за освобождение памяти.

Что из этого нормально:

[...]
STDMETHOD(Method)([out] BSTR* psValue)
[...]

BSTR myBStr1;
Method(&myBStr1);
::SysFreeString(myBStr1);

BSTR myBStr2 = SysAllocString(L"MyStringValue");
Method(&myBStr2);
::SysFreeString(myBStr2);

BSTR myBStr3 = NULL;
Method(&myBStr3);
::SysFreeString(myBStr3);

person Afriza N. Arief    schedule 07.06.2013    source источник


Ответы (2)


Да, мусор допустим. Метод отвечает за инициализацию значения. Вызывающий отвечает только за освобождение.

MSDN в атрибуте out:

Параметр только для [out] считается неопределенным при вызове удаленной процедуры и выделении памяти для объекта сервером.

№1, №3 в порядке. # 2 собирается создать утечку памяти.

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

person Roman R.    schedule 07.06.2013
comment
you might be now sure » не уверен? - person Afriza N. Arief; 07.06.2013

Посмотрите на правило с точки зрения упорядоченного вызова (то есть того, что происходит, когда объект находится в другом подразделении COM или когда он находится в другом процессе или машине); вы поймете, как работает это правило и почему это единственное разумное правило, которое может существовать.

Ключевым фактом является то, что для параметра [out] маршаллер не маршалирует начальное значение вызываемому объекту.

Это означает, что не имеет значения, что вы поместите в переменную перед выполнением вызова. Это может быть мусор или NULL. Никто не собирается на это смотреть.

Примечание. Это также означает, что если перед вызовом переменная содержит что-то, что нужно освободить, вы должны сами освободить это перед вызовом, иначе произойдет утечка. Маршаллер не собирается выпускать его для вас (потому что, будучи параметром [out], маршаллер должен считать его мусором), а вызываемый объект не может освободить его, даже если захочет (потому что он никогда не получит существующее значение от машаллер).< /эм>

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

Ситуация с условиями ошибки немного сложнее.

В принципе, COM требует, чтобы параметр [out] был установлен в согласованное ("marshallable"?) значение перед возвратом (либо NULL, либо его эквивалент, либо допустимое значение), даже при наличии ошибки. Это также «единственное разумное правило»: маршаллер не может знать, что вызываемый объект находится в ситуации ошибки. Он также не будет угадывать на основе HRESULT, потому что даже HRESULT «класса ошибок» не означает, что метод не намеревается возвращать значения вызывающей стороне. Маршаллер собирается передать значение параметра обратно вызывающему объекту (с ошибкой или без ошибки), поэтому лучше, чтобы параметр был действительным, иначе сам маршаллер выйдет из строя.

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

Канонический способ убедиться, что значения параметра остаются согласованными даже в случае ошибки, выглядит примерно так:

STDMETHODIMP Class::Method(BSTR *pValue)
{
  *pValue = NULL;        // 1) Initialize all the [out] parameters before
                         //    doing anything, to make sure they are always
                         //    consistent

  BSTR tempValue;        // 2) Use a temporary to hold any result value

  ...                    // 3) Use the temporary for all computations
  ...                    //    Finish all other processing

  *pValue = tempValue;   // 4) Only after all possible error conditions
                         //    are behind us, do put the new value
                         //    in the output parameter

  return S_OK;
}

Очевидно, что если выходной параметр — это просто значение, которое не нужно освобождать (например, int и т. д.), то все это не имеет значения.

person Euro Micelli    schedule 07.06.2013
comment
@RomanR., конечно - не нужно спрашивать. Спасибо, что поймали это. Это учит меня не публиковать ответы до первого кофе. - person Euro Micelli; 07.06.2013