- •WINDOWS
- •Джеффри Рихтер
- •ЧАCTЬ I МАТЕРИАЛЫ ДЛЯ ОБЯЗАТЕЛЬНОГО ЧТЕНИЯ
- •ГЛАВА 1. Обработка ошибок
- •Вы тоже можете это сделать
- •Программа-пример ErrorShow
- •ГЛАВА 2 Unicode
- •Наборы символов
- •Одно- и двухбайтовые наборы символов
- •Unicode: набор широких символов
- •Почему Unicode?
- •Windows 2000 и Unicode
- •Windows 98 и Unicode
- •Windows CE и Unicode
- •В чью пользу счет?
- •Unicode и СОМ
- •Как писать программу с использованием Unicode
- •Unicode и библиотека С
- •Типы данных, определенные в Windows для Unicode
- •Unicode- и ANSI-функции в Windows
- •Строковые функции Windows
- •Ресурсы
- •Текстовые файлы
- •Перекодировка строк из Unicode в ANSI и обратно
- •ГЛАВА 3 Объекты ядра
- •Что такое объект ядра
- •Учет пользователей объектов ядра
- •Защита
- •Таблица описателей объектов ядра
- •Создание объекта ядра
- •Закрытие объекта ядра
- •Совместное использование объектов ядра несколькими процессами
- •Наследование описателя объекта
- •Изменение флагов описателя
- •Именованные объекты
- •Пространства имен Terminal Server
- •Дублирование описателей объектов
- •ЧАСТЬ II НАЧИНАЕМ РАБОТАТЬ
- •ГЛАВА 4 Процессы
- •Ваше первое Windows-приложение
- •Описатель экземпляра процесса
- •Описатель предыдущего экземпляра процесса
- •Командная строка процесса
- •Переменные окружения
- •Привязка к процессорам
- •Режим обработки ошибок
- •Текущие диск и каталог для процесса
- •Текущие каталоги для процесса
- •Определение версии системы
- •Функция CreateProcess
- •Параметры pszApplicationName и pszCommandLine
- •Параметры psaProcess, psaThread и blnheritHandles
- •Параметр fdwCreate
- •Параметр pvEnvironment
- •Параметр pszCurDir
- •Параметр psiStartlnfo
- •Параметр ppiProclnfo
- •Завершение процесса
- •Возврат управления входной функцией первичного потока
- •Функция ExitProcess
- •Функция TerminateProcess
- •Когда все потоки процесса уходят
- •Что происходит при завершении процесса
- •Дочерние процессы
- •Запуск обособленных дочерних процессов
- •Перечисление процессов, выполняемых в системе
- •Программа-пример Processlnfo
- •ГЛАВА 5 Задания
- •Определение ограничений, налагаемых на процессы в задании
- •Включение процесса в задание
- •Завершение всех процессов в задании
- •Получение статистической информации о задании
- •Уведомления заданий
- •Программа-пример JobLab
- •ГЛАВА 6 Базовые сведения о потоках
- •В каких случаях потоки создаются
- •И в каких случаях потоки не создаются
- •Ваша первая функция потока
- •Функция CreateThread
- •Параметр psa
- •Параметр cbStack
- •Параметры pfnStartAddr и pvParam
- •Параметр fdwCreate
- •Параметр pdwThreadlD
- •Завершение потока
- •Возврат управления функцией потока
- •Функция ExitThread
- •Функция TerminateThread
- •Если завершается процесс
- •Что происходит при завершении потока
- •Кое-что о внутреннем устройстве потока
- •Некоторые соображения по библиотеке С/С++
- •Ой, вместо _beginthreadex я по ошибке вызвал CreateThread
- •Библиотечные функции, которые лучше не вызывать
- •Как узнать о себе
- •Преобразование псевдоописателя в настоящий описатель
- •ГЛАВА 7 Планирование потоков, приоритет и привязка к процессорам
- •Приостановка и возобновление потоков
- •Приостановка и возобновление процессов
- •Функция Sleep
- •Переключение потоков
- •Определение периодов выполнения потока
- •Структура CONTEXT
- •Приоритеты потоков
- •Абстрагирование приоритетов
- •Программирование приоритетов
- •Динамическое изменение уровня приоритета потока
- •Подстройка планировщика для активного процесса
- •Программа-пример Scheduling Lab
- •Привязка потоков к процессорам
- •ГЛАВА 8 Синхронизация потоков в пользовательском режиме
- •Кэш-линии
- •Более сложные методы синхронизации потоков
- •Худшее, что можно сделать
- •Критические секции
- •Критические секции: важное дополнение
- •Критические секции и спин-блокировка
- •Критические секции и обработка ошибок
- •Несколько полезных приемов
- •Не занимайте критические секции надолго
- •ГЛАВА 9 Синхронизация потоков с использованием объектов ядра
- •Wait-функции
- •Побочные эффекты успешного ожидания
- •События
- •Программа-пример Handshake
- •Ожидаемые таймеры
- •Ожидаемые таймеры и АРС-очередь
- •И еще кое-что о таймерах
- •Семафоры
- •Мьютексы
- •Отказ от объекта-мьютекса
- •Мьютексы и критические секции
- •Программа-пример Queue
- •Сводная таблица объектов, используемых для синхронизации потоков
- •Другие функции, применяемые в синхронизации потоков
- •Асинхронный ввод-вывод на устройствах
- •Функция WaitForlnputldle
- •Функция MsgWaitForMultipleObjects(Ex)
- •Функция WaitForDebugEvent
- •Функция SignalObjectAndWait
- •ГЛАВА 10 Полезные средства для синхронизации потоков
- •Реализация критической секции: объект-оптекс
- •Программа-пример Optex
- •Создание инверсных семафоров и типов данных, безопасных в многопоточной среде
- •Программа-пример lnterlockedType
- •Синхронизация в сценарии "один писатель/группа читателей"
- •Программа-пример SWMRG
- •Реализация функции WaitForMultipleExpressions
- •Программа-пример WaitForMultExp
- •ГЛАВА 11 Пулы потоков
- •Сценарий 1: асинхронный вызов функций
- •Сценарий 2: вызов функций через определенные интервалы времени
- •Программа-пример TimedMsgBox
- •Сценарий 3: вызов функций при освобождении отдельных объектов ядра
- •Сценарий 4; вызов функций по завершении запросов на асинхронный ввод-вывод
- •ГЛАВА 12 Волокна
- •Работа с волокнами
- •Программа-пример Counter
- •ЧАСТЬ III УПРАВЛЕНИЕ ПАМЯТЬЮ
- •Виртуальное адресное пространство процесса
- •Как адресное пространство разбивается на разделы
- •Увеличение раздела для кода и данных пользовательского режима до 3 Гб на процессорах x86 (только Windows 2000)
- •Закрытый раздел размером 64 Кб (только Windows 2000)
- •Раздел для общих MMF (только Windows 98)
- •Регионы в адресном пространстве
- •Передача региону физической памяти
- •Физическая память и страничный файл
- •Физическая память в страничном файле не хранится
- •Атрибуты защиты
- •Защита типа «копирование при записи»
- •Специальные флаги атрибутов защиты
- •Подводя итоги
- •Блоки внутри регионов
- •Особенности адресного пространства в Windows 98
- •Выравнивание данных
- •ГЛАВА 14 Исследование виртуальной памяти
- •Системная информация
- •Программа-пример Syslnfo
- •Статус виртуальной памяти
- •Программа-пример VMStat
- •Определение состояния адресного пространства
- •Функция VMQuery
- •Программа-пример VMMap
- •ГЛАВА 15 Использование виртуальной памяти в приложениях
- •Резервирование региона в адресном пространстве
- •Передача памяти зарезервированному региону
- •Резервирование региона с одновременной передачей физической памяти
- •В какой момент региону передают физическую память
- •Возврат физической памяти и освобождение региона
- •В какой момент физическую память возвращают системе
- •Программа-пример VMAIloc
- •Изменение атрибутов защиты
- •Сброс содержимого физической памяти
- •Программа-пример MemReset
- •Механизм Address Windowing Extensions (только Windows 2000)
- •Программа-пример AWE
- •ГЛАВА 16 Стек потока
- •Стек потока в Windows 98
- •Функция из библиотеки С/С++ для контроля стека
- •Программа-пример Summation
- •ГЛАВА 17 Проецируемые в память файлы
- •Проецирование в память EXE- и DLL-файлов
- •Статические данные не разделяются несколькими экземплярами EXE или DLL
- •Программа-пример Applnst
- •Файлы данных, проецируемые в память
- •Метод 1: один файл, один буфер
- •Метод 2: два файла, один буфер
- •Метод 3: один файл, два буфера
- •Метод 4: один файл и никаких буферов
- •Использование проецируемых в память файлов
- •Этап1: создание или открытие объекта ядра «файл»
- •Этап 2: создание объекта ядра «проекция файла»
- •Этап 3: проецирование файловых данных на адресное пространство процесса
- •Этап 4: отключение файла данных от адресного пространства процесса
- •Этапы 5 и 6: закрытие объектов «проекция файла» и «файл»
- •Программа-пример FileRev
- •Обработка больших файлов
- •Проецируемые файлы и когерентность
- •Базовый адрес файла, проецируемого в память
- •Особенности проецирования файлов на разных платформах
- •Совместный доступ процессов к данным через механизм проецирования
- •Файлы, проецируемые на физическую память из страничного файла
- •Программа-пример MMFShare
- •Частичная передача физической памяти проецируемым файлам
- •Программа-пример MMFSparse
- •ГЛАВА 18 Динамически распределяемая память
- •Стандартная куча процесса
- •Дополнительные кучи в процессе
- •Защита компонентов
- •Более эффективное управление памятью
- •Локальный доступ
- •Исключение издержек, связанных с синхронизацией потоков
- •Быстрое освобождение всей памяти в куче
- •Создание дополнительной кучи
- •Выделение блока памяти из кучи
- •Изменение размера блока
- •Определение размера блока
- •Освобождение блока
- •Уничтожение кучи
- •Использование куч в программах на С++
- •Другие функции управления кучами
- •ЧАСТЬ IV ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫЕ БИБЛИОТЕКИ
- •ГЛАВА 19 DLL: основы
- •DLL и адресное пространство процесса
- •Общая картина
- •Создание DLL-модуля
- •Что такое экспорт
- •Создание DLL для использования с другими средствами разработки (отличными от Visual C++)
- •Создание ЕХЕ-модуля
- •Что такое импорт
- •Выполнение ЕХЕ-модуля
- •ГЛАВА 20 DLL: более сложные методы программирования
- •Явная загрузка DLL и связывание идентификаторов
- •Явная загрузка DLL
- •Явная выгрузка DLL
- •Явное подключение экспортируемого идентификатора
- •Функция входа/выхода
- •Уведомление DLL_PROCESS_ATTACH
- •Уведомление DLL_PROCESS_DETACH
- •Уведомление DLL_THREAD_ATTACH
- •Уведомление DLL_THREAD_DETACH
- •Как система упорядочивает вызовы DIIMain
- •Функция DllMain и библиотека С/С++
- •Отложенная загрузка DLL
- •Программа-пример DelayLoadApp
- •Переадресация вызовов функций
- •Известные DLL
- •Перенаправление DLL
- •Модификация базовых адресов модулей
- •Связывание модулей
- •ГЛАВА 21 Локальная память потока
- •Динамическая локальная память потока
- •Использование динамической TLS
- •Статическая локальная память потока
- •Пример внедрения DLL
- •Внедрение DLL c использованием реестра
- •Внедрение DLL с помощью ловушек
- •Утилита для сохранения позиций элементов на рабочем столе
- •Внедрение DLL с помощью удаленных потоков
- •Программа-пример lnjLib
- •Библиотека lmgWalk.dll
- •Внедрение троянской DLL
- •Внедрение DLL как отладчика
- •Внедрение кода в среде Windows 98 через проецируемый в память файл
- •Внедрение кода через функцию CreateProcess
- •Перехват API-вызовов: пример
- •Перехват API-вызовов подменой кода
- •Перехват API-вызовов с использованием раздела импорта
- •Программа-пример LastMsgBoxlnfo
- •ЧАСТЬ V СТРУКТУРНАЯ ОБРАБОТКА ИСКЛЮЧЕНИЙ
- •ГЛАВА 23 Обработчики завершения
- •Примеры использования обработчиков завершения
- •Funcenstein1
- •Funcenstein2
- •Funcenstein3
- •Funcfurter1
- •Проверьте себя: FuncaDoodleDoo
- •Funcenstein4
- •Funcarama1
- •Funcarama2
- •Funcarama3
- •Funcarama4: последний рубеж
- •И еще о блоке finally
- •Funcfurter2
- •Программа-пример SEHTerm
- •ГЛАВА 24 Фильтры и обработчики исключений
- •Примеры использования фильтров и обработчиков исключений
- •Funcmeister1
- •Funcmeister2
- •EXCEPTION_EXECUTE_HANDLER
- •Некоторые полезные примеры
- •Глобальная раскрутка
- •Остановка глобальной раскрутки
- •EXCEPTION_CONTINUE_EXECUTION
- •Будьте осторожны с EXCEPTION_CONTINUE_EXECUTION
- •EXCEPTION_CONTINUE_SEARCH
- •Функция GetExceptionCode
- •Функция GetExceptionlnformation
- •Программные исключения
- •ГЛАВА 25 Необработанные исключения и исключения С++
- •Отладка по запросу
- •Отключение вывода сообщений об исключении
- •Принудительное завершение процесса
- •Создание оболочки вокруг функции потока
- •Создание оболочки вокруг всех функций потоков
- •Автоматический вызов отладчика
- •Явный вызов функции UnhandledExceptionFilter
- •Функция UnhandledExceptionFilter изнутри
- •Исключения и отладчик
- •Программа-пример Spreadsheet
- •Исключения С++ и структурные исключения
- •Перехват структурных исключений в С++
- •ЧАСТЬ VI ОПЕРАЦИИ С ОКНАМИ
- •ГЛАВА 26 Оконные сообщения
- •Очередь сообщений потока
- •Посылка асинхронных сообщений в очередь потока
- •Посылка синхронных сообщений окну
- •Пробуждение потока
- •Флаги состояния очереди
- •Алгоритм выборки сообщений из очереди потока
- •Пробуждение потока с использованием объектов ядра или флагов состояния очереди
- •Передача данных через сообщения
- •Программа-пример CopyData
- •ГЛАВА 27 Модель аппаратного ввода и локальное состояние ввода
- •Поток необработанного ввода
- •Локальное состояние ввода
- •Ввод с клавиатуры и фокус
- •Управление курсором мыши
- •Подключение к очередям виртуального ввода и переменным локального состояния ввода
- •Программа-пример LISLab
- •Программа-пример LISWatch
элемента поменялось на 2 143 289 344 (2 Гб за вычетом 4 Мб). Разница в 4 Мб возникает из-за того, что Windows 98 блокирует нижний раздел от 0x00000000 до 0x003FFFFF (размером в 4 Мб).
И, наконец, dwAvailVirtual (показываемый как AvailVirtual) — единственный элемент структуры, специфичный для конкретного процесса, вызывающего GlobalMemoryStatus (остальные элементы относятся исключительно к самой системе и не зависят от того, какой именно процесс вызывает эту функцию). При подсчете значения dwAvaiWirtual функция суммирует размеры вссх свободных регионов в адресном пространстве вы зывающего процесса. В данном случае его значение говорит о том, что в распоряже нии программы VMStat имеется 2 136 846 336 байтов свободного адресного простран ства. Вычтя из значения dwTotalVirtual величину dwAvailVirtual, Вы получите 10 506 240 байтов
— такой объем памяти VMStat зарезервировала в своем виртуальном адресном
пространстве Отдельного элемента, который сообщал бы количество физической памяти, используемой процессом в данный момент, не предусмотрено
Определение состояния адресного пространства
В Windows имеется функция, позволяющая запрашивать определенную информацию об участке памяти по заданному адресу (в пределах адресного пространства вызыва ющего процесса): размер, тип памяти и атрибуты защиты. В частности, с ее помощью программа VMMap (ее листинг см. на рис. 14-4) выводит карты виртуальной памяти, с которыми мы познакомились в главе 13Вот эта функция:
DWORD VirtualQuery( LPCVOID pvAddress, PMEMORY_BASIC_INFORMATION pmbi, DWORD dwLength);
Парная ей функция, VirtualQueryEx, сообщает ту же информацию о памяти, но в другом процессе:
DWORD VirtualQueryEx( HANDLE hProcess, LPCVOID pvAddress,
PMEMORY_BASIC_INFORMATION pmbi, DWORD dwLength);
Эти функции идентичны с тем исключением, что VirtualQueryEx принимает опи сатель процесса, об адресном пространстве которого Вы хотите получить информа цию Чаще всего функцией VirtualQueryEx пользуются отладчики и системные утили ты — остальные приложения обращаются к VirtitalQuery. При вызове VirtualQitery(Ex) параметр pvAddress
должен содержать адрес виртуальной памяти, о которой Вы хо тите получить информацию Параметр ртbi — это адрес структуры MEMORY_BA SIC_INFORMATION, которую надо создать перед вызовом функции. Данная структура определена в файле
WinNT.h так
typedef struct _MFMORY_BASIC_INFORMATION
{
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
SIZE_T RegionSize;
DWORO State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION, PMEMORY_BASIC_INFORMATION;
Параметр dwLength задает размер структуры MEMORY_BASIC_INFORMATION. Фун кция VirtualQuery(Ex) возвращает число байтов, скопированных в буфер.
Используя адрес, указанный Вами в параметре pvAddress, функция VirtualQuery(Ex) заполняет структуру информацией о диапазоне смежных страниц, имеющих одина ковые состояние, атрибуты защиты и тип. Описание элементов структуры приведено в таблице ниже
Элемент |
Описание |
|
|
|
|
BaseAddress |
Сообщает то же значение, что и параметр pvAddress, но округленное до ближайшего меньшею адреса, |
|
|
кратного размеру страницы |
|
|
|
|
AllocationBase |
Идентифицирует базовый адрес региона, включающего в себя адрес, указанный в параметре pvAddress |
|
|
|
|
AllocationProtect |
Идентифицирует атрибут защиты, присвоенный региону при его резервировании |
|
|
|
|
RegionSize |
Сообщаем суммарный размер (в байтах) группы страниц, которые на чинаются с базового адреса |
|
|
BaseAddress и имеют тс же атрибуты защи ты, состояние и тип, что и страница, расположенная по адресу, |
|
|
укачан ному в параметре pvAddress |
|
|
|
|
State |
Сообщает состояние (MEM_FRFF, MFM_RFSFRVE или MEM_COMMIT) всех смежных страниц, которые |
|
|
имеют те же атрибуты защиты, состо яние и тип, что и страница, расположенная по адресу, указанному в |
|
|
параметре pvAddress. |
|
|
При MEM_FREE элементы AllocationBase, AllocationProtect, Protect и Туре содержат неопределенные |
|
|
значения, а при MEM_RESERVE неопреде ленное значение содержит элемент Protect. |
|
Protect |
Идентифицирует атрибут защиты (PAGE *) всех смежных страниц, ко торые имеют те же трибуты |
|
|
защиты, состояние и тип, что и страница, расположенная по адресу, указанному в параметре pvAddress |
|
|
|
|
Type |
Идентифицируем тип физической памяти (MEM_IMAGE, MEM_MAPPED или MEM PRIVATE), |
|
|
связанной с группой смежных страниц, которые имеют те же атрибуты защиты, состояние и тип, что и |
|
|
страница, рас положенная по адресу, указанному в пара метре pvAddress В Windows 98 этот элемент всегда |
|
|
дает MFM_PRIVATE |
|
|
|
Функция VMQuery
Начиная изучать архитектуру памяти в Windows, я пользовался функцией VirtualQuery как «поводырем». Если Вы читали первое издание моей книги, то заметите, что про грамма VMMap была гораздо проще ее нынешней версии, представленной в следую щем разделе. Прежняя была построена на очень простом цикле, из которого перио дически вызывалась функция VirtualQuery, и для каждого вызова я формировал одпу строку, содержавшую элементы структуры MEMORY_BASIC__INFORMATION. Изучая полученные дампы и сверяясь с документацией из SDK (в то время весьма неудачной), я пытался разобраться в архитектуре подсистемы управления памятью. Что ж, с тех пор я многому научился и теперь знаю, что функция VirtualQuery и структура MEMO RY_BASIC_INFORMATION не дают полной картины
Проблема в том, чю в MEMORY_BASIC_INFORMATION возвращается отнюдь не вся информация, имеющаяся в распоряжении системы. Если Вам нужны простейшие дан ные о состоянии памяти по конкретному адресу, VirtualQuery действительно незаме нима. Она отлично работает, если Вас интересует, передана ли по этому адресу фи зическая память и доступен ли он для операций чтения или записи. Но попробуйте e ее помощью узнать общий размер зарезервированного региона и количество блоков в нем или выяснить, не содержит ли этот регион стек потока, — ничего не выйдет
Чтобы получать более полную информацию о памяти, я создал собственную фун кцию и назвал ее VMQuery.
BOOL VMQuery( HANDLE hProcess, PVOID pvAddress, PVMQUERY pVMQ);
По аналогии с VirtualQueryEx она принимает в hProcess описатель процесса, в pvAddress
— адрес памяти, а в pVMQ — указатель на структуру, заполняемую самой функцией. Структура VMQUERY (тоже определенная мной) представляет собой вот что.
typedef struct
{
//информация о регионе
PVOID pvRgnBaseAddress; DWORD dwRgnProtection;
//PAGE_*
SIZE_T RgnSize;
DWORD dwRgnStorage;
//MEM_* Free. Irnage, Mapped, Private DWORD dwRgnBlocks;
DWORD dwRgnGuardBlks; // если > 0, регион содержит стек потока
BOOL tRqnlsAStack; // TRUE, если регион содержит стек потока
//информация о блоке
PVOID pvBlkBaseAddress;
DWORD dwBlkProtection;
// PAGE_*
SIZE_T BlkSize;
DWORD dwBlkStorage;
//MEM_* Free, Reserve, Image, Mapped, Private
}VMQUERY, *PVMQUERY;
Спервого взгляда заметно, что моя структура VMQUERY содержит куда больше информации, чем MEMORY_BASIC_INFORMATION Она разбита (условно, конечно) на две части: в одной — информация и регионе, в другой — информация о блоке (адрес которого указан в параметре pvAddress). Элементы этой структуры описываются в следующейтаблице.
Элемент |
Описание |
pvRgnBaseAddress |
Идентифицирует базовый адрес региона виртуального адресного про странства, включающего |
|
адрес, указанный в параметре pvAddress |
|
|
dwRgnProtection |
Сообщает атрибут защиты, присвоенный региону при его резервиро вании. |
|
|
RgnSize |
Указывает размер (в байтах) зарезернириванного о региона. |
|
|
dwRgnStorage |
Идентифицирует тип физической памяти, используемой группой бло ков данного peгиона: |
|
MEM_FREE, MEM_IMAGE, MEM_MAPPED или MEM PRIVATE. Поскольку Windows 98 не |
|
различает типы памяти, в этой операционной системе данный элемент содержит либо |
|
MEM_FREE, либо MEM_PRIVATE |
|
|
dwRgnBlocks |
Содержит значение — число блоков в указанном регионе |
|
|
dwRgnGuardBlks |
Указывает число блоков с установленным флагом атрибутов защиты PAGE GUARD. Обычно это |
|
значение либо 0, либо 1. Если оно равно 1, то регион скорее всего зарезервирован под стек |
|
потока В Windows 98 этот элемент всегда равен 0 |
fRgnIsAStack |
Сообщает, есть ли в данном регионе стек потока Результат определя ется на основе взвешенной |
|
оценки, так как невозможно дать стопро центной гарантии тому, что в регионе содержится стек. |
|
|
pvBlkBaseAddress |
Идентифицирует базовый адрес блока, включающего адрес, указанный в параметре pvAddress, |
|
|
dwBlkProtection |
Идентифицирует атрибут защиты блока, включающего адрес, указан ный в параметре pvAddress. |
|
|
BlkSize |
Содержит значение — размер блока (в байтах), включающего адрес, указанный в параметре |
|
pvAddress. |
|
|
dwBlkStorage |
Идентифицирует содержимое блока, включающего адрес, указанный в параметре pvAddress. |
|
Принимает одно из значений: MEM FREE, MEM_RESERVE, MEM_IMAGE, MEM_MAPPED или |
|
MEM_PRIVATE. В Windows 98 этот элемент никогда не содержит значения MEM IMAGE и |
|
MEM MAPPED |
|
|
Чтобы получить всю эту информацию, VMQuery, естественно, приходится выпол нять гораздо болыше операций (в том числе многократно вызывать VirtualQueryEx), а потому она работает значительно медленнее VirtualQueryEx. Так что Вы должны все тщательно взвесить, прежде чем остановить свой выбор па одной из этих функций Если Вам не нужна дополнительная информация, возвращаемая VMQuery, используй те VirtualQuery
или VirtualQueryEx.
Листинг файла VMQuerycpp (рис. 14-3) показывает, как я получаю и обрабатываю данные, необходимые для инициализации элементов структуры VMQUERY. (Файлы VMQuery.cpp и VMQueryh содержатся в каталоге 14-VMMap на компакт-диске, прила гаемом к книге.)
Чтобы не объяснять подробности обработки данных «на пальцах», я снабдил тексты программ массой комментариев, вольно разбросанных но всему коду.
Программа-пример VMMap
Эта программа, «14 VMMap.exe» (см. листинг на рис 14-4), просматривает свое адрес ное пространство и показывает содержащиеся в нем регионы и блоки, присутствую щие в регионах Файлы исходного кода и ресурсов этой программы находятся в ка талоге 14VMMap па компакт-диске, прилагаемом к книге. После запуска VMMap на
экране появляется следующее окно.
Карты виртуальной памяти, представленные в главе 13 в таблицах 13-2, 13-3 и 13-4, созданы с помощью именно этой программы
Каждый злемент в списке — результат вызова моей функции VMQuery. Основний цикл программы VMMap (в функции Refresb) выглядит так:
BOOL fOk = TRUE;
PVOID pvAddress = NULL;
...
while (fOk)
{
VMQUERY vmq;
fOk = VMQuery(hProcess, pvAddress, &vmq);
if (fOk)
{
//формируем строку для вывида на экран
//и добавляем ее в окно списка
TCHAR szLine[1024];
ConstructRgnInfoLine(hProcess. &vmq, szLine, sizeof(szLine});
LisTBox_AddString(hwndLB, szLine);
if (fExpandRegions)
{
for (DWORD dwBlock = 0; f0k && (dwBlock < vmq.dwRgnBlocks); dwBlock++)
{
ConstructBlkInfoL ine(&vmq, szLine, sizeof(szLine)); ListBox_AddString (hwndLB, szLine);
// получаем адрес следующего региона
pvAddress = ((PBYTE) pvAddress + vmq BlkSize);
if (dwBlock < vmq dwRgnBlocks - 1)
{
//
нельз
я
запра
шиват
ь
инфор
мацию
о
памят и за после дним блоко
м
fOk = VMQue ry(li Proce ss, pvAdd ress, &vmq)
;
}
}
}
// получаем адрес следующего региона
