Визуальное программирование и MFC

       

Куча Windows и семейство функций GlobalAlloc


Куча (heap) — это пул памяти какого-либо процесса. Когда программе требуется блок памяти, онавызывает функцию, выделяющую память из кучи, а чтобы освободить ранее выделенную память, — функцию, парную первой. Выравнивание по границам, кратным 4 Кб, в этом случае не производится; диспетчер кучи использует пространство на выделенных ранее страницах или обращается к VirtualAlloc, чтобы получить дополнительные страницы. Одна из двух куч, с которыми программа работает, — куча Windows. Для выделения из нее памяти служит функция HeapAlloc, а для освобождения — функция HeapFree. HeapAlloc особенно удобна для выделения "крупных" блоков памяти.

Может быть, приложение и не будет явно вызывать НеарАlloc, но за него это сделает функция GlobalAlloc, унаследованная от Winl6. В идеальном мире 32-разрядных программ функция GlobalAlloc не понадобилась бы, но мы имеем дело с реальным миром. Все еще остается колоссальный объем кода, перенесенного из Winl6, в том числе OLE, в котором вместо 32-разрядных адресов применяются параметры типа "описатель памяти" (HGLOBAL).

Работа функции GlobalAlloc зависит от передаваемых ей атрибутов. Если ей указывается GMEM_FIXED, она просто вызывает НеарАlloс и возвращает адрес, приводя тип данных к 32-битному значению HGLOBAL. Если же ей передается GMEM_MOVEABLE, возвращаемое значение HGLOBAL является указателем на элемент "таблицы описателей" в данном процессе. В этом элементе содержится указатель на память, выделенную функцией HeapAlloc.

Зачем нужна "перемещаемая" (moveable) память, если она вводит еще один слой "непрямого управления"? Здесь мы встречаемся с наследием Winl6, в котором операционная система когда-то действительно перемещала блоки памяти. В Win32 перемещаемые блоки памяти существуют лишь для поддержки GlobalReAlloc, которая выделяет новый блок памяти, копирует в него содержимое старого блока, освобождает последний и помещает адрес нового блока в существующий элемент таблицы описателей.


К сожалению, многие библиотечные функции используют в качестве параметров и возвращаемых значений HGLOBAL, а не адреса памяти. Если такая функция возвращает HGLOBAL, приложение должно считать, что данная память выделена с атрибутом GMEM_MOVEABLE, и, следовательно, чтобы получить адрес памяти, надо вызвать функцию GlobalLock. (В случае фиксированной памяти GlobalLock просто возвращает переданный ей описатель как адрес.) Если от приложения требуется передать параметр HGLOBAL, то следует получить это значение с помощью GlobalAlloc(GMEM_MOVEABLE,...) — на тот случай, если вызываемая функция обращается к GlobalReAlloc и ожидает, что значение описателя не изменится.

Буфер обмена Windows использует фиксированную память, тем самым можно передавать адреса, возвращенные HeapAlloc, и приводить возвращаемые значения HGLOBAL к void-указателям. Можно также передавать фиксированный адрес OLE-функции, которая принимает параметр типа HGLOBAL, однако значения HGLOBAL, возвращаемые OLE-функциями, являются перемещаемыми, поэтому приложение должны вызывать GlobalLock.


Содержание раздела