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

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

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

int DidAlloc([in, unique] void *pv);

void HeapMinimize(void);

}

Для получения доступа к интерфейсу IMalloc распределителя памяти задачи в СОМ имеется API-функция CoGetMalloc:

HRESULT CoGetMalloc(

[in] DWORD dwMemCtx, // reserved, must be one

// зарезервировано, должно равняться единице

[out] IMalloc **ppMalloc); // put it here!

// помещаем его здесь!

Это означает, что вместо вызова удобного метода CoTaskMemAlloc:

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

можно использовать следующую менее удобную форму:

IMalloc *pMalloc = 0;

pHuman = 0;

HRESULT hr = CoGetMalloc(1, &pMalloc);

if (SUCCEEDED(hr)) {

pHuman = (HUMAN*)pMalloc->Alloc(sizeof(HUMAN));

pMalloc->Release();

}

Преимущество последней технологии заключается в том, что она совместима с ранними, до Windows NT, версиями СОМ. Но в целом предпочтительнее использовать CoTaskMemAlloc и другие, поскольку эти методы требуют меньше программного кода и поэтому меньше подвержены ошибкам программирования.

До сих пор обсуждение распределителя памяти задачи было сфокусировано на вопросах, как и когда объекты выделяют память, а клиенты – освобождают ее. Однако не обсуждалось, что происходит, когда объект и клиент размещаются в различных адресных пространствах. Это во многом связано с отсутствием различия в способах реализации клиентов и объектов при использовании интерфейсных маршалеров. СОМ-распределитель памяти задачи получает свою память из закрытого адресного пространства процессов. С учетом этого сокрытие того обстоятельства, что распределитель памяти задачи не может охватить оба адресных пространства, является делом интерфейсной заглушки и интерфейсного заместителя. Когда интерфейсная заглушка вызывает метод объекта, она маршалирует любые [out]– или [in, out]-параметры в ответное ORPC-сообщение. Как показано на рис. 7.1, по завершении этого маршалинга интерфейсная заглушка (которая в конечном счете является внутриапартаментным клиентом данного объекта) освобождает с помощью метода CoTaskMemFree любую память, выделенную вызываемой программой. Это эффективно освобождает всю память, выделенную в течение вызова метода внутри адресного пространства объекта. При получении ответного ORPC-сообщения интерфейсный заместитель с помощью метода CoTaskMemAlloc выделяет пространство для всех параметров, размещаемых в вызываемой программе.

Когда эти блоки памяти освобождаются настоящим клиентом с помощью CoTaskMemFree, это эффективно освобождает всю память, выделенную в результате вызова метода, внутри адресного пространства клиента.

Поскольку программисты печально известны своим пренебрежением к освобождению памяти, иногда бывает полезно следить за активностью распределителя памяти задачи в процессе (или отсутствием таковой активности). Для обеспечения этого контроля СОМ предлагает подключить к распределителю памяти задачи определяемый пользователем шпионский объект (spy object), который будет уведомляться до и после каждого вызова распределителя памяти. Этот шпионский объект, определяемый пользователем, должен реализовать интерфейс IMallocSpy:

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

interface IMallocSpy : IUnknown {

ULONG PreAlloc([in] ULONG cbRequest);

void *PostAlloc([in] void *pActual);

void *PreFree([in] void *pRequest,[in] BOOL fSpyed);

void PostFree([in] BOOL fSpyed);

ULONG PreRealloc([in] void *pRequest,[in] ULONG cbRequest,

[out] void **ppNewRequest,[in] BOOL fSpyed);

void *PostRealloc([in] void *pActual, [in] BOOL fSpyed);

void *PreGetSize([in] void *pRequest, [in] BOOL fSpyed);

ULONG PostGetSize([in] ULONG cbActual,[in] BOOL fSpyed);

void *PreDidAlloc([in] void *pRequest, [in] BOOL fSpyed);

int PostDidAlloc([in] void *pRequest, [in] BOOL fSpyed, [in] int fActual);

void PreHeapMinimize(void);

void PostHeapMinimize(void);

}

Отметим, что для каждого метода IMalloc интерфейс IMallocSpy имеет два метода: один, вызываемый СОМ до того, как действующий распределитель памяти задачи начнет свою работу, и второй, вызываемый СОМ после того, как распределитель памяти выполнил свою работу. В каждом «предметоде» (premethod) предусмотренный пользователем шпионский объект может изменять параметры, передаваемые пользователем распределителю памяти. В каждом «постметоде» (postmethod) шпионский объект может изменять результаты, возвращаемые действующим распределителем памяти задачи. Это дает возможность шпионскому объекту выделять дополнительную память, чтобы добавить к каждому блоку памяти отладочную информацию. В СОМ имеется API-функция для регистрации шпиона распределения памяти (Malloc spy) всего процесса:


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