Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Microsoft Windows 3.1 для программиста

© Александр Фролов, Григорий Фролов
Том 13, М.: Диалог-МИФИ, 1993, 284 стр.

[Назад] [Содеожание] [Дальше]

2.3. Работа с памятью в приложениях Windows

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

Глобальная динамическая память

В Windows версии 3.1 область глобальной памяти общая для всех приложений Windows. Теоретически одно приложение может заказать для себя блок памяти из глобальной области и передать его идентификатор другому приложению, однако такая практика не приветствуется, так как в следующих версиях Windows адресные пространства приложений могут быть разделены (для передачи данных между приложениями необходимо использовать механизм динамической передачи данных DDE, который будет описан в одном из следующих томов "Библиотеки системного программиста").

Получение глобального блока памяти

Для получения глобального блока памяти вы должны использовать функцию GlobalAlloc :

HGLOBAL WINAPI GlobalAlloc(UINT fuAlloc, DWORD cbAlloc);

Параметр fuAlloc определяет тип выделяемой памяти. Размер блока памяти в байтах должен передаваться через параметр cbAlloc, причем вы можете заказать блок памяти размером больше, чем 64 Кбайт. Для стандартного режима работы Windows можно заказать блок памяти размером до 1 Мбайт без 80 байт, для расширенного - до 16 Мбайт без 64 Кбайт.

Функция возвращает идентификатор глобального блока памяти или NULL, если Windows не может выделить память указанного объема.

Параметра fuAlloc должен быть указан как логическая комбинация следующих значений:

Флаг Описание
GMEM_DDESHARE Блок памяти будет использоваться совместно несколькими приложениями при помощи механизма динамического обмена данными DDE
GMEM_DISCARDABLE Заказывается удаляемый блок памяти. Этот флаг должен использоваться совместно с флагом GMEM_MOVEABLE
GMEM_FIXED Заказывается фиксированный блок памяти. Этот флаг несовместим с флагом GMEM_MOVEABLE.При работе в среде Windows версии 3.1 в защищенном режиме фиксированный сегмент, созданный с использованием флага GMEM_FIXED, является перемещаемым. Для такого сегмента в процессе перемещения логический адрес не изменяется, но линейный (и, следовательно, физический) может изменяться
GMEM_LOWER Синоним для GMEM_NOT_BANKED. Не используется в Windows версии 3.1
GMEM_MOVEABLE Заказывается перемещаемый блок памяти. Логический адрес перемещаемого блока памяти может изменяться. Этот флаг несовместим с флагом GMEM_FIXED
GMEM_NOCOMPACT Для удовлетворения запроса памяти не следует выполнять объединение всех свободных участков памяти в один и удалять блоки памяти, отмеченные как удаляемые
GMEM_NODISCARD Для удовлетворения запроса памяти не следует выполнять объединение всех свободных участков памяти в один
GMEM_NOT_BANKED Получить блок памяти вне фрейма дополнительной памяти EMS. Не используется в Windows версии 3.1
GMEM_NOTIFY Если заказанный объект будет удален, требуется вызов процедуры извещения. Процедура извещения назначается функцией GlobalNotify и должна располагаться в фиксированном сегменте кода в библиотеке DLL. С ее помощью приложение может разрешить или запретить Windows удалять блок данных
GMEM_SHARE Синоним для GMEM_DDESHARE
GMEM_ZEROINIT Во все байты блока необходимо записать нулевые значения
GHDN Синоним для комбинации флагов GMEM_MOVEABLE и GMEM_ZEROINIT
GPTR Синоним для комбинации флагов GMEM_FIXED и GMEM_ZEROINIT

Идентификатор, полученный от функции GlobalAlloc, нельзя использовать для адресации памяти непосредственно. Напомним, что пока вы не зафиксировали блок памяти, его логический адрес недоступен.

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

hmemGlobal = GlobalAlloc(GHND, 200000l);

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

hmemGlobalDisc = GlobalAlloc(
  GMEM_MOVEABLE | GMEM_DISCARDABLE, 200000l);

Фиксирование и расфиксирование блока памяти

Для получения доступа к полученному блоку памяти его необходимо зафиксировать, вызвав функцию GlobalLock :

void FAR* WINAPI GlobalLock(HGLOBAL hglb);

Функция GlobalLock фиксирует блок памяти, идентификатор которого передается ей через параметр hglb и возвращает логический адрес зафиксированного блока или NULL, если указанный блок удален или произошла ошибка.

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

Для каждого блока памяти Windows поддерживает счетчик фиксирований. Этот счетчик увеличивается при вызове функции GlobalLock и уменьшается при расфиксировании блока функцией GlobalUnlock :

BOOL WINAPI GlobalUnlock(HGLOBAL hglb);

Если содержимое счетчика уменьшилось до нуля, функция возвращает значение FALSE. В противном случае возвращается TRUE.

Файл windowsx.h содержит макрокоманды, облегчающие работу с глобальными блоками памяти. Например, макрокоманда GlobalAllocPtr получает блок памяти и фиксирует его:

#define GlobalAllocPtr(flags, cb) \
  (GlobalLock(GlobalAlloc((flags), (cb))))

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

Определение идентификатора блока памяти по его адресу

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

DWORD WINAPI GlobalHandle(UINT uGlobalSel);

Параметр uGlobalSel указывает селекторную компоненту логического адреса блока памяти.

Младшее слово возвращаемого значения содержит идентификатор блока памяти, старшее - селектор блока памяти. В случае ошибки возвращается нулевое значение.

В файле windowsx.h определена макрокоманда GlobalPtrHandle , упрощающая получение идентификатора блока памяти по его логическому адресу:

#define GlobalPtrHandle(lp) \
  ((HGLOBAL)LOWORD(GlobalHandle(SELECTOROF(lp))))

Макрокоманда SELECTOROF определена в файле windows.h и предназначена для получения селекторной компоненты логического адреса:

#define SELECTOROF(lp) HIWORD(lp)

В файле windows.h есть также определения для макрокоманды OFFSETOF , возвращающей компоненту смещения, и макрокоманда MAKELP , конструирующая указатель из компонент смещения и селектора:

#define OFFSETOF(lp)   LOWORD(lp)
#define MAKELP(sel, off) ((void FAR*)MAKELONG((off), (sel)))
#define MAKELONG (low, high) ((LONG)(((WORD)(low)) | \
  (((DWORD)((WORD)(high))) << 16)))

Если вы работаете с транслятором Borland C++ for Windows, вместо этих макрокоманд можете использовать знакомые вам макрокоманды FP_SEG , FP_OFF и MK_FP , описанные в файле dos.h:

#define FP_SEG(fp)((unsigned)(void _seg*)(void far*)(fp))
#define FP_OFF(fp)((unsigned)(fp))
#define MK_FP(seg,ofs)((void _seg*)(seg)+(void near*)(ofs))

Работа с удаляемыми блоками памяти

Для того чтобы заказать удаляемый блок памяти, вы должны указать флаги GMEM_DISCARDABLE и GMEM_MOVEABLE , например:

hmemGlDiscard =
  GlobalAlloc(GMEM_MOVEABLE | GMEM_DISCARDABLE, 200000l);

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

При попытке зафиксировать удаленный блок с помощью функции GlobalLock приложение получит от этой функции значение NULL. В этом случае следует вызвать функцию GlobalFlags , предназначенную для определения состояния блока памяти, и проверить, находится ли данный блок в удаленном состоянии.

Приведем прототип функции GlobalFlags:

UINT WINAPI GlobalFlags(HGLOBAL hglb);

Функция возвращает состояние блока памяти, указанного своим единственным параметром. Младший байт возвращаемого значения содержит содержимое счетчика фиксаций блока памяти. В старшем байте могут быть установлены флаги GMEM_DISCARDABLE и GMEM_DISCARDED.

Если установлен флаг GMEM_DISCARDABLE, проверяемый блок памяти может быть удален Windows в процессе дефрагментации свободной области памяти. Если же установлен флаг GMEM_DISCARDED, удаление блока памяти уже произошло.

Для получения доступа к удаленному блоку памяти его необходимо восстановить, вызвав функцию GlobalReAlloc. Эта функция позволяет изменить характеристики существующего блока памяти.

Приведем прототип функции GlobalReAlloc :

HGLOBAL WINAPI GlobalReAlloc(HGLOBAL hglb, 
  DWORD cbNewSize, UINT fuAlloc);

Параметр hglb указывает идентификатор восстанавливаемого блока памяти.

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

Параметр fuAlloc определяет тип восстановленного блока памяти. Можно указывать логическую комбинацию следующих флагов:

Флаг Описание
GMEM_DISCARDABLE Если блок памяти был перемещаемый, то теперь дополнительно он будет и удаляемый. Этот флаг должен использоваться совместно с флагом GMEM_MODIFY
GMEM_MODIFY Выполняется изменение характеристик существующего блока памяти. этот флаг необходимо указывать вместе с флагами GMEM_DISCARDABLE и GMEM_MOVEABLE
GMEM_MOVEABLE Если раньше указанный блок памяти был перемещаемым и удаляемым, содержимое счетчика фиксирования для него равно нулю и новый размер блока указан равным нулю, данный блок будет удален. Если же параметр cbNewSize равен нулю, но блок памяти не является перемещаемым или удаляемым, функция вернет признак ошибки.Для фиксированного блока памяти ненулевой длины данный флаг разрешает перемещение, т. е. преобразует фиксированный блок в перемещаемый.
GMEM_NODISCARD Блок памяти не может быть удален операционной системой. Этот флаг должен использоваться совместно с флагом GMEM_MODIFY
GMEM_ZEROINIT Если размер блока памяти увеличивается, во все байты дополнительной памяти необходимо записать нулевые значения. Этот флаг несовместим с флагом GMEM_MODIFY

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

Изменение блока памяти

Как мы уже говорили, функция GlobalReAlloc , описанная выше, позволяет изменить характеристики глобального блока памяти.

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

hmemGlobal = GlobalReAlloc(hmemGlobal, 51200,
 GMEM_MODIFY | GMEM_DISCARDABLE | GMEM_MOVEABLE |
 GMEM_ZEROINIT);

После вызова функции блок памяти будет перемещаемый и удаляемый, причем если раньше его размер был меньше 51200 байт, во все байты дополнительной памяти будут записаны нулевые значения.

Учтите, что при увеличении размера блока может возникнуть ситуация нехватки памяти, поэтому проверяйте значение идентификатора, возвращаемой функцией GlobalReAlloc, на неравенство константе NULL.

Вы можете инициировать удаление блока памяти при помощи функции GlobalReAlloc, если укажите нулевой размер блока и флаг GMEM_MOVEABLE. В файле windows.h имеется определение макрокоманды GlobalDiscard, при помощи которой приложение может принудительно удалить блок из памяти:

#define GlobalDiscard(h) GlobalReAlloc(h, 0L, GMEM_MOVEABLE)

Определение размера блока памяти

С помощью функции GlobalSize вы можете определить размер блока памяти по его идентификатору:

DWORD WINAPI GlobalSize(HGLOBAL hglb);

Эта функция возвращает размер блока памяти, идентификатор которого задан параметром hglb. Если указанный блок памяти не существует или удален, возвращается нулевое значение.

Дефрагментация памяти

Если вызвать функцию GlobalCompact , операционная система Windows выполнит объединение всех свободных блоков в один. В качестве параметра этой функции необходимо указать требуемый размер непрерывного блока свободной памяти:

DWORD WINAPI GlobalCompact(DWORD dwMinFree);

Функция возвращает размер самого большого доступного непрерывного блока памяти, причем, если параметр на равен 0 или -1, выполняется дефрагментация памяти и удаление блоков, отмеченных как удаляемые. Если параметр функции указан как 0 или -1, функция не выполняет дефрагментацию памяти, но возвращает правильное значение с учетом возможного выполнения дефрагментации.

Получение памяти в первом мегабайте адресного пространства

Если у вас возникает необходимость заказать память, доступную приложениям MS-DOS, и располагающуюся в первом мегабайте адресного пространства, воспользуйтесь функцией GlobalDosAlloc :

DWORD WINAPI GlobalDosAlloc(DWORD cbAlloc);

Параметр cbAlloc определяет размер блока в байтах.

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

Младшее слово возвращаемого значения содержит селектор для доступа к полученному блоку в защищенном режиме.

Память, заказанная при помощи функции GlobalDOSAlloc, является фиксированной, поэтому вам не следует вызывать функцию GlobalLock для фиксирования и получения логического адреса.

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

UINT WINAPI GlobalDosFree(UINT uSelector);

В качестве параметра uSelector этой функции следует передать селекторную компоненту блока памяти, расположенного в первом мегабайте адресного пространства.

В случае успешного завершения возвращаемое значение равно нулю. При ошибке возвращается селектор, указанный в параметре uSelector.

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

Освобождение глобального блока памяти

Для освобождения глобального блока памяти, полученного от функции GlobalAlloc, вы должны использовать функцию GlobalFree :

HGLOBAL WINAPI GlobalFree(HGLOBAL hglb);

Идентификатор освобождаемого блока передается функции в качестве ее единственного параметра.

Функция возвращает NULL при успешном завершении или значение hglb при ошибке.

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

Для освобождения памяти, полученной при помощи макрокоманды GlobalAllocPtr, удобно использовать макрокоманду GlobalFreePtr , описанную в файле windowsx.h:

#define GlobalFreePtr(lp) \
  (GlobalUnlockPtr(lp),(BOOL)GlobalFree(GlobalPtrHandle(lp)))

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

Фиксирование линейного адреса блока памяти

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

Если драйвер какого-либо устройства ввода/вывода работает с линейным адресом буфера, память, отведенная для такого буфера в некоторых случаях должна быть зафиксирована функцией GlobalFix :

void WINAPI GlobalFix(HGLOBAL hglb);

Параметр функции указывает идентификатор фиксируемого блока памяти.

Как только отпадет необходимость в фиксировании блока памяти, его следует расфиксировать, вызвав функцию GlobalUnfix :

void WINAPI GlobalUnfix(HGLOBAL hglb);

Единственный параметр этой функции должен содержать идентификатор блока памяти, который будет расфиксирован.

Фиксирование страниц блока памяти

В расширенном режиме работы операционной системы Windows версии 3.1 используется виртуальная память. Как мы уже говорили, при использовании виртуальной памяти вся глобальная область памяти делится на страницы размером 4 Кбайт. Эти страницы могут располагаться в физической оперативной памяти или на диске в специальном файле виртуальной памяти. Если приложение обращается к странице, которая отсутствует в физической оперативной памяти, она загружается туда из файла виртуальной памяти.

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

Фиксирование блока памяти функцией GlobalFix не предотвращает сброс страниц памяти, распределенных блоку, в файл виртуальной памяти, а всего лишь запрещает перемещение блока памяти в линейном адресном пространстве.

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

UINT WINAPI GlobalPageLock(HGLOBAL hglb);

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

Операционная система Windows поддерживает счетчик блокирования страничного обмена. Содержимое этого счетчика увеличивается на единицу при каждом вызове функции GlobalPageLock.

Функция GlobalPageLock возвращает новое значение счетчика или ноль при ошибке.

Как только надобность в блокировке страничного обмена отпадает, следует вызвать функцию GlobalPageUnlock :

UINT WINAPI GlobalPageUnlock(HGLOBAL hglb);

Эта функция разрешает страничный обмен для блока памяти, заданного параметром hglb. Функция возвращает текущее значение счетчика или ноль при ошибке.

Приложение GMEM

Для демонстрации использования основных функций управления глобальной памятью мы приведем исходный текст приложения GMEM (листинг 2.3).


Листинг 2.3. Файл gmem/gmem.cpp


#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <dos.h>

#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
   HINSTANCE hPrevInstance,
   LPSTR     lpszCmdLine, int nCmdShow)
{
  BYTE    szBuf[100];
  HGLOBAL hmemGlobal;
  HGLOBAL hmemGlDiscard;
  LPVOID  lpvoidGlobal;
  LPVOID  lpvoidGlDiscard;
  DWORD   dwMaxFreeMem;

  // Определяем размер доступной памяти
  dwMaxFreeMem = GlobalCompact(-1l);

  wsprintf(szBuf, "Доступно памяти:\t%lu\n",
        dwMaxFreeMem);
  MessageBox(NULL, (LPSTR)szBuf, "Global Block", MB_OK);

  // --------------------------------------------------------
  // Работаем с перемещаемым блоком памяти
  // --------------------------------------------------------

  // Дефрагментируем память для получения блока
  // размером 100000 байт
  dwMaxFreeMem = GlobalCompact(100000l);

  // Заказываем буфер размером 100000 байт
  hmemGlobal = GlobalAlloc(GHND, 100000l);

  if(hmemGlobal != NULL)
  {
    // Если буфер получен, фиксируем его в памяти
    lpvoidGlobal = GlobalLock(hmemGlobal);
    if(lpvoidGlobal != (LPVOID) NULL)
    {
      // Если блок успешно зафиксирован,
      // выводим значения идентификатора блока
      // и логический адрес блока
      wsprintf(szBuf, "hmemGlobal=\t%04.4X\n"
        "lpvoidGlobal=\t%04.4X:%04.4X",
        hmemGlobal,
        FP_SEG(lpvoidGlobal), FP_OFF(lpvoidGlobal));

      MessageBox(NULL, (LPSTR)szBuf, "Global Block", MB_OK);

      // -----------------------------------------
      // Можно работать с полученным блоком памяти
      // Записываем в первый байт блока символ S
      *(LPSTR)lpvoidGlobal = 'S';
      // -----------------------------------------

      // Разрешаем перемещение блока
      GlobalUnlock(hmemGlobal);
    }
    else
    {
      MessageBox(NULL, "Ошибка при фиксировании блока",
        "Global Block", MB_OK);
    }
    // Отдаем блок памяти операционной системе
    GlobalFree(hmemGlobal);
  }
  else
  {
    MessageBox(NULL, "Мало памяти для перемещаемого блока",
      "Global Block", MB_OK);
  }

  // --------------------------------------------------------
  // Работаем с удаляемым блоком памяти
  // --------------------------------------------------------

  // Заказываем удаляемый блок памяти размером 200000 байт
  hmemGlDiscard =
      GlobalAlloc(GMEM_MOVEABLE | GMEM_DISCARDABLE, 200000l);

  if(hmemGlDiscard != NULL)
  {
    // Если мы его получили, удаляем блок
    GlobalDiscard(hmemGlDiscard);

    // Пытаемся зафиксировать блок памяти
    lpvoidGlDiscard = GlobalLock(hmemGlDiscard);

    if(lpvoidGlDiscard != (LPVOID) NULL)
    {
      // Если удалось (чего не должно быть, так как
      // мы только что удалили блок), выводим
      // идентификатор блока и логический адрес
      wsprintf(szBuf, "hmemGlDiscard=\t%04.4X\n"
        "lpvoidGlDiscard=\t%04.4X:%04.4X",
        hmemGlDiscard,
        FP_SEG(lpvoidGlDiscard), FP_OFF(lpvoidGlDiscard));
      MessageBox(NULL, (LPSTR)szBuf, "Global Block", MB_OK);

      // Разрешаем перемещение блока
      GlobalUnlock(hmemGlDiscard);
    }
    else
    {
      // Если блок памяти не удалось зафиксировать,
      // проверяем, не был ли он удален
      if(GlobalFlags(hmemGlDiscard) & GMEM_DISCARDED)
      {
         MessageBox(NULL, 
           "Блок удален и мы его восстанавливаем",
           "Global Block", MB_OK);

         // Восстанавливаем удаленный блок памяти
         hmemGlDiscard = 
           GlobalReAlloc(hmemGlDiscard, 200000l,
           GMEM_MOVEABLE | GMEM_DISCARDABLE);

         // Фиксируем блок памяти
         lpvoidGlDiscard = GlobalLock(hmemGlDiscard);

         if(lpvoidGlDiscard != (LPVOID) NULL)
         {
           // Выводим идентификатор и логический адрес
           // зафиксированного блока памяти
           wsprintf(szBuf, "hmemGlDiscard=\t%04.4X\n"
             "lpvoidGlDiscard=\t%04.4X:%04.4X",
             hmemGlDiscard,
             FP_SEG(lpvoidGlDiscard),
             FP_OFF(lpvoidGlDiscard));
           MessageBox(NULL, (LPSTR)szBuf,
             "Global Block", MB_OK);

           // Освобождаем блок памяти
           GlobalUnlock(hmemGlDiscard);
         }
         else
         {
           MessageBox(NULL, "Ошибка при фиксировании блока",
           "Global Block", MB_OK);
         }
      }
    }

    // Отдаем удаляемый блок памяти операционной системе
    GlobalFree(hmemGlDiscard);
  }
  else
  {
    MessageBox(NULL, "Мало памяти для удаляемого блока",
      "Global Block", MB_OK);
  }

  return 0;
}

Перед началом работы приложение определяет объем свободной памяти, вызывая функцию GlobalCompact со значением параметра, равным -1. Определенное значение выводится на экран при помощи функции MessageBox.

Далее приложение вызывает функцию GlobalCompact еще раз для освобождения непрерывного блока свободной памяти размером 100000 байт.

После этого приложение заказывает буфер размером 100000 байт, вызывая функцию GlobalAlloc. В качестве первого параметра этой функции указана константа GHND, соответствующее перемещаемой памяти, инициализированной нулевым значением.

В случае успешного получения блока памяти он фиксируется и на экран выводится значение идентификатора блока и его логический адрес. После этого в первый байт полученного блока записывается код символа 'S' и блок расфиксируется. При ошибке выдается сообщение.

Далее полученный блок освобождается и возвращается операционной системе, для чего вызывается функция GlobalFree.

После этого приложение заказывает удаляемый блок памяти размером 200000 байт и сразу же удаляет его, вызывая макрокоманду GlobalDiscard. Затем приложение предпринимает попытку зафиксировать только что удаленный блок памяти, вызывая функцию GlobalLock. Если блок памяти удалился успешно, функция GlobalLock должна вернуть значение NULL. В этом случае нам надо убедиться в том, что блок был удален, и если это так и есть, восстановить удаленный блок.

Для проверки приложение использует функцию GlobalFlags. Если блок, идентификатор которого был передан этой функции в качестве параметра, удален, в возвращаемом значении установлен флаг GMEM_DISCARDED.

Для восстановления удаленного блока приложение вызывает функцию GlobalReAlloc, указывая размер и характеристики блока. Затем восстановленный блок фиксируется в памяти и на экран выводится его идентификатор и логический адрес.

Перед завершением работы приложения блок расфиксируется и освобождается.

Файл определения модуля приложения GMEM приведен в листинге 2.4.


Листинг 2.4. Файл gmem/gmem.def


; =============================
; Файл определения модуля
; =============================
NAME        GMEM
DESCRIPTION 'Приложение GMEM, (C) 1994, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
CODE        preload moveable discardable
DATA        preload moveable multiple

Локальная динамическая память

Для каждого приложения Windows создается автоматический сегмент данных размером 64 Кбайт, в котором располагаются статические данные , стек и локальная область данных (local heap ). Кроме этого, автоматический сегмент данных имеет заголовок размером 16 байт (рис. 2.10).

Рис. 2.10. Автоматический сегмент данных приложения Windows

Размер стека определяется оператором STACKSIZE в файле определения модуля:

STACKSIZE   8120

Минимальный размер стека, назначаемый Windows для приложений, составляет 5 Кбайт. Следует отметить, что в руководстве к SDK нет точного описания способа определения минимально необходимого объема стека. В этом руководстве предлагается определить этот объем экспериментально, причем подчеркивается, что результаты переполнения стека непредсказуемы.

В отличие от стека, размер локальной области данных может при необходимости увеличиваться автоматически. Начальное значение задается в файле определения модуля при помощи оператора HEAPSIZE :

HEAPSIZE    1024

Вы можете указать любое отличное от нуля значение.

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

Получение локального блока памяти

Для получения локального блока памяти вы должны использовать функцию LocalAlloc :

HLOCAL WINAPI LocalAlloc(UINT fuAlloc, UINT cbAlloc);

Параметр fuAlloc определяет тип выделяемой памяти. Размер блока памяти в байтах должен передаваться через параметр cbAlloc.

Функция возвращает идентификатор локального блока памяти или NULL, если Windows не может выделить память указанного объема.

Параметра fuAlloc должен быть указан как логическая комбинация следующих значений:

Флаг Описание
LMEM_DISCARDABLE Заказывается удаляемый блок памяти. Этот флаг должен использоваться совместно с флагом LMEM_MOVEABLE
LMEM_FIXED Заказывается фиксированный блок памяти (в защищенном режиме работы блок памяти будет перемещаемым, даже если он заказан с использованием флага LMEM_FIXED, однако в процессе перемещения будет изменяться только линейный адрес, но не логический). Этот флаг несовместим с флагом LMEM_MOVEABLE
LMEM_MOVEABLE Заказывается перемещаемый блок памяти. Логический адрес такого блока может изменяться в процессе перемещения. Этот флаг несовместим с флагом LMEM_FIXED
LMEM_NOCOMPACT Для удовлетворения запроса памяти не следует выполнять объединение всех свободных участков памяти в один и удалять блоки памяти, отмеченные как удаляемые
LMEM_NODISCARD Для удовлетворения запроса памяти не следует выполнять объединение всех свободных участков памяти в один
LMEM_ZEROINIT Во все байты блока необходимо записать нулевые значения
NONZEROLHND Синоним для LMEM_MOVEABLE
NONZEROLPTR Синоним для LMEM_FIXED
LHDN Синоним для комбинации флагов LMEM_MOVEABLE и LMEM_ZEROINIT
LPTR Синоним для комбинации флагов LMEM_FIXED и LMEM_ZEROINIT

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

hmemLocal = LocalAlloc(LHND, 2000);

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

hmemLocalDisc = LocalAlloc(
  LMEM_MOVEABLE | LMEM_DISCARDABLE, 200);

Фиксирование и расфиксирование блока памяти

Для получения доступа к полученному блоку памяти его необходимо зафиксировать, вызвав функцию LocalLock :

void NEAR* WINAPI LocalLock(HLOCAL hloc);

Функция LocalLock фиксирует блок памяти, идентификатор которого передается ей через параметр hloc и возвращает логический адрес зафиксированного блока или NULL, если указанный блок удален или произошла ошибка.

Так как операционная система Windows версии 3.1 работает только в защищенном режиме, все блоки памяти, зафиксированные при помощи функции LocalLock, будут перемещаемыми. Перемещение блоков памяти выполняется методом изменения базового адреса в локальной таблице дескрипторов, причем логический адрес перемещаемого блока остается неизменным. Поэтому перемещение фиксированных блоков памяти происходит для приложений незаметно.

Для каждого блока памяти Windows поддерживает счетчик фиксирования. Этот счетчик увеличивается при вызове функции LocalLock и уменьшается при расфиксировании блока функцией LocalUnlock :

BOOL WINAPI LocalUnlock(HLOCAL hloc);

Если содержимое счетчика уменьшилось до нуля, функция возвращает значение FALSE. В противном случае возвращается TRUE.

Определение идентификатора блока памяти по его адресу

С помощью функции LocalHandle вы можете определить идентификатор локального блока памяти:

HLOCAL WINAPI LocalHandle(void NEAR* pvMem);

Параметр pvMem указывает адрес локального блока памяти.

Возвращаемое значение содержит идентификатор локального блока памяти. В случае ошибки возвращается нулевое значение.

Работа с удаляемыми блоками памяти

Для получения доступа к удаленному локальному блоку памяти его необходимо восстановить, вызвав функцию LocalReAlloc. Эта функция аналогична функции GlobalReAlloc и позволяет изменить характеристики существующего блока памяти.

Приведем прототип функции LocalReAlloc :

HLOCAL WINAPI LocalReAlloc(HLOCAL hloc, 
  UINT cbNewSize, UINT fuAlloc);

Параметр hloc указывает идентификатор восстанавливаемого блока памяти.

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

Параметр fuAlloc определяет тип восстановленного блок памяти. Можно указывать логическую комбинацию следующих флагов:

Флаг Описание
LMEM_DISCARDABLE Если блок памяти был перемещаемый, то теперь дополнительно он будет и удаляемый. Этот флаг должен использоваться совместно с флагом LMEM_MODIFY
LMEM_MODIFY Выполняется изменение характеристик существующего блока памяти. этот флаг необходимо указывать вместе с флагами LMEM_DISCARDABLE и LMEM_MOVEABLE
LMEM_MOVEABLE Если раньше указанный блок памяти был перемещаемым и удаляемым, содержимое счетчика фиксирования для него равно нулю и новый размер блока указан равным нулю, данный блок будет удален. Если же параметр cbNewSize равен нулю, но блок памяти не является перемещаемым или удаляемым, функция вернет признак ошибки.Для фиксированного блока памяти ненулевой длины данный флаг разрешает перемещение, т. е. преобразует фиксированный блок в перемещаемый.
LMEM_NODISCARD Блок памяти не может быть удален операционной системой. Этот флаг должен использоваться совместно с флагом LMEM_MODIFY
LMEM_ZEROINIT Если размер блока памяти увеличивается, во все байты дополнительной памяти необходимо записать нулевые значения. Этот флаг несовместим с флагом LMEM_MODIFY

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

Определение характеристик локального блока памяти

Для определения характеристик локального блока памяти предназначена функция LocalFlags , аналогичная рассмотренной нами ранее функции GlobalFlags:

UINT WINAPI LocalFlags(HLOCAL hloc);

Функция возвращает состояние блока памяти, указанного своим единственным параметром. Младший байт возвращаемого значения содержит содержимое счетчика фиксаций блока памяти. В старшем байте могут быть установлены флаги LMEM_DISCARDABLE и LMEM_DISCARDED.

Если установлен флаг LMEM_DISCARDABLE, проверяемый блок памяти может быть удален Windows в процессе дефрагментации свободной области памяти. Если же установлен флаг LMEM_DISCARDED, удаление блока памяти уже произошло.

Определение размера блока памяти

С помощью функции LocalSize вы можете определить размер блока памяти по его идентификатору:

UINT WINAPI LocalSize(HLOCAL hloc);

Эта функция возвращает размер блока памяти, идентификатор которого задан параметром hloc. Если указанный блок памяти не существует или удален, возвращается нулевое значение.

Дефрагментация локального блока памяти

Функция LocalCompact выполняет дефрагментацию свободного пространства в локальной области данных:

UINT WINAPI LocalCompact(UINT uMinFree);

Функция возвращает размер самого большого доступного непрерывного блока в локальной области памяти, причем, если параметр на равен 0 или -1, выполняется дефрагментация памяти и удаление блоков, отмеченных как удаляемые. Если параметр функции указан как 0, функция не выполняет дефрагментацию памяти, но возвращает правильное значение с учетом возможного выполнения дефрагментации.

Уменьшение размера локального блока памяти

Для уменьшения размера существующего локального блока памяти можно использовать функцию LocalShrink :

UINT WINAPI LocalShrink(HLOCAL hloc, UINT cbNewSize);

Параметр hloc указывает идентификатор изменяемого локального блока памяти. Новые размеры блока памяти задаются параметром cbNewSize.

Возвращаемое значение в случае успеха равно новому размеру блока памяти.

В процессе получения памяти при помощи функции LocalAlloc размер использованной локальной памяти будет расти, пока не достигнет 64 Кбайт. После освобождения локальных блоков данных размер локальной области не уменьшится сам по себе. Для восстановления этого размера вы должны вызвать функцию LocalShrink.

Освобождение локального блока памяти

Для освобождения локального блока памяти, полученного от функции LocalAlloc, вы должны использовать функцию LocalFree :

HLOCAL WINAPI LocalFree(HLOCAL hloc);

Идентификатор освобождаемого блока передается функции в качестве ее единственного параметра.

Функция возвращает NULL при успешном завершении или значение hloc при ошибке.

Перед освобождением зафиксированных блоков памяти их следует предварительно расфиксировать.

Инициализация локальной области данных в заданном сегменте

Для создания и инициализации локальной области данных в заданном сегменте вы можете воспользоваться функцией LocalInit :

BOOL WINAPI LocalInit(UINT uSegment, 
  UINT uStartAddr, UINT uEndAddr);

Параметр uSegment перед вызовом функции должен содержать идентификатор сегмента, который будет содержать локальную область данных.

Параметр uStartAddr определяет начальный адрес локальной области данных, а параметр uEndAddr - конечный адрес локальной области данных.

Первые 16 байт в сегменте данных необходимо зарезервировать для системы.

Приложение LMEM

Приведем исходный текст приложения LMEM, которое работает аналогично приложению GMEM, но с использованием локальной области памяти (листинг 2.5).


Листинг 2.5. Файл lmem/lmem.cpp


#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <dos.h>

#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
   HINSTANCE hPrevInstance,
   LPSTR     lpszCmdLine, int nCmdShow)
{
  BYTE szBuf[100];
  HLOCAL hmemLocal;
  HLOCAL hmemLoDiscard;
  void*  pLocal;
  void*  pLoDiscard;
  UINT   uMaxFreeMem;

  // Определяем размер доступной памяти
  uMaxFreeMem = LocalCompact(0);

  wsprintf(szBuf, "Доступно памяти:\t%u\n",
        uMaxFreeMem);
  MessageBox(NULL, (LPSTR)szBuf, "Local Block", MB_OK);

  // --------------------------------------------------------
  // Работаем с перемещаемым блоком памяти
  // --------------------------------------------------------

  // Дефрагментируем память для получения блока
  // размером 1000 байт
  uMaxFreeMem = LocalCompact(1000);

  // Заказываем буфер размером 1000 байт
  hmemLocal = LocalAlloc(GHND, 1000);

  if(hmemLocal != NULL)
  {
    // Если буфер получен, фиксируем его в памяти
    pLocal = LocalLock(hmemLocal);
    if(pLocal != NULL)
    {
      // Если блок успешно зафиксирован,
      // выводим значения идентификатора блока
      // и логический адрес блока
      wsprintf(szBuf, "hmemLocal=\t%04.4X\n"
        "pLocal=\t\t%04.4X",
        hmemLocal, pLocal);

      MessageBox(NULL, (LPSTR)szBuf, "Local Block", MB_OK);

      // -----------------------------------------
      // Можно работать с полученным блоком памяти
      // Записываем в первый байт блока символ S
      *(PSTR)pLocal = 'S';
      // -----------------------------------------

      // Разрешаем перемещение блока
      LocalUnlock(hmemLocal);
    }
    else
    {
      MessageBox(NULL, "Ошибка при фиксировании блока",
        "Local Block", MB_OK);
    }
    // Отдаем блок памяти операционной системе
    LocalFree(hmemLocal);
  }
  else
  {
    MessageBox(NULL, "Мало памяти для перемещаемого блока",
      "Local Block", MB_OK);
  }

  // --------------------------------------------------------
  // Работаем с удаляемым блоком памяти
  // --------------------------------------------------------

  // Заказываем удаляемый блок памяти размером 2000 байт
  hmemLoDiscard =
      LocalAlloc(LMEM_MOVEABLE | LMEM_DISCARDABLE, 2000);

  if(hmemLoDiscard != NULL)
  {
    // Если мы его получили, удаляем блок
    LocalDiscard(hmemLoDiscard);

    // Пытаемся зафиксировать блок памяти
    pLoDiscard = LocalLock(hmemLoDiscard);

    if(pLoDiscard != NULL)
    {
      wsprintf(szBuf, "hmemLoDiscard=\t%04.4X\n"
        "pLoDiscard=\t%04.4X",
        hmemLoDiscard, pLoDiscard);
      MessageBox(NULL, (LPSTR)szBuf, "Local Block", MB_OK);

      // Разрешаем перемещение блока
      LocalUnlock(hmemLoDiscard);
    }
    else
    {
      // Если блок памяти не удалось зафиксировать,
      // проверяем, не был ли он удален
      if(LocalFlags(hmemLoDiscard) & LMEM_DISCARDED)
      {
         MessageBox(NULL, 
           "Блок удален и мы его восстанавливаем",
           "Local Block", MB_OK);

         // Восстанавливаем удаленный блок памяти
         hmemLoDiscard = LocalReAlloc(hmemLoDiscard, 256,
           LMEM_MOVEABLE | LMEM_DISCARDABLE);

         // Фиксируем блок памяти
         pLoDiscard = LocalLock(hmemLoDiscard);

         if(pLoDiscard != NULL)
         {
           // Выводим идентификатор и логический адрес
           // зафиксированного блока памяти
           wsprintf(szBuf, "hmemLoDiscard=\t%04.4X\n"
             "pLoDiscard=\t%04.4X",
             hmemLoDiscard, pLoDiscard);
           MessageBox(NULL, (LPSTR)szBuf, 
             "Local Block", MB_OK);

           // Освобождаем блок памяти
           LocalUnlock(hmemLoDiscard);
         }
         else
         {
           MessageBox(NULL, "Ошибка при фиксировании блока",
           "Local Block", MB_OK);
         }
      }
    }

    // Отдаем удаляемый блок памяти операционной системе
    LocalFree(hmemLoDiscard);
  }
  else
  {
    MessageBox(NULL, "Мало памяти для удаляемого блока",
      "Local Block", MB_OK);
  }

  return 0;
}

Файл определения модуля приложения LMEM приведен в листинге 2.6.


Листинг 2.6. Файл lmem/lmem.def


; =============================
; Файл определения модуля
; =============================
NAME        LMEM
DESCRIPTION 'Приложение LMEM, (C) 1994, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
STACKSIZE   8120
HEAPSIZE    4096
CODE        preload moveable discardable
DATA        preload moveable multiple

В этом файле начальное значение локальной области данных установлено равным 4096 байт. Проверяя работу приложения LMEM, вы можете попробовать уменьшить размер локальной области данных, например, до величины 1 Кбайт, а затем заказать локальный блок памяти размером 10 Кбайт. В этом случае несмотря на то, что сразу после запуска приложения в локальной области данных будет свободно всего несколько сотен байт, запрос на 10 Кбайт будет удовлетворен за счет автоматического увеличения размера локальной области данных.

Статическая память

Статические данные, описанные в приложении Windows с использованием ключевого слова static или объявленные как внешние переменные располагаются в автоматическом сегменте данных приложения (рис. 2.10).

В документации к SDK не рекомендуется в моделях памяти small и medium использовать дальние указатели на статические данные (См. раздел 16.5 руководства, который называется Traps to Avoid When Managing Program Data).

Дело в том, что автоматический сегмент данных приложения является перемещаемым. Операционная система Windows фиксирует сегмент данных при активизации приложения и расфиксирует его во время переключения на другие приложения. Поэтому логический адрес сегмента данных может изменяться.

Следующий способ является недопустимым:

static LPSTR lpstrDlgName = "MyDlg";
........
hDlg = CreateDialog(hInst, 
           lpstrDlgName,
           hWndParent,
           (DLGPROC) lpDialogProc);

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

Рекомендуется в указанной выше ситуации использовать ближний статический указатель с явным преобразованием типа к LPSTR:

static PSTR pstrDlgName = "MyDlg";
........
hDlg = CreateDialog(hInst, 
           (LPSTR)pstrDlgName,
           hWndParent,
           (DLGPROC) lpDialogProc);

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

Автоматическая память

Автоматические данные располагаются, как и статические, в автоматическом сегменте данных (рис. 2.10), назначаемым приложению при его загрузке в память. К автоматическим данным относится стек, параметры функций и локальные переменные.

Как мы уже говорили, размер стека определяется оператором STACKSIZE в файле определения модуля и не увеличивается автоматически во время работы приложения.

Дополнительная память в структуре класса окна

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

Для работы с этой дополнительной памятью предназначены функции SetClassWord, SetClassLong, GetClassWord, GetClassLong.

Функция SetClassWord устанавливает в структуре, описывающей класс для окна hwnd, новое значение wNewWord, при этом смещение устанавливаемого слова определяется параметром offset:

WORD WINAPI SetClassWord(HWND hwnd, int offset,
   WORD wNewWord);

Для параметра offset вы должны использовать значения от нуля до указанного в поле cbClsExtra минус 2 или следующие значения:

Значение Описание
GCW_HBRBACKGROUND Идентификатор кисти для закрашивания фона окна
GCW_HCURSOR Идентификатор курсора
GCW_HICON Идентификатор пиктограммы
GCW_STYLE Стили окна

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

В случае ошибки функция SetClassWord возвращает нулевое значение.

Функция GetClassWord позволяет вам прочитать содержимое слова дополнительной области памяти со смещением offset:

WORD WINAPI GetClassWord(HWND hwnd, int offset);

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

Значение Описание
GCW_CBCSLEXTRA Размер дополнительной области памяти в структуре класса окна
GCW_CBWNDEXTRA Размер дополнительной области памяти в структуре окна
GCW_HBRBACKGROUND Идентификатор кисти для закрашивания фона окна
GCW_HCURSOR Идентификатор курсора
GCW_HICON Идентификатор пиктограммы
GCW_STYLE Стили окна

Функция GetClassWord возвращает значение указанного слова из структуры класса окна или нулевое значение при ошибке.

Функция SetClassLong аналогична функции SetClassWord, но работает с двойными словами:

LONG WINAPI SetClassLong(HWND hwnd, int offset, LONG nVal);

Для параметра offset дополнительно можно указать значение GCL_WNDPROC, при этом функция заменит адрес функции окна. Мы пользовались этим приемом в приложении SMARTPAD, перехватывая управление у стандартной функции окна органа управления класса "edit".

В случае ошибки функция SetClassLong возвращает нулевое значение.

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

LONG WINAPI GetClassLong(HWND hwnd, int offset);

Для этой функции можно указать положительное смещение или одну из двух констант - GCL_WNDPROC и GCL_MENUNAME. В первом случае функция GetClassLong возвратит адрес функции окна для данного класса, во втором - указатель на строку имени меню, указанного при регистрации класса.

Дополнительная память в структуре окна

При регистрации класса окна функцией RegisterClass вы можете в поле cbWndExtra структуры WNDCLASS указать размер дополнительной области памяти, которая будет зарезервирована в структуре, описывающей каждое окно, создаваемое на базе данного класса.

Для работы с этой дополнительной памятью предназначены функции SetWindowWord, SetWindowLong, GetWindowWord, GetWindowLong.

Функция SetWindowWord устанавливает в структуре, описывающей окно hwnd, новое значение wNewWord, при этом смещение устанавливаемого слова определяется параметром offset:

WORD WINAPI SetWindowWord(HWND hwnd, int offset,
   WORD wNewWord);

Для параметра offset вы должны использовать значения от нуля до указанного в поле cbClsExtra минус 2 или следующие значения:

Значение Описание
GWW_HINSTANCE Идентификатор приложения, владеющего данным окном
GWW_ID Идентификатор дочернего окна

В случае ошибки функция SetWindowWord возвращает нулевое значение.

Функция GetWindowWord позволяет вам прочитать содержимое слова дополнительной области памяти в структуре окна со смещением offset:

WORD WINAPI GetWIndowWord(HWND hwnd, int offset);

Для этой функции вы можете использовать следующие значения:

Значение Описание
GWW_HINSTANCE Идентификатор приложения, владеющего данным окном
GWW_HWNDPARENT Идентификатор родительского окна
GWW_ID Идентификатор дочернего окна

Функция GetWindowWord возвращает значение указанного слова из структуры класса окна или нулевое значение при ошибке.

Функция SetWindowLong аналогична функции SetWindowWord, но работает с двойными словами:

LONG WINAPI SetWindowLong(HWND hwnd, int offset, LONG nVal);

Для параметра offset дополнительно можно указать следующие значения:

Значение Описание
GWL_EXSTYLE Расширенный стиль окна
GWL_STYLE Стиль окна
GWL_WNDPROC Указатель на функцию окна

Если параметр hwnd содержит идентификатор диалоговой панели, вы можете использовать еще несколько значений:

Значение Описание
DWL_MSGRESULT Значение, возвращенное при обработке сообщения в функции диалоговой панели
DWL_USER Дополнительная информация, имеющая отношение к приложению, такая как идентификаторы или указатели
DWL_DLGPROC Указатель на функцию диалоговой панели

В случае ошибки функция SetWindowLong возвращает нулевое значение.

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

LONG WINAPI GetWindowLong(HWND hwnd, int offset);

Для этой функции можно указать положительное смещение или одну из констант, описанных выше для функции SetWindowLong .

Ресурсы приложения

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

В файле описания ресурсов вы можете указать, что для хранения ресурсов следует использовать фиксированную, перемещаемую или удаляемую память. Вы можете описать ресурсы, как загружаемые в память при запуске приложения (PRELOAD) или по требованию (LOADONCALL).

Загрузка ресурсов в оперативную память выполняется такими функциями, как LoadIcon или CreateDialog . Для загрузки ресурсов, имеющих нестандартный формат, вы должны использовать функции FindResource (поиск ресурса и получение идентификатора ресурса) и LoadResource (загрузка ресурса и получение идентификатора блока памяти, в который загружен найденный ресурс).

Все эти функции были описаны в предыдущем томе, однако для удобства мы приведем их краткое описание еще раз.

Приведем прототип функции FindResource :

HRSRC WINAPI FindResource(HINSTANCE hInst, 
   LPCSTR lpszName, LPCSTR lpszType);

Параметр hInst является идентификатором модуля, содержащего ресурс. Для извлечения ресурса из приложения вы должны указать его идентификатор, передаваемый функции WinMain через параметр hInstance.

Параметр lpszName должен содержать адрес имени ресурса. Для загрузки произвольных данных в качестве этого параметра следует передать указатель на строку, содержащую идентификатор ресурса.

Функции FindResource в качестве третьего параметра можно передавать идентификаторы предопределенных типов ресурсов, список которых приведен ниже.

Идентификатор ресурса Название ресурса
RT_ACCELERATOR Таблица акселераторов
RT_BITMAP Изображение bitmap
RT_CURSOR Курсор
RT_DIALOG Диалоговая панель
RT_FONT Шрифт
RT_FONTDIR Каталог шрифтов
RT_ICON Пиктограмма
RT_MENU Меню
RT_RCDATA Произвольные данные
RT_STRING Таблица строк

Вы можете использовать функцию FindResource для загрузки таких ресурсов, как пиктограммы или курсоры, указав ей тип ресурса, соответственно, RT_ICON или RT_CURSOR. Однако в документации к SDK сказано, что загрузку предопределенных ресурсов, таких как пиктограммы и курсоры, следует выполнять специально предназначенными для этого функциями (LoadIcon, LoadCursor и т. д.).

После того как ресурс найден, его следует загрузить, вызвав функцию LoadResource :

HGLOBAL WINAPI LoadResource(HINSTANCE hinst, HRSRC hrsrc);

Параметр hinst представляет собой идентификатор модуля, из файла которого загружается ресурс. Если ресурс загружается из файла вашего приложения, используйте значение hInstance, полученное через соответствующий параметр функции WinMain.

В качестве второго параметра этой функции следует передать значение, полученное от функции FindResource.

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

void FAR* WINAPI LockResource(HGLOBAL  hGlb);

В качестве параметра hGlb функции LockResource следует передать идентификатор ресурса, полученный от функции LoadResource.

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

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

BOOL WINAPI GlobalUnlock(HGLOBAL hGlb);
#define UnlockResource(h) GlobalUnlock(h)

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

BOOL WINAPI FreeResource(HGLOBAL hGlb);

В качестве параметра hGlb следует передать идентификатор ресурса, полученный от функции LoadResource.

[Назад] [Содеожание] [Дальше]