Сущность технологии СОМ. Библиотека программиста

ОглавлениеДобавить в закладки К обложке

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

HRESULT GetFromPound([out] DOG *pDog);

В то время как память для объекта DOG должна быть выделена вызывающей программой (pDog является указателем высшего уровня), память для объекта HUMAN должна быть выделена реализацией метода с использованием распределителя памяти задачи (pDog->pOwner является вложенным в [out]-параметр указателем). Реализация метода выглядела бы примерно так:

STDMETHODIMP GetFromPound(/*[out]*/DOG *pDog)

{

short did = LookupNewDogId();

short hid = LookupHumanId(did);

pDog->nDogID = did;

// allocate memory for embedded pointer

// выделяем память для вложенного указателя

pDog->pOwner = (HUMAN*) CoTaskMemAlloc(sizeof(HUMAN));

if (pDog->pOwner == 0)

// not enough memory

// недостаточно памяти

return R_OUTOFMEMORY;

pDog->pOwner->nHumanID = hid;

return S_OK;

}

Отметим, что метод возвращает специальный HRESULT E_OUTOFMEMORY, указывающий на то, что операция прервана из-за нехватки памяти.

Программа, вызывающая метод GetFromPound, ответственна за освобождение любой памяти, выделенной вызываемым методом, после использования соответствующих значений:

DOG fido;

HRESULT hr = p->GetFromPound(&fido);

if (SUCCEEDED(hr)) {

printf(«The dog %h is owned by %h», fido.nDogID, fido.pOwner->nHumanID);

// data has been consumed, so free the memory

// данные использованы, поэтому освобождаем память

CoTaskMemFree(fido.pOwner);

}

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

В только что приведенном примере использован чистый [out]-параметр. Управление [in, out]– параметрами несколько более сложно. Вложенные указатели для [in, out]-параметров должны быть размещены вызывающей программой с помощью распределителя памяти задачи. Если методу требуется повторно распределить память, переданную клиентом, то метод должен сделать это с использованием CoTaskMemRealloc. Если же вызывающая программа не имеет никакой информации для передачи методу, то она может передать ему на входе нулевой указатель, и тогда метод может использовать CoTaskMemRealloc (который без проблем принимает нулевой указатель и делает то, что нужно). Подобным же образом, если у метода нет информации для обратной передачи в вызывающую программу, он может просто освободить память, на которую ссылается вложенный указатель. Рассмотрим следующее определение метода IDL:

HRESULT SendToVet([in, out] DOG *pDog);

Пусть у вызывающей программы имеется легальное значение HUMAN, которое она хочет передать как параметр. Тогда клиентский код может выглядеть примерно так:

HUMAN *pHuman = (HUMAN*)CoTaskMemAllocc(sizeof(HUMAN));

pHuman->nHumanID = 1522;

DOG fido = { 4111, pHuman };

HRESULT hr = p->SendToVet(&fido); // [in, out]

if (SUCCEEDED(hr)) {

if (fido.pOwner)

printf(«Dog is now owned by %h», fido.pOwner->nHumanID);

CoTaskMemFree(fido.pOwner);

// OK to free null ptr.

// можно освободить нулевой указатель

}

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

STDMETHODIMP MyClass::SendToVet(/*[in, out]*/DOG *pDog)

{

if (fido.pOwner == 0)

fido.pOwner = (HUMAN*)CoTaskMemAlloc(sizeof (HUMAN));

if (fido.pOwner == 0)

// alloc failed

// сбой выделения памяти

return E_OUTOFMEMORY;

fido.pOwner->nHumanID = 22;

return S_OK;

}

Поскольку работа с [in,out]-параметрами в качестве вложенных указателей имеет ряд тонкостей, в документации на интерфейс часто повторяются правила управления памятью для вложенных указателей.

Приведенные выше фрагменты кода используют наиболее удобный интерфейс для СОМ-распределителя памяти задач. До появления версии СОМ под Windows NT основная связь с распределителем памяти задачи осуществлялась через его интерфейс IMallос:

[ uuid(00000002-0000-0000-C000-000000000046),local,object]

interface IMalloc : IUnknown {

void *Alloc([in] ULONG cb);

void *Realloc ([in, unique] void *pv, [in] ULONG cb);

void Free([in, unique] void *pv);


Логин
Пароль
Запомнить меня