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

       

Файл discard/discard.cpp


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

// Прототип функции извещения о том, что Windows // планирует удалить блок памяти // Так как функция NotifyProc составлена на языке // программирования C (а не C++), мы описываем ее // как extern "C" extern "C" BOOL CALLBACK _export NotifyProc(HGLOBAL hglbl);

#pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { BYTE szBuf[100]; 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);

// Устанавливаем процедуру извещения, которая // получит управление при попытке удалить // блок памяти GlobalNotify((GNOTIFYPROC)NotifyProc);

// Заказываем удаляемый блок памяти размером 200000 байт // Для включения режима извещения необходимо // указать флаг GMEM_NOTIFY hmemGlDiscard = GlobalAlloc(GMEM_MOVEABLE | GMEM_DISCARDABLE | GMEM_NOTIFY, 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; }

DLL-библиотека, в которой расположена функция извещения, составлена нами с использованием языка программирования C, а не C++. Это сделано для упрощения экспортирования функции, которая в данном случае экспортируется с использованием своего порядкового номера. Подробнее этот вопрос мы рассмотрим при описании исходного текста DLL-библиотеки, а пока заметим, что так как для функции извещения использован язык C, мы должны это отметить в прототипе функции, включаемой в исходный текст приложения, составленного на языке C++:

extern "C" BOOL CALLBACK _export NotifyProc(HGLOBAL hglbl);

После запуска приложения функция WinMain определяет размер доступной памяти, вызывая функцию GlobalCompact, а затем выводит его на экран.

После этого функция WinMain устанавливает процедуру извещения, вызывая функцию GlobalNotify и передавая ей в качестве параметра адрес внешней по отношению к приложению функции извещения NotifyProc, расположенной в DLL-библиотеке:

GlobalNotify((GNOTIFYPROC)NotifyProc);

Каждая копия приложения может вызывать функцию GlobalNotify только один раз.

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


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

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

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

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

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

GlobalDiscard(hmemGlDiscard);

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

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

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


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