Программирование для Windows NT

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


Вначале наше приложение создает динамический пул памяти, вызывая для этого функцию HeapCreate. Для пула резервируется 2 Кбайта памяти, причем для непосредственного использования выделяется только 1 Кбайт.

При возникновении ошибки ее код определяется с помощью функции GetLastError и отображается в консольном окне хорошо знакомой вам из MS-DOS функцией fprintf. Затем работа приложения завершается.

Заметим, что многие (но не все) функции программного интерфейса Microsoft Windows NT в случае возникновения ошибки перед возвращением управления устанавливают код ошибки, вызывая для этого функцию SetLastError. При необходимости приложение может извлечь этот код сразу после вызова функции, как это показано в нашем приложении.

Далее приложение пытается получить блок памяти размером 0x1500 байт, вызывая функцию HeapAlloc:

__try

{

  lpszBuff = (char*)HeapAlloc(hHeap,

    HEAP_GENERATE_EXCEPTIONS, 0x1500);

}

Так как во втором параметре мы передали этой функции значение HEAP_GENERATE_EXCEPTIONS, в случае ошибки возникнет исключение. Поэтому вызов функции HeapAlloc выполняется с обработкой исключений. Соответствующий обработчик получает код исключения при помощи функции GetExceptionCode и отображает его в консольном окне.

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

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

__try



{

  strcpy(lpszBuff, "Строка для проверки");

}

Так как при получении блока памяти произошло исключение, в указателе lpszBuff находится неправильный адрес. Это, в свою очередь, приведет к возникновению исключения при попытке записи строки. Поэтому на рис. 1.19 в верхней части консольного окна находятся два сообщения об исключениях.

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

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

__try

{

  HeapReAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS |

    HEAP_REALLOC_IN_PLACE_ONLY, lpszBuff, 150);

}

Так как указан флаг HEAP_REALLOC_IN_PLACE_ONLY, при изменении размера блок не будет перемещен, поэтому мы игнорируем значение, возвращаемое функцией HeapReAlloc.

А что произойдет, если размер блока увеличится настолько, что он не поместится в адресном пространстве, отведенном для него ранее?

Мы указали флаг HEAP_GENERATE_EXCEPTIONS, поэтому в этом случае произойдет исключение, которое наше приложение обработает.

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



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