Функции w32api


Содержание

Методы перехвата API-вызовов в Win32

Автор: Игорь В. Филимонов
Источник: RSDN Magazine #1-2004

Опубликовано: 13.09.2004
Исправлено: 10.12.2020
Версия текста: 1.0

Введение

Данная статья написана в результате анализа известных методов перехвата API-вызовов в Windows. В некоторых широко известных примерах реализации перехвата системных функций есть небольшие ошибки, которые в некоторых случаях приводят к тому, что перехват не работает. Один из таких примеров был описан в RSDN Magazine #1, другой – в известной книге Джеффри Рихтера «Windows для профессионалов: создание эффективных Win32-приложений с учетом специфики 64-разрядной версии Windows», 4-е издание.

Перехват системных функций операционной системы – приём, известный давно. Обычно перехватывается некоторая системная функция с целью мониторинга или изменения её поведения. Во времена DOS программисты перехватывали программные прерывания (int 21h, int 16h, int 10h). С приходом Win16 понадобились средства для перехвата API-функций. И, наконец, с появлением Win32 средства перехвата ещё раз эволюционировали, подстроившись под новую систему. Операционные системы семейства Windows никогда не содержали встроенных средств, специально предназначенных для перехвата системных функций. И понятно почему – всё-таки это немного хакерский приём. Поэтому перехват обычно осуществляется «подручными средствами», и для его реализации нужно чётко представлять многие глубинные аспекты устройства и функционирования операционной системы.

В данной статье рассматриваются методы реализации перехвата системных API-функций в 32-разрядных операционных системах Windows. Рассматриваются особенности реализации перехвата в Win9X (Windows 95/98/98SE/ME) и WinNT (Windows NT/2000/XP/2003).

Особенности организации памяти в Windows

Так как перехват практически всегда связан с модификацией памяти (либо кода перехватываемой функции, либо таблиц импорта/экспорта), то для его осуществления необходимо учитывать особенности архитектуры памяти WinNT и Win9X.

Каждому процессу (начиная с Windows 95) выделяется собственное виртуальное адресное пространство. Для 32-разрядных процессов его размер составляет 4 Гб. Это адресное пространство разбивается на разделы, функциональное назначение и свойства которых довольно сильно отличаются у семейств ОС WinNT и Win9Х.

Адресное пространство любого процесса в Win9Х можно разделить на три раздела:

  • Младшие два гигабайта (00400000-7FFFFFFF) – код и данные пользовательского режима (в диапазоне 00000000-003FFFFF расположены разделы для выявления нулевых указателей и для совместимости с программами DOS и Win16);
  • Третий гигабайт – для общих файлов, проецируемых в память (MMF), и системных DLL.
  • Четвёртый гигабайт – для кода и данных режима ядра (здесь располагается ядро операционной системы и драйверы).

Старшие два гигабайта являются общими для всех процессов. Основные системные DLL – kernel32.dll, advAPI32.dll, user32.dll и GDI32.dll загружаются в третий гигабайт. По этой причине эти четыре библиотеки доступны всем процессам в системе. Поскольку этот гигабайт общий, они существуют во всех процессах по одним и тем же адресам. Из соображений безопасности Microsoft запретила запись в область, куда они загружаются. Если же запись туда всё же произвести (а это возможно из режима ядра или недокументированными методами), то изменения произойдут во всех процессах одновременно.

В WinNT общих разделов у процессов нет, хотя системные библиотеки по-прежнему во всех процессах загружаются по одинаковым адресам (но теперь уже в область кода и данных пользовательского режима). Запись в эту область разрешена, но у образов системных библиотек в памяти стоит атрибут «копирование при записи» (copy-on-write). По этой причине попытка записи, например, в образ kernel32.dll приведёт к появлению у процесса своей копии изменённой страницы kernel32.dll, а на остальных процессах это никак не отразится.

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

Перехваты можно разделить на два типа: локальные (перехват в пределах одного процесса) и глобальные (в масштабах всей системы).

Локальный перехват


Локальный перехват с использованием раздела импорта

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

В разделе импорта каждого exe- или DLL-модуля содержится список всех используемых DLL. Кроме того, в нем перечислены все импортируемые функции. Вызывая импортируемую функцию, поток получает ее адрес фактически из раздела импорта. Поэтому, чтобы перехватить определенную функцию, надо лишь изменить её адрес в разделе импорта. Для того чтобы перехватить произвольную функцию в некотором процессе, необходимо поправить её адрес импорта во всех модулях процесса (так как процесс может вызывать эту функцию не только из exe-модуля, но и из DLL-модулей). Кроме того, процесс может воспользоваться для загрузки DLL функциями LoadLibraryA, LoadLibraryW, LoadLibraryExA, LoadLibraryExW или, если она уже загружена, определить её адрес при помощи функции GetProcAddress. Поэтому для перехвата любой API-функции необходимо перехватывать и все эти функции.

Существует несколько широко известных примеров реализации этого метода, в частности один из них описан в книге Джеффри Рихтера «Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64-разрядной версии Windows» (Jeffrey Richter «Programming Applications for Microsoft Windows»), 4-е издание. Другой пример – библиотека APIHijack, написанная Wade Brainerd на основе DelayLoadProfileDLL.CPP (Matt Pietrek, MSJ, февраль 2000). Для описания этого метода я взял за основу пример Джеффри Рихтера (с небольшими изменениями).

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

При помощи функции ImageDirectoryEntryToData определяется дескриптор таблицы импорта и, если он есть, перебираются все DLL, из которых импортируются функции. Если DLL находится, то среди функций, импортируемых из неё, ищется нужная, а затем при помощи WriteProcessMemory её адрес меняется на адрес своего обработчика. Теперь он будет вызываться каждый раз, когда из данного модуля будет происходить обращение к перехваченной функции.

ПРИМЕЧАНИЕ

Если вы читали уже упоминаемую выше книгу Джеффри Рихтера, то могли заметить, что в функции ReplaceIATEntryInOneMod я сделал одно изменение. У него она работала так: в таблице импорта находился список функций того модуля, функция из которого импортировалась, и если в этом списке эта функция не находилась, то ReplaceIATEntryInOneMod больше ничего не делала (т. е. перехват не происходил). Я столкнулся с таким поведением, когда написал тестовую программу на Delphi для примера DriveType2 (этот пример описан ниже, в разделе «Глобальный перехват методом тотального локального перехвата», он перехватывает функцию GetDriveTypeA во всех приложениях с использованием описываемого метода). Тест, написанный на Visual C++, работал прекрасно – функция GetDriveTypeA перехватывалась. А вот программа на Delphi всё равно для всех перехватываемых мной дисков возвращала реальные значения. Я посмотрел таблицу импорта тестовой программы при помощи утилиты DUMPBIN и обнаружил, что компилятор Delphi не поместил все импортируемые функции из kernel32.dll в один список, а разбил их на 3 части, причём GetDriveTypeA оказалась в третьей. Поэтому функция ReplaceIATEntryInOneMod Джеффри Рихтера, просмотрев все функции из первого списка Kernel32.dll, не нашла функции GetDriveTypeA, хотя она и импортировалась модулем DriveTypeTest.exe. Я исправил эту функцию таким образом, чтобы она проверяла всю таблицу импорта и перебирала все списки с функциями из kernel32.dll (как оказалось, их может быть несколько). В описании формата РЕ-файла нигде не оговаривается, что каждый модуль, из которого импортируются функции, должен встречаться в секции импорта только один раз, и, видимо, некоторые компиляторы этим пользуются.

При реализации данного метода следует учитывать, что вызовы из DllMain библиотеки, в которой находится перехватываемая функция, перехватить не удастся. Это связано с тем, что перехват может быть осуществлён только по окончании выполнения LoadLibrary, а к этому времени DllMain уже будет вызвана. Конечно, можно написать свой вариант LoadLibrary (примеры загрузки DLL «вручную» существуют) и осуществлять перехват между загрузкой DLL и вызовом DllMain, но это сильно усложняет задачу.

Основным достоинством данного метода является то, что он одинаково реализуется как в Win9X, так и в WinNT.

ПРИМЕЧАНИЕ

В Windows NT функции Module32First и Module32Next не реализованы, и для перечисления модулей процесса вместо них придётся воспользоваться функциями из PSAPI.dll.

Локальный перехват посредством изменения перехватываемой функции (только WinNT)

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

Существует множество примеров реализации этого метода. Я рассмотрю метод, предлагаемый Microsoft – Detours library.

Detours – это первая официальная библиотека, предназначенная для перехвата функций (не только системных, но и любых других). К основным понятиям Detours относятся:

  • целевая функция (target function) – функция, перехват которой осуществляется;
  • функция-перехватчик (detour function) – функция, замещающая перехватываемую;
  • функция-трамплин (trampoline function) – функция, состоящая из заголовка целевой функции и команды перехода к остальному коду целевой функции.

ПРИМЕЧАНИЕ

Trampoline в переводе с английского – «батут», однако словосочетание «функция-трамплин» более точно передаёт логику её работы.

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

то в результате перехвата получится следующее:

Причём функция-перехватчик может вызывать функцию-трамплин в качестве оригинальной целевой функции.

Библиотека Detours предлагает два метода внедрения «трамплинов» – статический и динамический. Статический метод используется, когда адрес целевой функции известен на этапе сборки модуля. Реализуется он так:

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

При перехвате функция DetourFunction динамически создаёт трамплин и возвращает его адрес. В качестве функции SomeFunction, которая в данном примере возвращает адрес целевой функции, можно использовать DetourFindFunction, которая пытается найти нужную функцию в нужном модуле. Сначала она пытается сделать это через LoadLibrary и GetProcAddress, а в случае неудачи – использует библиотеку ImageHlp для поиска отладочных символов.

Макрос DETOUR_TRAMPOLINE и функция DetourFunction включают в себя встроенный табличный дизассемблер, который определяет, какое количество байт из заголовка целевой функции должно быть скопировано в функцию-трамплин (не менее 5 байт (размер команды jmp), составляющих целое число команд процессора). Если целевая функция занимает менее 5 байт, то перехват оканчивается неудачей.

К достоинствам данного метода следует отнести простоту и надёжность. В отличие от метода с использованием раздела импорта, не нужно учитывать все возможные методы, которыми может быть получен реальный адрес функции. Недостаток – не удастся перехватить функцию с размером менее 5 байт или функцию со следующим заголовком:

Вышеприведённый пример Galen Hunt, один из авторов Detours, прокомментировал следующим образом: «Существует множество теоретических примеров кода, где пролог функции меньше 5 байт, требуемых для команды jmp. Однако никто не сообщал о реальных примерах функции с такими проблемами».

ПРЕДУПРЕЖДЕНИЕ

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

Существует другой способ реализации данного метода. Вместо команды jmp в начало функции помещается команда INT 3, а управление функции-перехватчику передаётся косвенно в обработчике необработанных исключений (её адрес заносится в pExceptionInfo->ContextRecord->Eip и обработчик возвращает EXCEPTION_CONTINUE_EXECUTION). Так как команда INT 3 занимает 1 байт, то вышеописанная ситуация в этом случае даже теоретически невозможна.

ПРЕДУПРЕЖДЕНИЕ

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

Глобальный перехват

Глобальный перехват может быть реализован различными способами. Первый способ – применение локального перехвата ко всем приложениям в системе (запущенным в момент перехвата или позже). Второй способ – «взлом системы» – подразумевает подмену кода перехватываемой функции непосредственно в DLL-файле или его образе в памяти.

Глобальный перехват методом тотального локального перехвата

Данный метод основан на следующем: если можно перехватить функцию из текущего процесса, то нужно выполнить код перехвата во всех процессах в системе. Существует несколько методов заставить чужой процесс выполнить код перехвата. Самый простой – внести этот код в DllMain некоторой библиотеки, а затем внедрить её в чужой процесс. Методов внедрения DLL также существует несколько (см. Джеффри Рихтер). Самый простой, работающий и в Win9X, и в WinNT – внедрение DLL при помощи ловушек. Реализуется он так: в системе устанавливается ловушка (при помощи функции SetWindowsHookEx) типа WH_GETMESSAGE (эта ловушка служит для перехвата Windows-сообщений). В этом случае модуль, в котором находится ловушка, автоматически подключается к потоку, указанному в последнем аргументе SetWindowsHookEx (если указан 0, то производится подключение ко всем потокам в системе). Однако подключение происходит не сразу, а перед тем, как в очередь сообщений потока будет послано какое-нибудь сообщение. Поэтому перехват осуществляется не сразу после запуска приложения, а перед обработкой процессом первого сообщения. Так что все вызовы перехватываемой функции до обработки процессом первого сообщения перехватываться не будут. А в приложениях без очереди сообщений (например, консольных) этот способ внедрения вообще не работает.

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

Функция GetDriveTypeA из библиотеки kernel32.dll используется программами Windows для определения типа диска (локальный, CD-ROM, сетевой, виртуальный и т. д.). Она имеет следующий прототип:

lpRootPathName – путь до диска (А:\, В:\ и т.д.)

GetDriveTypeA возвращает одно из следующих значений:

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

Программа DriveType2 состоит из двух модулей: DriveType2.exe и DT2lib.dll.

DriveType2.exe реализует интерфейс, а вся работа выполняется в DT2lib.dll.

Проект DT2lib состоит из трёх основных файлов:

APIHook.cpp – этот файл написан Джеффри Рихтером (за исключением некоторых исправлений, сделанных мной. О них я расскажу ниже). В этом файле описан класс CAPIHook, реализующий перехват заданной API-функции во всех модулях текущего процесса. Здесь же автоматически перехватываются функции LoadLibraryA, LoadLibraryW, LoadLibraryExA, LoadLibraryExW и GetProcAddress.

Toolhelp.h – этот файл также написан Джеффри Рихтером. В нём описан класс CToolhelp, реализующий обращение к системным toolhelp-функциям. В данном случае он используется классом CAPIHook для перечисления всех модулей, подключенных к процессу.

DT2Lib.cpp – в этом файле я реализовал перехват функции GetDriveTypeA с использованием класса CAPIHook, а также установку ловушки типа WH_GETMESSAGE, обеспечивающей подключение данного модуля (DT2lib.dll) ко всем потокам в системе.

Как же происходит перехват?

Сразу же после запуска DriveType2.exe вызывается функция DT2_HookAllApps из DT2lib.dll, которая устанавливает ловушку.

Функция ловушки GetMsgProc ничего не делает, а просто вызывает следующую функцию ловушки (возможно, не только наша программа установила ловушку, и это, как минимум нужно проверить). Перед тем, как поместить в очередь, ассоциированную с некоторым потоком, какое-то сообщение, система должна вызвать все установленные ловушки типа WH_GETMESSAGE (обычно такие ловушки используются для мониторинга или изменения некоторых сообщений, однако мы ничего подобного не делаем – нам нужно просто подключиться ко всем потокам в системе). Система не может просто вызвать нашу функцию ловушки – она и получатель сообщения находятся в разных процессах, а значит, и в разных адресных пространствах. И выход из этой ситуации один – система просто подключает модуль (а это обязательно должен быть DLL-модуль), в котором находится ловушка, к тому процессу, которому посылается сообщение (что нам собственно и нужно).


После подключения DLL к потоку происходит инициализация всех переменных (каждый процесс, к которому подключается DLL, имеет копии всех глобальных и статических переменных, описанных в ней). Из глобальных переменных у нас есть 6 экземпляров класса CAPIHook (для GetDriveTypeA в DT2Lib.cpp и для LoadLibraryA, LoadLibraryW, LoadLibraryExA, LoadLibraryExW и GetProcAddress – в APIHook.cpp). Таким образом, при подключении DLL происходит шестикратный вызов конструктора класса CAPIHook, перехватывающего вышеперечисленные функции в текущем (то есть в том, к которому только что произошло подключение) процессе.

При завершении процесса внедрённая DLL отключается. При этом происходит вызов деструктора CAPIHook для всех экземпляров класса.

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

Функция Hook_GetDriveTypeA сначала вызывает оригинальную GetDriveTypeA. Затем, если возвращаемое значение больше DRIVE_NO_ROOT_DIR (то есть функции был передан корректный аргумент, и она выполнилась без ошибок), то проверяется, переопределен ли диск, тип которого запрашивается. Информация о значениях перехватываемых функций в данном случае хранится в реализованном мной массиве BYTE Drives[26], что позволяет реализовать перехват 26 дисков, от A: до Z:. В этом массиве хранятся значения, возвращаемые функцией GetDriveTypeA для каждого из дисков. Итак, если значение элемента массива, соответствующего аргументу GetDriveTypeA равно 0xFF, то значение возвращается без изменений, в противном случае возвращается значение из массива. Запись значений в этот массив реализуется в DriveType2.cpp.

СОВЕТ

Если вы хотите, чтобы эта программа полноценно работала в WinNT, следует также перехватить функцию GetDriveTypeW.

Ещё одна реализация данного метода описана в статье «Перехват API-функций в Windows NT/2000/XP», автор Тихомиров В. А., публиковалась в RSDN Magazine #1 (будьте осторожны, там та же ошибка, что и у Джеффри Рихтера).

ПРИМЕЧАНИЕ

У этого метода есть ещё один существенный недостаток: некоторые коммерческие программы (например, популярный файловый менеджер Total Commander, упакованный ASPack) используют различные системы защиты (ASProtect, VBox и т. д.), шифрующие таблицу импорта защищаемого приложения. С такими программами этот метод не работает.

Глобальный перехват может быть реализован и с помощью Detours (только в WinNT). А так как методов внедрения DLL известно несколько, то различных вариантов реализации глобального перехвата можно предложить довольно много.

Глобальный перехват методом подмены кода в DLL

Данный метод можно реализовать двумя способами: непосредственной правкой кода DLL, в которой расположена целевая функция, или подменой этой DLL другой, экспортирующей тот же набор функций. Второй способ известен под названием «Подмена с использованием оберток (wrappers)».

Первый способ позволяет реализовывать только сравнительно небольшие по размеру функции-перехватчики, так как код необходимо внедрять в свободные участки DLL – в основном в межсекционное пространство. Другой недостаток – код необходимо писать на ассемблере. Общая идеология работы этого метода та же, что и в Detours. В код целевой функции внедряется команда jmp к функции-перехватчику. Байты, скопированные «из-под» jmp’а, перемещаются в перехватчик (так как перехватчик всё равно пишется на ассемблере, в этом случае его проще сразу совместить с функцией-трамплином). Вот пример реализации этого метода.

В каталоге DriveType0 находится файл kernel32.dll, в котором я сделал следующие исправления (при помощи hiew32.exe):

По адресу 4E02 – локальный адрес .0BFF74E02 (это конец функции GetDriveTypeA) я поместил команду jmp .0BFF71080 – на первое попавшееся свободное место (в исполняемых файлах всегда много свободного места – обычно в концах секций).

По адресу .0BFF71080 (глобальный адрес 1080) я поместил следующий код:

Таким образом, когда светодиод ScrollLock не горит, функция GetDriveTypeA работает как обычно, а если горит – то для всех Windows-приложений все локальные диски (у меня это С:\ и D:\) превращаются в CD-ROMы.

ПРИМЕЧАНИЕ

Чтобы всё это заработало, необходимо заменить файл C:\Windows\System\kernel32.dll на файл DriveType0\kernel32.dll. Сделать это можно, только загрузив компьютер в режиме MS-DOS, так как kernel32.dll – одна из системных библиотек Windows. Данный пример реализован для Windows 98. Поскольку системные библиотеки меняются в зависимости от версии Windows (и даже от номера билда), то в других операционных системах этот пример работать не будет (его нужно реализовывать для каждой версии kernel32.dll заново).

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

Другой способ реализации этого метода – использование оберток (wrappers). Суть его в создании собственной DLL с тем же набором экспортируемых функций, что и оригинальная. В качестве примера могу привести следующий вариант реализации вышеприведённого примера:

  • Системную библиотеку Kernel32.dll переименовываем в kernel31.dll :).
  • Создаём библиотеку с именем Kernel32.dll, в которой реализована одна функция – GetDriveTypeA (это будет функция-перехватчик), а все остальные функции переадресуем к kernel31.dll (благо компилятор Visual C++ поддерживает переадресацию функций DLL).
  • Полученную библиотеку помещаем в системный каталог.

При этом функция-перехватчик может вызывать оригинальную функцию из kernel31.dll.

Основным недостатком данного способа является то, что он не годится для DLL, экспортирующих переменные.

Глобальный перехват методом подмены кода DLL в памяти (только Win9X)

Идея данного метода заключается в следующем: в Win9X системные DLL загружаются в общую для всех процессов область памяти (в третий гигабайт). Поэтому если бы удалось произвести реализацию Detours под Win9X, то изменения коснулись бы всех процессов (то есть случился бы глобальный перехват). Ситуация осложняется тем, что запись в область системных DLL в Win9X возможна или из режима ядра, или недокументированными средствами. Кроме того, в момент записи необходимо остановить все потоки, которые могут вызывать целевую функцию. Это можно сделать при помощи SuspendThread. Однако эта функция требует в качестве аргумента handle потока, а используемые для перечисления потоков функции Thread32First/Thread32Next возвращают ThreadID. Функция OpenThread, которая позволяет получить handle из ThreadID, реализована только начиная с Windows ME. По этой причине в общем виде документированными средствами из режима пользователя данный метод в Win9X нереализуем. Однако есть другой способ. Обе обозначенные проблемы (запись в область системных DLL и останов всех пользовательских потоков в системе) могут быть решены, если для перехвата использовать драйвер. Драйвер работает в режиме ядра, поэтому из него можно производить запись по любым доступным адресам. А если на момент установки/снятия перехвата поднять IRQL (уровень запроса на прерывание) до DISPATCH_LEVEL, то прервать поток сможет только процедура обработки аппаратного прерывания (откуда вызывать пользовательские системные функции нельзя). Кроме того, применение драйвера позволяет решить ещё одну проблему. Функция-перехватчик и функция-трамплин должны располагаться в области памяти, общей для всех процессов (в старших 2 гигабайтах). Конечно, можно было бы создать файл, отображаемый в память (MMF – они в Win9X размещаются в третьем гигабайте), и поместить код этих функций туда, или разместить их в отдельной DLL с ImageBase в третьем гигабайте, но проще реализовать их непосредственно в драйвере (драйверы в Win9X размещаются в четвёртом гигабайте – в разделе кода и данных режима ядра).

Рассмотрим пример, реализующий описанный метод. Для осуществления перехвата и размещения функции-трамплина и функции-перехватчика я написал WDM-драйвер (с использованием Visual C++ 6.0, Windows 2000 DDK и Compuware DriverStudio 2.7), а также программу для взаимодействия с ним. Программа и драйвер расположены в каталоге DriveType1 (там же – инструкции по установке).

Пример DriveType1 состоит из двух частей – драйвера DTDrv.sys и установочного скрипта DTDrv.inf, а также программы DriveType.exe.

DriveType.exe компилируется из одного модуля DriveType.cpp, в котором реализованы пользовательский интерфейс и интерфейс с драйвером. Интерфейс с драйвером реализуется через функции CreateFile (открытие драйвера), DeviceIoControl (операции ввода-вывода) и CloseHandle (закрытие драйвера). Реализованы четыре команды, вызываемые через DeviceIoControl – перехват функции GetDriveTypeA, снятие перехвата, установка возвращаемого значения функцией перехвата для каждого из дисков A: .. Z:, чтение текущего состояния перехвата.

Ну а вся работа по перехвату делается в драйвере, за исключением того, что адрес функции GetDriveTypeA определяется также в DriveType.cpp и присылается в качестве параметра в команде перехвата. После получения этого адреса функция DTDRV_IOCTL_HOOK_Handler (из модуля DTDrvDevice.cpp) реализует перехват рассмотренным выше способом. Функция DTDRV_IOCTL_UNHOOK_Handler снимает перехват, функция DTDRV_IOCTL_SETDRIVE_Handler устанавливает значение, возвращаемое перехватчиком, функция DTDRV_IOCTL_GETSTATE_Handler возвращает значения перехвата и флаг перехвата.

Основные переменные, используемые DriveType.cpp:

В Drives[26] хранятся значения, возвращаемые функцией MGetDriveType для дисков A: .. Z: (=0xFF, если информация о диске не переопределена).

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

Изначально эта функция «пустая», так как весь её код пишется во время перехвата функцией DTDRV_IOCTL_HOOK_Handler, которая, если оперировать терминами Detours, реализует динамический перехват.

ПРИМЕЧАНИЕ

Код этой функции изначально может быть любым, но он должен занимать по крайней мере 10 байт (чтобы уместились 5 байт из заголовка GetDriveTypeA и 5 байт – команда jmp).

Собственно функция-перехватчик MGetDriveType реализована в моём примере так:

Сначала вызывается функция-трамплин NewGetDriveType, которая фактически выполняет код оригинальной GetDriveTypeA (сначала выполняются первые 5 байт – это 3 команды ассемблера, затем – всё остальное). После этого определяется буква диска. Преобразование буквы в верхний регистр осуществляется вручную. Далее, если данный диск перехвачен, возвращается значение из массива Drives, в противном случае – то, которое вернула NewGetDriveType.

Перехват реализован в функции DTDRV_IOCTL_HOOK_Handler следующим образом:

Если функция GetDriveTypeA ещё не перехвачена (IsHook=false), то:

  1. Определяется адрес функции GetDriveTypeA (он присылается в качестве параметра);
  2. По адресу NewGetDriveType копируются 5 байт из начала GetDriveTypeA;
  3. За ними вставляется байт 0xE9 (код команды jmp) и смещение до точки GetDriveTypeA + 5;
  4. По адресу GetDriveTypeA вставляется 0xE9 и смещение до точки MGetDriveType;
  5. Флаг перехвата IsHook устанавливается в true.

Функция снятия перехвата возвращает всё на свои места:

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

Заключение

Итак, перехват API-вызовов – вещь, хотя и достаточно сложная, но все-таки реализуемая (причём различными способами). Методы перехвата различны и часто не переносимы из одной версии Windows в другую.

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

Diplom Consult.ru

Разговор об особенностях вызова Win32 API функций еще не закончен. Тем не менее, пора прервать общее изложение и обратиться к примерам. Многое из того, что было сказано, целесообразно проиллюстрировать примерами работы с конкретными функциями Win 32 API. Каждый из наших примеров будет посвящен работе с некоторой группой функций.

Работа с окнами

Как мы уже говорили, окна — это один из основных объектов операционной системы. Функции для работы с ними находятся, в основном, в библиотеке User32. Из большого множества функций мы отобрали несколько функций, позволяющих продемонстрировать, как можно получать описатели окон, как, зная описатель, можно получать характеристики окон и как можно изменять характеристики окон. Начнем с приведения программного текста, а уж потом подробно прокомментируем его. Заметим, что для получения корректного описания операторов Declare, используемых типов данных и констант мы использовали API Viewer и описание функций, которое можно найти на уже упоминавшемся сервере Microsoft.

В проекте нашего тестового документа был создан модуль «Окна» и в разделе его объявлений помещен следующий текст:

Public Const SW_H >

Public Const SW_SHOWNORMAL = 1

Public Const SW_SHOWMINIMIZED = 2

Public Const SW_SHOWMAXIMIZED = 3

Public Type RECT

Public Declare Function GetActiveWindow Lib «user32» () As Long

Public Declare Function GetWindowRect Lib «user32» (ByVal hwnd As Long, _

lpRect As RECT) As Long

Public Declare Function GetWindowText Lib «user32» Alias «GetWindowTextA» _

(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Public Declare Function FindWindow Lib «user32» Alias «FindWindowA» _

(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Public Declare Function ShowWindow Lib «user32» (ByVal hwnd As Long, _

ByVal nCmdShow As Long) As Long

Public Declare Function SetWindowText Lib «user32» Alias «SetWindowTextA» _


(ByVal hwnd As Long, ByVal lpString As String) As Long

Дадим краткую характеристику используемых функций:

GetActiveWindow возвращает описатель активного окна.

GetWindowRect получает в качестве входного параметра описатель окна hwnd и возвращает значения полей структуры Rect, переданной функции в качестве второго параметра lpRect. Заметьте, тип Rect должен быть предварительно определен. Обратите внимание, первый параметр передается по значению, а второй по ссылке. Передача по ссылке позволяет функции заполнить значениями поля переданной ей структуры. Значения этих полей задают координаты прямоугольника, определяющего положение окна на экране дисплея. Возвращаемый функцией результат будет равен нулю, если выполнение функции закончится неуспехом, например, при некорректном задании описателя.

GetWindowText по описателю окна возвращает его заголовок. Поскольку функция должна вернуть строку, то, как мы говорили ранее, ей передаются два параметра — lpString и cch, задающие строку и число символов, доступных для заполнения. Обратите внимание, здесь используется псевдоним с окончанием A, указывающим на использование кодировки ANSI. Опять-таки, результат, возвращаемый функцией, зависит от успеха операции.

FindWindow возвращает описатель окна. Функция ищет окно и возвращает в случае успеха описатель этого окна. Окно ищется либо по имени класса, заданного параметром lpClassName, либо по заголовку, заданному параметром lpWindowName. При вызове может быть задан только один из этих параметров, второй может быть указателем с неопределенным значением.. Поскольку параметр может быть либо указателем, имеющим тип Long, либо строкой, то в предыдущих версиях по этой причине для обоих параметров следовало указывать тип Any. Теперь указывается тип String, поскольку введена специальная константа vbNullString, формально имеющая тип String, но позволяющая передать указатель со значением Null.

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

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

Приведем теперь процедуру, в которой поочередно запускаются описанные здесь функции (В примерах используются документы: DocOne6, DocTwo6

Public Sub WorkWithWindows()

Dim Res As Long ‘Результат выполнения функции

Dim HandleAW As Long ‘Описатель активного окна

Dim RectAW As RECT ‘Структура, задающая прямоугольник окна

Dim TextAW As String ‘Заголовок активного окна

Dim LenTextAW As Long ‘Длина строки

Dim HandleW As Long ‘Описатель окна

Dim TextW As String ‘Заголовок окна

‘Получить описатель активного окна

‘Получить прямоугольник, задающий положение активного окна

Res = GetWindowRect(HandleAW, RectAW)

If Res > 0 Then ‘OK

Debug.Print «Размеры окна: Left = «, RectAW.Left, » Top = «, _

RectAW.Top, » Right = «, RectAW.Right, » Bottom = «, RectAW.Bottom

MsgBox («Не удалось получить размеры активного окна»)

‘Получить заголовок окна

‘Предварительная набивка результирующей строки нулевыми символами

TextAW = VBA.String$(255, vbNullChar)

Res = GetWindowText(HandleAW, TextAW, LenTextAW)

If Res > 0 Then ‘OK

TextAW = VBA.Left(TextAW, VBA.InStr(1, TextAW, vbNullChar) — 1)

MsgBox («Не удалось получить заголовок активного окна»)

‘Поиск окна документа по его заголовку

‘Возвращается описатель окна

TextW = «DocOne6 — Microsoft Word»

HandleW = FindWindow(vbNullString, TextW)

If HandleW > 0 Then ‘OK

MsgBox («Не удалось найти окно с указанным заголовком» _

‘Минимизация и нормализация окна документа

Res = ShowWindow(HandleW, SW_SHOWMINIMIZED)

If Res > 0 Then Debug.Print «Окно минимизировано»

Res = ShowWindow(HandleW, SW_SHOWNORMAL)

If Res > 0 Then Debug.Print «Окно в нормальном состоянии»

‘Изменение заголовка окна

TextW = «Document1 — Microsoft Word»

HandleW = FindWindow(vbNullString, TextW)

If HandleW > 0 Then ‘OK

MsgBox («Не удалось найти окно с указанным заголовком» _

Res = SetWindowText(HandleW, «DocTwo6 — Microsoft Word»)

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

Размеры окна: Left = 8 Top = 73 Right = 769 Bottom = 580

Microsoft Visual Basic — DocOne6 [running] — [Окна (Code)]

Окно в нормальном состоянии

Дадим комментарии к работе этой процедуры:

Вначале, при вызове функции API GetActiveWindow был получен описатель активного окна. Заметим, что это было окно кода выполняемой процедуры. Значение этого описателя равно 655706.

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

На следующем шаге был получен заголовок активного окна. Как видно из распечатки заголовка, активным являлось выполняемое окно кода. Результат 58, который вернула функция GetWindowText, задает число символов результирующей строки. Обратите внимание, перед вызовом функции передаваемая ей строка была инициализирована нулевыми символами. После успешного завершения из строки был выделен результат, задающий заголовок. Его печать показывает, что активным в момент запуска функции было выполняемое окно кода. Для выделения заголовка из строки использовался нулевой символ, как признак окончания заголовка. Для решения этой задачи можно было использовать и число возвращаемых символов — значение, возвращаемое функцией.

Следующим шагом было получение описателя окна по заданному заголовку. В качестве такового был использован заголовок окна с тестовым документом. Функция FindWindow нашла такое окно и вернула его описатель, равный 6684884. Обратите внимание, первый параметр был задан константой vbNullString.

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

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

Функции w32api

Windows API был изначально спроектирован для использования в программах, написанных на языке C (или C++). Работа через Windows API — это наиболее близкий к системе способ взаимодействия с ней из прикладных программ. Более низкий уровень доступа, необходимый только для драйверов устройств, в текущих версиях Windows предоставляется через Windows Driver Model.

Версии

  • Win16 — первая версия Windows API для 16-разрядных версий Windows. Изначально назывался просто Windows API, затем стал называться Win16 для отличия от Win32.
  • Win32s — подмножество Win32, устанавливаемое на семейство 16-разрядных систем Windows 3.x и реализующее ограниченный набор функций Win32 API для этих систем.
  • Win32 — 32-разрядный API для современных версий Windows. Самая популярная ныне версия. Базовые функции этого API реализованы в kernel32.dll и advapi32.dll; базовые модули GUI — в user32.dll и gdi32.dll. Win32 появился вместе с Windows NT и затем был перенесён (в несколько ограниченном виде) в системы серии Windows 9x. В современных версиях Windows, происходящих от Windows NT, работу Win32 GUI обеспечивают два модуля: csrss.exe (Client/Server Runtime Subsystem), работающий в пользовательском режиме, и win32k.sys в режиме ядра. Работу же системных Win32 API обеспечивает ядро — ntoskrnl.exe
  • Win64 — 64-разрядная версия Win32, содержащая дополнительные функции для использования на 64-разрядных компьютерах. Win64 API можно найти только в 64-разрядных версиях Windows XP, Windows Server 2003, Windows Vista, Windows Server 2008 и Windows 7.

Полный алфавитный список технологий, доступных через Windows API


  • Access Control
  • Active Accessibility
  • Active Directory
  • Active Directory Services Interface (ASP)
  • ActiveX Data Objects (
  • Automation
  • Background Intelligent Transfer Service (BITS)
  • Certificate Services
  • Collaboration Data Objects
  • Cryptography
  • Debugging and Error Handling
  • Device I/O
  • Distributed File System (Dfs)
  • Threads
  • Domain Name System (DHCP)
  • Extensible Authentication Protocol (XML) и парсер
  • Group Policy
  • ICS и ICF
  • Image Color Management (ICM)
  • Indexing Service
  • Infrared Data Association (IrDa)
  • Internet Authentication Service (IAS)
  • Internet Connection Sharing and Firewall (ICSF)
  • Internet Explorer
  • Internet Information Services (IIS)
  • Internet Protocol Helper (IP Helper)
  • Interprocess Communications
  • Lightweight Directory Access Protocol (LSA Authentication
  • LSA Policy
  • Memory Management
  • Message Queuing (MSMQ)
  • Messaging Application Programming Interface (
  • Microsoft Agent
  • Microsoft Data Access Components (MIDL)
  • Microsoft Management Console (MTS)
  • Multicast Address Dynamic Client Allocation Protocol (MADCAP)
  • Multicast Group Manager
  • National Language Support
  • NetMeeting
  • NetShell
  • Network Load Balancing Provider
  • Network Management
  • Network Monitor
  • Network Provider API
  • OLE DB
  • OLE DB Provider for Internet Publishing
  • OnNow
  • Open Database Connectivity (OpenGL
  • Password Filters
  • PC Health
  • Performance Monitoring
  • Plug and Play и Universal Plug and Play
  • Power Management
  • Quality of Service (
  • Remote Access Service (RAS)
  • Remote Procedure Call (имеется в виду служба RPC Service)
  • Removable Storage Manager (RSM)
  • Routing and Remote Access Service (RRAS)
  • Routing Table Manager Version 1 (RTMV1)
  • Routing Table Manager Version 2 (RTMV2)
  • Security Support Provider Interface (SSPI)
  • Server Cluster API
  • Server Data Objects (SDO)
  • Service Security Attachments
  • Setup API

  • Shell (имеется в виду Explorer Shell)
  • Side-by-side Assemblies
  • Simple Network Management Protocol (
  • Still Image
  • Storage и Structured Storage
  • Synchronization Manager
  • System.DirectoryServices
  • System Event Notification Service (SENS)
  • System Restore
  • Tablet PC
  • Task Scheduler
  • Telephony Application Programming Interface (TAPI) 2.2
  • Telephony Application Programming Interface (TAPI) 3
  • Telephony Service Provider Interface (TSPI и MSPI)
  • Terminal Services
  • Text Services Framework
  • Unicode (и MSLU)
  • Universal Description, Discovery, and Integration (
  • Windows Clustering
  • Windows File Protection
  • Windows
  • Windows Image Acquisition (WIA)
  • Windows Installer
  • Windows Management Instrumentation (
  • Windows Sockets
  • Windows System Information
  • Windows User Interface
  • Winlogon и Gina
  • WinSNMP

См. также

  • Microsoft .NET
  • Window class
  • WinMain

Ссылки

  • WinAPI @ dmoz.org
  • windows api programming — Форум клуба программистов
  • Platform SDK: Windows API — раздел Windows API в библиотеке MSDN
  • Открытая документация по WinAPI

Литература

  • Гэри Неббет Справочник по базовым функциям API Windows NT/2000 = Windows NT/2000 Native API Reference. — М.: «Вильямс», 2002. — С. 528. — ISBN 1-57870-199-6
Программные интерфейсы и Microsoft Windows
Графика Проводник Windows • Direct3D • Windows Presentation Foundation • Windows Color System • Windows Image Acquisition • Windows Imaging Component
Звук MME
Мультимедиа DirectShow • Windows Media • Media Foundation
Веб MSHTML • JScript • ActiveX •
Доступ к данным Компоненты Microsoft Data Access • Extensible Storage Engine •
Сети Winsock (LSP) • Filtering Platform •
Коммуникации TAPI
Администрирование Консоль Win32 • Windows Script Host • Инструментарий управления Windows • PowerShell • Планировщик задач • Offline Files • Теневое копирование • Windows Installer • Диспетчер ошибок Windows • Журнал событий Windows
Модель компонентов COM • COM+ • DCOM • .NET Framework
Библиотеки Microsoft Foundation Classes (MFC) • Active Template Library (ATL) • Windows Template Library (WTL) • Framework Class Library (FCL)
Разработка драйверов Модель драйверов Windows • Windows Driver Foundation (KMDF • UMDF)
Безопасность Windows CardSpace • Data protection API • Security Support Provider Interface
.NET .NET Framework • ADO.NET • .NET Remoting • Windows Presentation Foundation • Windows Workflow Foundation • Windows Communication Foundation • Windows CardSpace • XNA •
Межпроцессное
взаимодействие
MSRPC • Именованные каналы
Текст и
поддержка языков
Framework Текстовых сервисов • Объектная модель текстов • • Языковые пакеты • Многоязычный интерфейс
Игры XNA •
Widget toolkit
Low-level Macintosh Toolbox/Carbon · Windows API · Intrinsics · Intuition · High-level
Amiga OS BOOPSI · Magic User Interface · Zune · ReAction GUI
Mac OS и Mac OS X · MacApp · MacZoop · PowerPlant
Microsoft Windows Microsoft Foundation > · Windows Template Library · SmartWin++ · Object Windows Library · Visual Component Library · Windows Forms · Windows Presentation Foundation
Unix, under the X Xaw · · · InterViews
Cross-platform Based on Flash: Adobe Flex · · SWF2EXE Software

Wikimedia Foundation . 2010 .

Смотреть что такое «Win32 API» в других словарях:

Win32-API — Das Windows Application Programming Interface (kurz: WinAPI; zu dt. etwa: Windows Anwendungs Programmierungs Schnittstelle) ist eine Programmierschnittstelle und Laufzeitumgebung, welche Programmierern bereitsteht, um Anwendungsprogramme für… … Deutsch Wikipedia

Win32 API — Das Windows Application Programming Interface (kurz: WinAPI; zu dt. etwa: Windows Anwendungs Programmierungs Schnittstelle) ist eine Programmierschnittstelle und Laufzeitumgebung, welche Programmierern bereitsteht, um Anwendungsprogramme für… … Deutsch Wikipedia

Win32 — Das Windows Application Programming Interface (kurz: WinAPI; zu dt. etwa: Windows Anwendungs Programmierungs Schnittstelle) ist eine Programmierschnittstelle und Laufzeitumgebung, welche Programmierern bereitsteht, um Anwendungsprogramme für… … Deutsch Wikipedia

Win32 — Windows API (application programming interfaces) общее наименование целого набора базовых функций интерфейсов программирования приложений операционных систем семейств Windows и Windows NT корпорации «Майкрософт». Является самым прямым способом… … Википедия

Win32 Thread Information Block — Esta página o sección está siendo traduc >Wikipedia Español

Win32 Thread Information Block — In computing, the Win32 Thread Information Block (TIB) is a data structure in Win32 on x86 that stores info about the currently running thread.The TIB is officially undocumented for Windows 9x. The Windows NT series DDK includes a struct NT TIB… … Wikipedia

Win32 console — is a plain text window for console applications within the system of Windows API. A Win32 console has a screen buffer and an input buffer.Win32 consoles are typically used for applications that do not need to display images. Examples include… … Wikipedia

Win32 — Win32, der Nachfolger von Win16, also die API von 32 bit Windows Versionen (9x/Me) … Universal-Lexikon

Win32 — Windows API Windows API ou WinAPI est le nom donné par Microsoft à l Interface de programmation (API) sur les systèmes d exploitation Microsoft Windows. Elle est conçue pour les langages de programmation C et C++ et est la manière la plus directe … Wikipédia en Français

API de Windows — La interfaz de programación de aplicaciones de Windows, cuyo nombre en inglés es Windows API (Windows application programming interface), es un conjunto de funciones res >Wikipedia Español

Идентификация функций в библиотеках DLL Identifying Functions in DLLs

Идентификатор функции DLL состоит из следующих элементов: The identity of a DLL function consists of the following elements:

Имя функции или порядковый номер Function name or ordinal

Имя файла DLL, в котором находится реализация Name of the DLL file in which the implementation can be found

Например, при указании функции MessageBox в библиотеке User32.dll определяется функция (MessageBox) и ее расположение (User32.dll, User32 или user32). For example, specifying the MessageBox function in the User32.dll identifies the function (MessageBox) and its location (User32.dll, User32, or user32). Программный интерфейс Microsoft Windows (API Windows) может содержать две версии для каждой функции, обрабатывающей символы и строки: версию ANSI для однобайтовых символов и версию Юникода для двухбайтовых символов. The Microsoft Windows application programming interface (Windows API) can contain two versions of each function that handles characters and strings: a 1-byte character ANSI version and a 2-byte character Unicode version. Если кодировка не указана, она определяется полем CharSet (по умолчанию ANSI). When unspecified, the character set, represented by the CharSet field, defaults to ANSI. Некоторые функции могут иметь более двух версий. Some functions can have more than two versions.

MessageBoxA — это точка входа ANSI для функции MessageBox; MessageBoxW — версия для Юникода. MessageBoxA is the ANSI entry point for the MessageBox function; MessageBoxW is the Unicode version. Чтобы получить список имен функций в конкретной библиотеке DLL, например user32.dll, можно воспользоваться различными средствами командной строки. You can list function names for a specific DLL, such as user32.dll, by running a variety of command-line tools. Например, для получения имен функций можно воспользоваться dumpbin /exports user32.dll или link /dump /exports user32.dll . For example, you can use dumpbin /exports user32.dll or link /dump /exports user32.dll to obtain function names.


Неуправляемую функцию в коде можно переименовать, при условии что новое имя функции соответствует исходной точке входа в библиотеке DLL. You can rename an unmanaged function to whatever you like within your code as long as you map the new name to the original entry point in the DLL. Инструкции по переименованию неуправляемой функции DLL в управляемом исходном коде см. в разделе Указание точки входа. For instructions on renaming an unmanaged DLL function in managed source code, see the Specifying an Entry Point.

Вызов неуправляемого кода позволяет управлять значительной частью операционной системы с помощью вызова функций в API Windows и других библиотеках DLL. Platform invoke enables you to control a significant portion of the operating system by calling functions in the Windows API and other DLLs. Наряду с API Windows существует несколько других API и библиотек DLL, для которых доступен вызов неуправляемого кода. In addition to the Windows API, there are numerous other APIs and DLLs available to you through platform invoke.

В следующей таблице описаны несколько распространенных библиотек DLL в API Windows. The following table describes several commonly used DLLs in the Windows API.

DLL DLL Описание содержимого Description of Contents
GDI32.dll GDI32.dll Функции интерфейса графических устройств (GDI) для вывода информации на устройство, например функции для рисования и управления шрифтами. Graphics Device Interface (GDI) functions for device output, such as those for drawing and font management.
Kernel32.dll Kernel32.dll Низкоуровневые функции операционной системы для управления памятью и обработки ресурсов. Low-level operating system functions for memory management and resource handling.
User32.dll User32.dll Функции управления Windows для обработки сообщений, таймеров, меню и обмена данными. Windows management functions for message handling, timers, menus, and communications.

Полную документацию по API Windows см. в разделе «Пакет SDK платформы». For complete documentation on the Windows API, see the Platform SDK. Примеры, демонстрирующие создание объявлений на основе .NET, которые используются с вызовом неуправляемого кода, см. в разделе Маршалинг данных при вызове неуправляемого кода. For examples that demonstrate how to construct .NET-based declarations to be used with platform invoke, see Marshaling Data with Platform Invoke.

Windows API Index

The following is a list of the reference content for the Windows application programming interface (API) for desktop and server applications.

Using the Windows API, you can develop applications that run successfully on all versions of Windows while taking advantage of the features and capabilities unique to each version. (Note that this was formerly called the Win32 API. The name Windows API more accurately reflects its roots in 16-bit Windows and its support on 64-bit Windows.)

User Interface

The Windows UI API create and use windows to display output, prompt for user input, and carry out the other tasks that support interaction with the user. Most applications create at least one window.

Вызываем функции Windows API (и любые другие функции, написанные на языке Си) джаваскриптом из Node.js

Со вчерашнего дня, господа, можно написать вот такой скрипт:

и, запустив его в Windows, получить желаемый результат — диалоговое окно Windows.

Это стало возможным потому, что модуль (обёртку вокруг той необыкновенно полезной библиотеки которая используется для вызова библиотек на языке Си не менее чем других языках) вчера портировали на Windows.

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

Ясное дело, что на Windows API свет клином не сошёлся: модуль можно использовать для вызова какой угодно библиотеки, написанной на языке Си или соблюдающей принятый в Си способ вызова функций (например, в Си++ это достигается директивою

Что означает этот шаг эволюции возможностей Node.js?

Напомню предсказание, сделанное мною меньше месяца назад — шестнадцатого декабря 2011 года:

В настоящее время будущее Node.js ещё не достаточно лучезарно, поскольку ещё только этот движок существует не только под Linux, под Мак и под Соляркою, но также и под Windows.

Следует ожидать мощного синергического толчка в тот момент, когда код модулей для Node.js начнут сочинять также и те разработчики на JavaScript, у которых на рабочем компьютере стоит Windows. По моим оценкам, для этого не достаточно портировать на Windows один только сам движок Node.js; потребуется также, по меньшей мере, вот что:

  • Плавная работа пакетного менеджера npm. В частности, будет необыкновенном полезным появление у разработчиков модулей возможности поставлять заранее скомпилированные модули для win32 и win64: нельзя же полагаться на то, что у каждого конечного пользователя стоят средства разработки (например, Visual Studio 2010 Express). Понятно, что и разработчики модулей должны взяться за ум, а не то даже команду npm install zip нельзя под Windows подать без того, чтобы наткнуться на симлинк внутри тарболла. (Или автор скрипта мог бы получше предусмотреть это.)
  • Появление возможности оскриптовывания произвольной системной библиотеки. (Появится, вероятно, после портирования на Windows.) Только отсюда протянется тропка к сотворению GUI.
  • Появление возможности работать с БД файлового (а не клиент-серверного) типа. (Появится, вероятно, после портирования на Windows.)

И месяца не прошло, как второй из этих трёх пунктов реализовался. Любая библиотека может быть оскриптована джаваскриптами. Соответственно, приложениям на Node.js под Windows стала доступна вся мощь системных API, а кросс-платформенные приложения могут невозбранно обращаться ко кросс-платформенным библиотекам, таская их с собою.

Первый из этих трёх пунктов (касавшийся как раз таскания библиотек с собою) не реализовался в том виде, в каком я мечтал о нём: пакетный менеджер npm всё ещё не обеспечивает возможность доставки только того скомпилированного кода, который нужен конкретной системе. Но у меня появился новый повод для оптимизма после того, как я увидел, что разработчикам модуля удалось обойти нехватку такой возможности. Их модуль содержит один и тот же код, скомпилированный под полдесятка различных платформ:
а скрипт lib/bindings.js во время вызова модуля оглядывается, соображая, куда же попал он, и подбирает нужный двоичный код:

Как видно, работает ничуть не хуже; папка «compiled» вот только занимает ≈700 килобайтов, но при нынешних объёмах дисков и скоростях сетей об этом можно позабыть.

Win32 API

  • Объекты и процессы ядра Windows
    • Многозадачность
    • Объекты ядра Windows
    • Процессы и потоки
      • Объекты ядра
      • Объекты GDI и User
  • Управление памятью в Win32
    • Организация виртуальной памяти в Windows
    • Кучи и менеджеры куч
    • Динамические хранилища
  • Обработка ошибок в Win32
    • Обработка ошибок с помощью функции GetLastError
    • Обработка ошибок с помощью функции SetErrorMode
    • Экстренное завершение приложения

Любой программист, создающий приложения Windows, должен иметь представление о процессах, происходящих в операционной системе, для создания качественных и эффективных приложений. Такие знания помогут решить многие проблемы, возникающие при разработке приложений, тесно связанных с операционной системой. В рассмотренных ранее главах мы уже изучили понятие потока и многопоточности. Эти понятия имеют непосредственное отношение к процессам операционной системы.
В данной главе мы рассмотрим, что такое объект ядра и что такое процесс ядра операционной системы. Изучим объекты GDI и User. Рассмотрим, как Windows управляет памятью и как эта операционная система обрабатывает ошибки.
Объекты и процессы ядра Windows
Сразу оговоримся, что все сказанное в этой главе относится к следующим версиям Windows: Windows 95, 98, 2000 и Windows NT, т. к. только в данных версиях была введена поддержка 32-разрядных приложений.

Примечание.
Для среды Windows 3.1 Microsoft специально разработала пакет Win32s, позволяющий с некоторыми ограничениями использовать поддержку приложений Win32.

Ядро Windows (Windows kernel) — это часть операционной системы, которая обеспечивает поддержку низкоуровневых функций, необходимых для выполнения приложений. Например, всякий раз, когда приложению нужна дополнительная память, оно обращается к ядру Windows.
Между всеми вышеперечисленными системами существуют различия в поддержке 32-разрядных приложений. В табл. 1.8 перечислены некоторые отличия, существующие между тремя операционными системами.
Таблица 1.8. Различия операционных систем при поддержке Win32 API

Windows 3.1 с поддержкой Win32

32-битная система координат

Асинхронный файловый ввод/вывод

Асинхронная модель ввода информации

На уровне Windows 3.1

Поддержка многопроцессорных материнских плат

Динамический обмен данными (DDE) по сети

Поддержка процессоров других фирм-производителей (не Intel)

Безопасность (сертификат С2)

Разделяемое адресное пространство

Поддержка TAPI (Telephone API)

Системные ресурсы для объектов User и GDI

Практически не ограничены


Итак, Win32 API (Application Programming Interface) — это интерфейс разработки 32-разрядных приложений Windows.
Многозадачность
Многозадачность (multitasking) — свойство операционной системы выполнять одновременно несколько приложений.

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

В ранних, 16-разрядных операционных системах поддерживалась так называемая кооперативная многозадачность (cooperative multitasking). Это такой вид многозадачности, когда приложение в процессе своего выполнения само «решает» передавать управление операционной системе или продолжать занимать процессорное время. При этом другие приложения, запущенные вместе с первым, просто не выполняют никаких действий. Данный вид многозадачности приводил к «зависаниям» операционной системы вместе со всеми запущенными приложениями, если «висло» приложение, захватившее ресурсы процессора.
На смену кооперативной многозадачности пришла вытесняющая многозадачность (preemptive multitasking). Вытесняющая многозадачность появилась лишь в 32-разрядных операционных системах. Данный вид многозадачности подразумевает, что управление всеми приложениями ведет операционная система. Она выделяет каждому приложению определенный квант времени процессора в зависимости от приоритета приложения (см. главу 3).
Объекты ядра Windows
Рассмотрим объекты, с которыми работает операционная система Windows.
Сразу обратим ваше внимание на то, что объекты Win32 и объекты Delphi — абсолютно разные объекты. В Win32 объекты делятся на объекты ядра (kernel) и объекты GDI и User.
Процессы и потоки
Процесс — это выполняющееся приложение Windows. Так как Windows — многозадачная операционная система, то в ней может работать сразу несколько процессов. Каждый процесс получает свое адресное пространство (размером до 4-х гигабайт). В этом пространстве хранится код приложения, его данные, а также все подключаемые библиотеки (DLL).
Сами процессы ничего не выполняют. Каждый процесс состоит из потоков (threads), которые выполняют код процесса (подробнее о потоках и об их создании см. главу 3). Любой процесс состоит как минимум из одного потока, который называется первичным или главным потоком (primary thread). Процесс может состоять из нескольких потоков, только один из которых будет главным.
Поток — это объект операционной системы, который представляет собой часть кода, находящегося внутри некоторого процесса. .
При создании процесса операционная система создает его главный поток, который может генерировать дополнительные потоки. При этом каждому потоку процесса Windows выделяет свои кванты времени процессора, в зависимости от приоритета потока.
Для работы с процессами Win32 API имеет встроенные функции, перечисленные в табл. 1.9.
Таблица 1.9. Функции Win32 API для работы с процессами

Функция Win32 АРI

Создает новый процесс и его главный поток. Используется вместо функции Windows 3.1 winExec. При помощи данной функции можно запускать приложения

Завершение выполнения процесса и всех его потоков

Возвращает псевдодескриптор текущего процесса. Настоящий дескриптор текущего процесса можно получить С ПОМОЩЬЮ функции DuplicateHandle

Функция, предназначенная для создания копии объекта ядра

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

Получение статуса окончания процесса

Возвращает класс приоритета для конкретного процесса

Возвращает содержимое структуры TStartupinfo,

которая создается во время создания процесса

Возвращает дескриптор процесса по его идентификатору

Устанавливает класс приоритета для конкретного процесса

Прекращение выполнения процесса и всех его потоков

Перевод процесса в режим ожидания ввода


Объекты ядра
Объекты ядра (kernel objects) — это процессы, потоки, события, семафоры, мьютексы и т. д., т. е. все то, с чем работает ядро Windows.
При создании объекта он существует в адресном пространстве процесса. При этом дескриптор объекта доступен породившему его процессу. Данный дескриптор не может быть использован другими приложениями для доступа к объекту разных процессов. Но эта проблема разрешима: с помощью функций Win32 API процесс имеет возможность получения собственного дескриптора (отличного от дескриптора процесса, породившего объект) для уже существующего объекта.
Например, первый процесс создает именованный или неименованный мьютекс и возвращает его дескриптор с помощью команды createMutex. Для того чтобы данный мьютекс мог использоваться другим процессом, необходимо воспользоваться функцией openMutex, которая возвращает дескриптор уже существующего мьютекса.
При обращении к объектам ядра Windows использует счетчик обращений к объекту. При создании или использовании объекта ядра приложениями счетчик увеличивается. При прекращении использования объекта ядра приложением счетчик уменьшается. Наконец, при обнулении счетчика объект ядра уничтожается.
Объекты GDI и User
В Windows 3.1 не было объектов ядра. Доступ ко всем объектам операционной системы осуществлялся с помощью дескрипторов. Объекты операционной системы делились на две группы: объекты, находящиеся в локальной памяти модулей GDI и User, и объекты, находящиеся в глобальной памяти.
Интерфейс графического устройства (Graphical Device Interface, GDI) — это часть Windows, которая управляет шрифтами, средствами печати и другими графическими системами Windows.
Когда приложение выводит что-либо на экран, оно использует службы, представляемые GDI.
Объекты GDI- это палитры, изображения, шрифты, и т. д., т.е. то, чем управляет GDI.
Средства пользовательского интерфейса (User) — это часть Windows, отвечающая за все окна, которые создаются приложениями.
Объекты User — это, в первую очередь, окна, меню.
В 16-разрядной версии Windows имелась непосредственная связь между объектом и его дескриптором. Таким образом, в данной версии Windows существовала таблица, содержащая указатели на все объекты операционной системы. Эта таблица была доступна любому приложению или динамически компонуемой библиотеке Windows. Она называлась таблицей локальных дескрипторов (Local Descriptor Table). В результате, любое приложение (или DLL) могло обращаться к объекту, используемому другим приложением.
В 32-разрядной операционной системе объекты хранятся в собственных адресных пространствах процессов, и для каждого процесса существует своя собственная таблица дескрипторов объектов. Таким образом, каждый процесс теперь работает с собственными дескрипторами объектов.
Любые дескрипторы объектов GDI или User управляются специальными подсистемами Win32 API. Для GDI — это GDI.EXE, для User — USER.EXE. Данные подсистемы выполняют создание, освобождение и проверку корректности работы дескрипторов объектов.
Управление памятью в Win32
В данном разделе мы рассмотрим все, чего еще не касались ранее о распределении памяти в Win32.
Организация виртуальной памяти в Windows
Как нам уже известно, Win32 — это 32-разрядная операционная система. Таким образом, любое приложение, запущенное в Win32, может захватывать адресное пространство в размере 4 Гбайта. Каждое приложение (процесс операционной системы) обладает своим индивидуальным адресным пространством, которое не пересекается с адресными пространствами других приложений.
Процесс выделения памяти в Windows состоит из двух последовательных этапов:
— резервирования участка памяти (виртуального адресного пространства), необходимого размера для размещения приложения и его данных. На этом этапе, физически, операционная система не выделяет оперативную память. То есть, фактически, вы можете дать команду операционной системе зарезервировать 300 Мбайт адресного пространства, и при этом не займете практически никаких системных ресурсов. Вы можете непосредственно указать операционной системе адреса, которые резервируете, или предоставить это дело операционной системе;


Примечание
Адресное пространство в Win32 резервируется блоками по 64 Кбайта. Поэтому, несмотря на ваши пожелания, при резервировании адресного пространства операционная система реально зарезервирует первый (базовый) участок адресного пространства объемом 64 Кбайта. Данное разбиение адресного пространства на блоки служит для ускорения работы ядра операционной системы.

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

Примечание
Минимальный блок памяти, с которым работает операционная система, называется страницей памяти. Размер страницы памяти различен в разных операционных системах. Например, в Windows NT — он равен 8 Кбайт, а в Windows 95/98 — 4 Кбайта.

Так как объем оперативной памяти компьютера обычно достаточно небольшой (для современных компьютеров 32-128 Мбайт), то операционная система вынуждена при нехватке основной оперативной памяти использовать так называемый файл подкачки (swap file) или виртуальную память. Таким образом, если в оперативной памяти компьютера имеется несколько приложений, запущенных одновременно, и им не хватает оперативной памяти, Windows просто выгружает те страницы памяти, к которым длительное время не было обращений. В случае, когда приложению потребуется выгруженная страница памяти, Windows освободит страницу, выгрузив страницу, к которой давно не было обращений, и загрузит на ее место требуемую, после чего вернет управление приложению.
Все вышеперечисленные действия абсолютно незаметны для приложения. Приложению не нужно заботиться о выгрузке и загрузке страниц, все эти действия выполняет операционная система.
Кучи и менеджеры куч
Серьезные приложения интенсивно используют механизмы выделения и освобождения памяти. В коммерческих приложениях широко применяются такие элементы, как: динамические массивы, строки, объекты и многое другое. При этом, создание и удаление подобных элементов происходит довольно часто, а сами элементы имеют относительно небольшой размер.
Такая работа по выделению памяти и ее освобождению, по отношению к элементам небольшого размера, является неэффективной. Во-первых, снижается производительность, т. к. резервирование адресного пространства и выделение страниц памяти происходит на уровне ядра операционной системы. Во-вторых, теряются большие объемы памяти. Например, если приложение требует выделить 256 байт под строку, операционная система реально выделяет одну страницу памяти, объемом 4 или 8 килобайт (в зависимости от операционной системы).
Для решения проблемы выделения памяти небольшим элементам приложения была введена организация по принципу кучи (heap). Куча — это достаточно большой непрерывный участок памяти, из которого выделяются небольшие блоки. Для того чтобы куча могла функционировать, в операционную систему был включен так называемый менеджер кучи. Менеджер кучи — это специальный механизм, который следит за выделением и освобождением блоков памяти. Для каждого вновь созданного процесса Windows по умолчанию создает кучу. Все кучи, которые создаются операционной системой, являются потокобезопасными. Следовательно, у программиста есть возможность обращаться к одной куче из разных потоков одновременно. Для работы с менеджером кучи Win32 API можно пользоваться следующими функциями (табл. 1.10).
Таблица 1.10. Функции Win32 API для работы с кучей

Основы программирования для Win32 API

Материал рассматривается на примере пакета Borland C++ 5.5 command line tools

Стартовая функция WinMain

Программа на Си для Windows, как и для любой другой платформы, должна обязательно содержать некоторую «стартовую» функцию, которой передается управление при запуске программы. Вообще говоря, имя такой «стартовой» функции может различаться в различных компиляторах, но исторически сложилось так (а, кроме того, имеются еще и стандарты ANSI и ISO, к которым, правда, производители коммерческих компиляторов типа Microsoft и Borland/Inprise относятся без особого трепета), что такой функцией является: У этой функции может быть до трех параметров:

  • argc — количество параметров в командной строке (включая имя программы),
  • argv — массив строк-параметров (argv[0] — имя программы),
  • env — массив строк-переменных окружения.

Многие компиляторы для Windows «понимают» такую стартовую функцию. Однако при этом они создают хотя и 32-битное, но консольное приложение. Пример 1 (example1.cpp): Компилируем: Запускаем:

При использовании стандартных библиотек (stdio, stdlib и т. п.) вам не потребуется никаких лишних телодвижений по сравнению с обычными методами написания программ на Си. Если же ваша цель — 32-битное приложение с графическим интерфейсом, то черное консольное окошко будет вас раздражать. Пример (example2.cpp):

В общем, чтобы получить нормальное приложение без каких-либо «довесков» типа консольного окошка, используется другая стартовая функция:

  • hInst — дескриптор для данного экземпляра программы,
  • hpi — в Win32 не используется (всегда NULL),
  • cmdline — командная строка,
  • ss — код состояния главного окна.

Пример (example3.cpp):

Кроме того, компилятору и компоновщику нужно сообщить о том, что вы делаете графическое приложение. Для bcc32 это опция -tW:

Если же вы соскучитесь по черному консольному окошку, его можно в любой момент создать при помощи вызова Win32 API

О типах, о функциях

Как известно, в Си есть лишь три базовых типа ( char , int , float/double ) и еще несколько их вариаций с модификаторами signed/unsigned , short/long . Однако фирме Microsoft зачем-то понадобилось описывать функции Win32 API с помощью переопределенных типов:

Кроме перечисленных простых типов, практически ни один вызов Win32 API не обходится без «штучек» с «ручками» — переменных типа handle («ручка»), которые идентифицируют некоторый объект («штучку»). Такие «ручки» принято называть дескрипторами. Реально такая переменная представляет собой всего лишь указатель на некоторую системную структуру или индекс в некоторой системной таблице.

При заполнении различных структур часто требуется указать такую «ручку» от какой-нибудь «штучки». Очень часто вместо конкретного дескриптора допустимо передавать значение NULL , означающее, что вы еще не обзавелись такой «штучкой» или собираетесь использовать «штучку» по умолчанию.

В стандартных версиях Си для функций используются два варианта соглашения о передаче параметров: соглашение языка Си (параметры функции помещаются в стек в порядке обратном их описанию, очистку стека производит вызывающая процедура) и соглашение языка Паскаль (параметры функции помещаются в стек в (прямом) порядке их описания, очистку стека производит вызываемая процедура). Для этих соглашений использовались, соответственно, модификаторы cdecl и pascal . При описании функций Win32 API используется модификатор WINAPI , а для описания пользовательских функций обратного вызова — модификатор CALLBACK . Оба этих модификатора являются переопределением специального модификатора _stdcall , соответствующего соглашению о передаче параметров, использующегося исключительно в Win32 API, — Standard Calling Convention (параметры функции помещаются в стек в порядке обратном их описанию, очистку стека производит вызываемая процедура).

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

Окно приложения может содержать строку заголовка title bar (1), строку меню menu bar (2), системное меню system menu (3), кнопку сворачивания окна minimize box (4), кнопку разворачивания окна maximize box (5), рамку изменения размеров sizing border (6), клиентскую область client area (7), горизонтальную и вертикальную полосы прокрутки scroll bars (8):

Меню, строка заголовка с системными кнопками, системное меню, рамка изменения размеров и полосы прокрутки относятся к области окна, называемой неклиентской областью (non-client area). С неклиентской областью Windows справляется сама, а вот за содержимое и обслуживание клиентской области отвечает приложение.

Кроме главного окна, приложение может использовать еще и другие типы окон: управляющие элементы (controls), диалоговые окна (dialog boxes), окна-сообщения (message boxes). Управляющий элемент — окно, непосредственно обеспечивающее тот или иной способ ввода информации пользователем. К управляющим элементам относятся: кнопки, поля ввода, списки, полосы прокрутки и т.п. Управляющие элементы обычно не болтаются сами по себе, а проживают в каком-либо диалоговом окне. Диалоговое окно — это временное окно, напичканное управляющими элементами, обычно использующееся для получения дополнительной информации от пользователя. Диалоговые окна бывают модальные (modal) и немодальные (modeless). Модальное диалоговое окно требует, чтобы пользователь обязательно ввел обозначенную в окне информацию и закрыл окно прежде, чем приложение продолжит работу. Немодальное диалоговое окно позволяет пользователю, не закрывая диалогового окна, переключаться на другие окна этого приложения. Окно-сообщение — это диалоговое окно предопределенного системой формата, предназначенное для вывода небольшого текстового сообщения с одной или несколькими кнопками. Пример такого окна показан в Примере 3.

В отличие от традиционного программирования на основе линейных алгоритмов, программы для Windows строятся по принципам событийно-управляемого программирования (event-driven programming) — стиля программирования, при котором поведение компонента системы определяется набором возможных внешних событий и ответных реакций компонента на них. Такими компонентами в Windows являются окна. С каждым окном в Windows связана определенная функция обработки событий. События для окон называются сообщениями. Сообщение относится к тому или иному типу, идентифицируемому определенным кодом (32-битным целым числом), и сопровождается парой 32-битных параметров ( WPARAM и LPARAM ), интерпретация которых зависит от типа сообщения. В заголовочном файле windows.h для кодов сообщений определены константы с интуитивно понятными именами:

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

Для стандартных управляющих элементов (библиотека Common Controls Library — COMCTL32.DLL) в Windows имеются предопределенные обработчики событий, которые при наступлении интересных событий сообщают всяческую полезную информацию окну, содержащему этот управляющий элемент. Стандартная библиотека Common Dialog Box Library (COMDLG32.DLL) содержит несколько готовых весьма полезных диалоговых окон с обработчиками: диалоги выбора файла, настроек печати, выбора шрифта, выбора цвета и др. Кроме того, любая среда разработки (VisualBasic, Delphi, VisualC++ и т.п.) навязывает разработчику дополнительный набор готовых управляющих элементов и диалогов — иногда достаточно удобных, иногда не очень.

Структура программы

Программа для Win32 обычно состоит из следующих блоков:

Каждое окно принадлежит определенному классу окон. Окна одного класса имеют схожий вид, обслуживаются общей процедурой обработки событий, имеют одинаковые иконки и меню. Обычно каждое приложение создает для главного окна программы свой класс. Если приложению требуются дополнительные нестандартные окна, оно регистрирует другие классы. Стандартные диалоги и управляющие элементы принадлежат к предопределенным классам окон, для них не надо регистрировать новые классы. Чтобы определить новый класс окон, надо заполнить структуру WNDCLASS , содержащую следующие поля:

  • UINT style — стиль (поведение) класса окон,
  • WNDPROC lpfnWndProc — процедура обработки событий окна,
  • int cbClsExtra — размер дополнительной памяти в системной структуре класса для данных пользователя,
  • int cbWndExtra — размер дополнительной памяти в системной структуре окна для данных пользователя,
  • HINSTANCE hInstance — дескриптор модуля (экземпляра программы), в котором реализована процедура обработки,
  • HICON hIcon — дескриптор иконки окна,
  • HCURSOR hCursor — дескриптор курсора мыши для окна,
  • HBRUSH hbrBackground — дескриптор «кисточки» для закрашивания фона окна,
  • LPCSTR lpszMenuName — имя ресурса, содержащего меню окна,
  • LPCSTR lpszClassName — имя класса.

Класс регистрируется при помощи функции: При успешном завершении функция возвращает целочисленный код, соответствующий строке-имени класса в общесистемной таблице строк (такой код называется атомом). При ошибке возвращается 0.

Для создания окна вызывается функция:

Вместо параметров x, y, nWindth, nHeight допустимо передавать константу CW_USEDEFAULT , позволяющую операционной системе задать эти числа по ее усмотрению.

Интерпретация кода стиля определяется классом окна. Стиль определяет не только оформление окна, но и его поведение. Общие для всех классов константы стилей (при необходимости объединяются операцией побитовое ИЛИ):

  • WS_DISABLED — при создании окно заблокировано (не может получать реакцию от пользователя);
  • WS_VISIBLE — при создании окно сразу же отображается (не надо вызывать ShowWindow );
  • WS_CAPTION — у окна есть строка заголовка;
  • WS_SYSMENU — у окна есть системное меню;
  • WS_MAXIMIZEBOX — у окна есть кнопка разворачивания;
  • WS_MINIMIZEBOX — у окна есть кнопка сворачивания;
  • WS_SIZEBOX или WS_THICKFRAME — у окна есть рамка изменения размеров;
  • WS_BORDER — у окна есть рамка (не подразумевает изменение размеров);
  • WS_HSCROLL или WS_VSCROLL — у окна есть горизонтальная или вертикальная прокрутка;
  • WS_OVERLAPPED или WS_TILED — «перекрываемое» окно — обычное окно с рамкой и строкой заголовка;
  • WS_POPUP — «всплывающее» окно;
  • WS_OVERLAPPEDWINDOW — «перекрываемое» окно с системным меню, кнопками сворачивания/разворачивания, рамкой изменения размеров, короче, типичный стиль для главного окна приложения.

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

После создания окна неплохо бы сделать его видимым (отобразить), если только оно не создано со стилем WS_VISIBLE: Второй параметр этой функции — код состояния отображения окна. В качестве этого кода можно взять значение четвертого параметра, с которым была запущена функция WinMain . Другие возможные значения этого параметра:

  • SW_SHOW — отобразить и активировать окно;
  • SW_HIDE — скрыть окно;
  • SW_MAXIMIZE — развернуть окно на весь экран;
  • SW_RESTORE — активировать окно и отобразить его в размерах по умолчанию;
  • SW_MINIMIZE — свернуть окно.

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

Если клиентская область главного окна приложения содержит объекты, прорисовываемые по сообщению WM_PAINT , имеет смысл прорисовать эти объекты сразу после отображения главного окна на экране. Функция UpdateWindow непосредственно вызывает процедуру обработки событий указанного окна с сообщением WM_PAINT (минуя очередь сообщений приложения):

Windows использует два способа доставки сообщений процедуре обработки событий окна:

  • непосредственный вызов процедуры обработки событий (внеочередные или неоткладываемые сообщенияnonqueued messages);
  • помещение сообщения в связанный с данным приложением буфер типа FIFO, называемый очередью сообщенийmessage queue (откладываемые сообщенияqueued messages).

К внеочередным сообщениям относятся те сообщения, которые непосредственно влияют на окно, например, сообщение активации окна WM_ACTIVATE и т.п. Кроме того, вне очереди сообщений обрабатываются сообщения, сгенерированные различными вызовами Win32 API, такими как SetWindowPos , UpdateWindow , SendMessage , SendDlgItemMessage .

К откладываемым сообщениям относятся сообщения, связанные с реакцией пользователя: нажатие клавиш на клавиатуре, движение мышки и клики. Чтобы извлечь сообщение из очереди, программа вызывает функцию Эта функция возвращает FALSE , если получено сообщение WM_QUIT , и TRUE в противном случае. Очевидно, что условием продолжения цикла обработки событий является результат этой функции. Если приложение хочет завершить свою работу, оно посылает само себе сообщение WM_QUIT при помощи функции Ее параметр — статус выхода приложения. Обычно эта функция вызывается в ответ на сообщение об уничтожении окна WM_DESTROY .

После извлечения сообщения из очереди следует вызвать функцию TranslateMessage , переводящую сообщения от нажатых клавиш в удобоваримый вид, а затем DispatchMessage , которая определяет предназначенное этому сообщению окно и вызывает соответствующую процедуру обработки событий. Результат возврата соответствует значению, которое вернула процедура обработки событий (обычно никому не нужен).

Процедура обработки сообщений окна должна быть объявлена по следующему прототипу: Значения параметров: hw — дескриптор окна, которому предназначено сообщение, msg — код сообщения, wp и lp — 32-битные параметры сообщения, интерпретация которых зависит от кода сообщения. Зачастую старший/младший байт или старшее/младшее слово параметров сообщения несут независимый смысл, тогда удобно использовать определенные в windows.h макросы:

Например, сообщение WM_COMMAND посылается окну в трех случаях:

  1. пользователь выбрал какую-либо команду меню;
  2. пользователь нажал «горячую» клавишу (accelerator);
  3. в дочернем окне произошло определенное событие.

При этом параметры сообщения интерпретируются следующим образом. Старшее слово параметра WPARAM содержит: 0 в первом случае, 1 во втором случае и код события в третьем случае. Младшее слово WPARAM содержит целочисленный идентификатор пункта меню, «горячей» клавиши или дочернего управляющего элемента. Параметр LPARAM в первых двух случаях содержит NULL , а в третьем случае — дескриптор окна управляющего элемента.

Процедура обработки событий должна вернуть определенное 32-битное значение, интерпретация которого также зависит от типа сообщения. В большинстве случаев, если сообщение успешно обработано, процедура возвращает значение 0.

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

Все описанное в данном параграфе суммируется в примере 4 (example4.cpp):

Приведенный пример создает окно с кнопкой «My button», при нажатии на которую вылезает окно-сообщение:

Ресурсы

Ресурсы — это бинарные данные, добавляемые в исполняемый файл при компоновке программы. К стандартным ресурсам относятся: иконки, курсоры, меню, диалоги, растровые изображения (BMP), векторные изображения (EMF), шрифты, таблицы горячих клавиш, таблицы строк, информация о версии программы или модуля. В процессе разработки программы ресурсы описывают в отдельном текстовом файле — файле описания ресурсов (расширение .rc), — а затем при помощи компилятора ресурсов переводят в бинарный вид и добавляют в исполняемый файл на этапе компоновки исполняемого файла. Использование ресурсов значительно облегчает работу программиста по визуализации графических примитивов интерфейса программы.

Файл описания ресурсов состоит из операторов, объединяемых в блоки. Один оператор занимает одну строку файла. Допускается использовать комментарии, определяемые так же, как в программе на языке Си. Файл описания ресурсов перед компиляцией так же обрабатывается препроцессором, поэтому в нем можно использовать директивы препроцессора ( #include , #define , . ) и макроопределения. В сложных «блочных» описаниях ресурсов вместо ключевых слов BEGIN и END можно использовать < и >, соответственно.

Иконки, картинки и курсоры мыши можно описать двумя способами (квадратные скобки не являются частью синтаксиса оператора и означают необязательный элемент): Здесь nameID — численный или строковой идентификатор; RESOURCETYPE — ключевое слово, обозначающее тип ресурса: ICON , BITMAP или CURSOR ; load-option и mem-option — всякие неинтересные в данный момент опции, которые можно спокойно пропустить; filename — имя файла, содержащее соответствующий ресурс.

Эти ресурсы можно внедрить в виде шестнадцатеричных кодов непосредственно в файл ресурсов:

Следует отметить, что первая иконка в ресурсах будет использоваться «Проводником» как иконка исполняемого файла.

Меню описывается следующим образом: Здесь item-definitions — один из трех операторов: Параметры операторов имеют следующий смысл: text — текст пункта меню или подменю (может содержать комбинации \t — табуляция, \a — выравнивание по правому краю, & — следующий символ подчеркивается, обозначает «горячую» клавишу для указанного пункта меню); result — целочисленный идентификатор пункта меню, посылаемый окну-владельцу через сообщение WM_COMMAND при выборе этого пункта меню; optionlist — необязательный список опций, разделенных запятой или пробелом:

  • CHECKED — рядом с пунктом меню отображается галочка,
  • GRAYED — пункт меню неактивен (не может быть выбран) и отображается серым цветом и др.

Доступ к ресурсам, скомпонованным с исполняемым файлом, можно получить при помощи следующих функций: Первый параметр этих функций — дескриптор экземпляра программы, второй — идентификатор соответствующего ресурса. Если ресурс идентифицируется не именем, а числом, то следует использовать макрос, объявленный в windows.h : Например:

Для закрепления полученных сведений, давайте добавим к примеру 4 какую-нибудь иконку и такое меню:

Для этого создаем файл ресурсов (example4a.rc):

Для перевода файла описания ресурсов в бинарный вид используется компилятор ресурсов Borland Resource Compiler: В результате получается файл example4a.res, который потребуется в процессе компоновки.

В примере 4 надо изменить строки на

Чтобы программа не была такой скучной, изменим обработчик сообщения WM_COMMAND : В результате при выборе того или иного пункта меню выводится окно-сообщение с кодом команды.

Обратите внимание: среди команд меню не используется код 1, который отведен кнопке «My button». Это типичная практика назначать всем дочерним элементам окна и командам меню разные численные идентификаторы, что облегчает обработку сообщения WM_COMMAND .

Компиляция и компоновка сложных проектов

Теперь компоновка программы будет более сложной, поэтому bcc32 с этой задачей не справится. В этом примере компилятор будет использоваться только для компилирования (запускаем с ключом ): В результате получаем объектный файл example4a.obj.

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

  • options — о допустимых опциях можно узнать, запустив компоновщик без параметров. Нам потребуются:
    • -aa — тип приложения «графическое для Win32» (другие варианты: -ap — консольное приложение, -ad — драйвер);
    • -Tpe — формат выходного файла «.EXE» (другой вариант: -Tpd — «.DLL»);
    • -L путь — путь для поиска библиотек и объектных файлов (обычно: -Lc:\bcc55\lib).
  • objfiles — список объектных файлов, из которых составляется программа, разделенных пробелом или знаком «+». Этот список должен начинаться с борландовского инициализационного объектного файла: c0w32.obj — для графического приложения под Win32 или c0x32.obj — для консольного приложения.
  • exefile — имя исполняемого файла, который получится в результате компоновки.
  • mapfile — имя файла, который после компиляции будет содержать карту сегментов вашей программы (оно вам надо? если нет, здесь делаем «пусто», а в опциях указываем -x, чтобы компоновщик не замусоривал рабочий каталог этой фигней).
  • libfiles — список библиотек, в которых надо искать не определенные в программе функции, разделенных пробелом или знаком «+». Как минимум, надо указать import32.lib, которая содержит код подключения к стандартным библиотекам Win32 API: kernel32.dll, user32.dll, gdi32.dll, advapi32.dll и др. Если вы используете какие-либо функции стандартных библиотек языка Си (stdlib, stdio, . ), надо указать еще cw32.lib.
  • deffile — файл параметров модуля (module definition file). Это текстовый файл, в котором определяются различные настройки компилируемой программы типа: размеры сегментов стека, кода, данных, «заглушка» (что будет происходить при попытке запуска программы в DOS) и проч. Если не указывать этот файл, компоновщик выберет вполне приличные для большинства случаев параметры по умолчанию.
  • resfiles — файлы ресурсов (разделяются пробелом или знаком «+»).

Компоновщик ilink32 умеет работать в пошаговом (инкрементирующем) режиме incremental linking, при этом он создает несколько файлов состояний (*.IL?). При последующих попытках компиляции он использует их, так что процесс компиляции занимает меньше времени. Чтобы отключить пошаговую компиляцию и не замусоривать рабочий каталог этими файлами, следует указать опцию -Gn. Например, если при отключенной пошаговой компиляции программа компилируется 8 секунд, то первая компиляция в пошаговом режиме займет 25 секунд, а все последующие — не более 2 секунд.

Итак, компонуем модифицированный пример 4:

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

Эта же иконка отображается в строке заголовка главного окна программы. Под строкой заголовка отображается созданное нами меню. При выборе любой команды меню появляется окно-сообщение с кодом команды. При выборе команды «Exit» программа завершается.

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

Сценарий компиляции должен содержать как минимум одно правило. Строка с командами обязательно должна начинаться с отступа табуляцией. В качестве имени правила обычно выступает имя файла, который получится в результате выполнения команд в теле правила. Зависимости — необязательный список имен файлов, разделенных пробелами, от которых зависит данное правило. Если при вызове make окажется, что хотя бы один файл из этого списка новее, чем файл-результат правила, то выполняются все команды из этого правила. В качестве зависимостей могут указываться имена файлов-названия других правил. Тогда make будет выполнять рекурсивную проверку зависимостей. Make не выполняет команды из правила, если все файлы-зависимости старее файла-результата.

Если не указывать в качестве файлов-зависимостей example4a.rc и example4a.cpp, то make не станет ничего делать, когда файл example4a.exe уже существует. Тем не менее, приведенный пример — не совсем удачный сценарий компиляции. Если мы изменим только файл ресурсов, make все равно будет перекомпилировать исходный текст. Если мы изменим только исходный текст, make будет перекомпилировать еще и ресурсы. С учетом этого замечания, более удачным будет следующий сценарий:

Если в командной строке make не указано иное, то make пытается выполнить первое правило из сценария. Именно поэтому первым правилом стоит example4a.exe — результат, который мы хотим получить после компиляции всего проекта.

Если написать подходящий сценарий компиляции, то для компиляции вашего проекта придется набирать лишь команду:

Диалоговые окна

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

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

Для создания модального диалога используется функция DialogBox , а для создания немодального диалога — CreateDialog :

Параметры: hInst — дескриптор экземпляра программы (модуля, в котором находится шаблон); template — имя ресурса, описывающего диалог; parent — дескриптор родительского окна; DlgFunc — диалоговая функция следующего формата: Параметры диалоговой функции такие же, как у обычной функции обработки событий. Отличие этой функции — она вызывается из предопределенной функции обработки событий для диалоговых окон. Она должна вернуть значение TRUE , если обработала переданное ей сообщение, или FALSE в противном случае. Она ни в коем случае не должна сама вызывать DefWindowProc .

При создании диалогового окна диалоговая процедура получает сообщение WM_INITDIALOG . Если в ответ на это сообщение процедура возвращает FALSE , диалог не будет создан: функция DialogBox вернет значение -1 , а CreateDialog — NULL .

Модальное диалоговое окно блокирует указанное в качестве родительского окно и появляется поверх него (вне зависимости от стиля WS_VISIBLE ). Приложение закрывает модальное диалоговое окно при помощи функции Приложение должно вызвать эту функцию из диалоговой процедуры в ответ на сообщение от кнопок «OK», «Cancel» или команды «Close» из системного меню диалога. Параметр result передается программе как результат возврата из функции DialogBox .

Немодальное диалоговое окно появляется поверх указанного в качестве родительского окна, но не блокирует его. Диалоговое окно остается поверх родительского окна, даже если оно неактивно. Программа сама отвечает за отображение/сокрытие окна (с помощью стиля WS_VISIBLE и функции ShowWindow ). Сообщения для немодального диалогового окна оказываются в основной очереди сообщений программы. Чтобы эти сообщения были корректно обработаны, следует включить в цикл обработки сообщений вызов функции:

Если эта функция вернула TRUE , то сообщение обработано и его не следует передавать функциям TranslateMessage и DispatchMessage .

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

Шаблон диалогового окна в ресурсах задается следующим образом:

В начале блока описания диалога задается: nameID — целочисленный или строковой идентификатор ресурса, x , y — координаты диалога на экране (или относительно родительского окна), width , height — размер диалога. Координаты и размеры диалога и всех элементов внутри него задаются в диалоговых единицах (dialog units). Одна горизонтальная диалоговая единица соответствует 1/4 средней ширины символа в системном шрифте. Одна вертикальная диалоговая единица соответствует 1/8 средней высоты символа в системном шрифте.

После заголовка блока идет ряд необязательных операторов-параметров диалога (property-statements) в любом порядке:

В качестве стиля диалога можно применять все перечисленные для обычных окон стили. Обычно выбирают: WS_POPUP , WS_SYSMENU , WS_CAPTION , а также WS_BORDER для немодального диалога и DS_MODALFRAME — для модального. Кроме того, можно использовать DS_SETFOREGROUND , чтобы при отображении диалога перевести его на передний план, даже если его родительское окно неактивно.

В теле шаблона (control-statements) перечисляются составляющие его управляющие элементы. Один из возможных вариантов оператора: Здесь text — текст управляющего элемента (заголовок), id — целочисленный идентификатор элемента 0. 65535 (внутри одного диалога идентификаторы всех элементов должны различаться), class — имя класса, к которому принадлежит управляющий элемент, style — стиль управляющего элемента, x , y , width , height — положение и размер диалогового элемента относительно клиентской области диалога в диалоговых единицах.

Вот пример диалога, содержащего простое статическое текстовое поле и кнопку «OK»:

Добавим этот диалог к ресурсам примера 4. В текст программы добавим две глобальных переменных:

Присвоим переменной h значение дескриптора экземпляра программы в самом начале функции WinMain . Это значение нам потребуется для вызова функции DialogBox .

Изменим обработчик сообщения WM_COMMAND следующим образом:

Теперь в текст программы необходимо добавить диалоговую процедуру:

При создании диалога вызывается процедура SetDlgItemText , меняющая содержание текстового поля в диалоге (элемент с , генерирующая сообщение WM_COMMAND с >Функция DlgProc должна быть определена или описана до ссылки на нее в вызове DialogBox .

Управляющие элементы

Управляющие элементы, как и другие окна, принадлежат тому или иному классу окон. Windows предоставляет несколько предопределенных классов управляющих элементов. Программа может создавать управляющие элементы поштучно при помощи функции CreateWindow или оптом, загружая их вместе с шаблоном диалога из своих ресурсов. Управляющие элементы — это всегда дочерние окна. Управляющие элементы при возникновении некоторых событий, связанных с реакцией пользователя, посылают своему родительскому окну сообщения-оповещения (notification messages) WM_COMMAND или WM_NOTIFY .

Как и любое другое окно, управляющий элемент может быть скрыт или отображен при помощи функции ShowWindow . Аналогично, управляющий элемент может быть блокирован или разблокирован при помощи функции: В качестве второго параметра передается флаг TRUE (разблокировать) или FALSE (блокировать). Функция возвращает значение TRUE , если перед ее вызовом окно было заблокировано. Узнать текущий статус блокирования окна можно при помощи функции: которая возвращает значение TRUE , если окно разблокировано.

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

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

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

Для управляющих элементов внутри диалогов специальный смысл имеют стили WS_TABSTOP и WS_GROUP . Если в диалоге имеются управляющие элементы со стилем WS_TABSTOP , то при нажатии пользователем на клавишу [Tab] (или [Shift]+[Tab]), текущий активный элемент диалога будет терять фокус и передавать его следующему за ним (или предыдущему) ближайшему элементу со стилем WS_TABSTOP . С помощью стиля WS_GROUP элементы диалога можно объединять в группы. Группа элементов начинается с элемента со стилем WS_GROUP и заканчивается элементом, после которого идет элемент со стилем WS_GROUP , или последним элементом в диалоге. Внутри группы только первый элемент должен иметь стиль WS_GROUP . Windows допускает перемещение внутри группы при помощи клавиш-стрелок.

Классы предопределенных управляющих элементов: «STATIC» Статический управляющий элемент представляет собой текстовую метку или прямоугольник. Не предоставляет пользователю ни средств ввода, ни вывода. Примеры:

Соответствующее описание ресурсов: Для текстовых статиков со стилями SS_LEFT , SS_RIGHT или SS_CENTER существуют более простые операторы объявления ресурсов: Чтобы поменять текст статика ему можно послать сообщение WM_SETTEXT ( wp=0 ; lp=(LPARAM)(LPCSTR)lpsz — адрес строки) или использовать функции: Чтобы сменить иконку или картинку нетекстового статика, надо послать ему сообщение STM_SETIMAGE ( wp=(WPARAM)fImageType — тип изображения: IMAGE_BITMAP или IMAGE_ICON ; lp=(LPARAM)(HANDLE)hImage — дескриптор иконки или картинки). «BUTTON» Кнопка — это небольшое прямоугольное дочернее окно, обычно имеющее два состояния: нажато/отпущено или включено/выключено. Пользователь меняет состояние этого элемента щелчком мыши. К этому классу относятся: кнопки-«давилки» (push buttons), кнопки-«галочки» (check boxes), «радио»-кнопки (radio buttons) и специальный тип групповых рамочек (group boxes). Примеры:

Соответствующее описание ресурсов: Для кнопок существуют более простые операторы объявления ресурсов: Разница между стилями BS_xxx и BS_AUTOxxx заключается в том, что при щелчке по AUTO -кнопкам Windows сама автоматически переключает их состояние. Для не AUTO -кнопок это надо делать вручную в диалоговой процедуре, послав сообщение BM_SETCHECK ( wp=(WPARAM)fCheck — флаг: BST_UNCHECKED , BST_CHECKED или BST_INDETERMINATE (для BS_3STATE -кнопок); lp=0 ) или при помощи функций: Автоматические радио-кнопки должны быть объединены в группу при помощи стиля WS_GROUP , чтобы Windows корректно их обрабатывала.
Проверить состояние кнопки можно, послав ей сообщение BM_GETCHECK ( wp=0; lp=0 ) или вызовом функции: При щелчке мыши по кнопке она присылает родительскому диалогу сообщение-оповещение WM_COMMAND ( HIWORD(wp)=BN_CLICKED; LOWORD(wp)=(int) >). «EDIT» Поле редактирования предназначено для ввода пользователем текста с клавиатуры. Щелчком мыши внутри элемента пользователь передает этому элементу фокус ввода (input focus). При этом внутри элемента появляется текстовый курсор — мигающая каретка. Пользователь может использовать мышь для перемещения каретки по полю редактирования и выделению текста в этом поле. Примеры:

Соответствующее описание ресурсов: Стиль ES_WANTRETURN означает, что кнопка [Enter] будет обрабатываться самим элементом, а не передаваться диалогу. Благодаря этому стилю оказался возможен переход на новую строчку для предложения «Она съела кусок. » (на картинке).
По умолчанию текстовые поля позволяют вводить столько текста, сколько может отобразиться в рамках поля. Чтобы предоставить пользователю возможность ввести больше текста, надо использовать стиль ES_AUTOHSCROLL (и ES_AUTOVSCROLL для многострочных полей).
Для текстовых полей существует более простой оператор объявления ресурсов: Чтобы поменять содержимое текстового поля, программа вызывает функцию SetDlgItemText . Чтобы получить текущее содержимое текстового поля, используется функция: Чтобы узнать размер строки в текстовом поле, надо послать элементу сообщение WM_GETTEXTLENGTH ( wp=0; lp=0 ).
Текстовое поле посылает родительскому диалогу следующие сообщения-оповещения WM_COMMAND ( LOWORD(wp)=(int) >):

  • HIWORD(wp)=EN_KILLFOCUS — текстовое поле потеряло фокус (фокус передан другому элементу диалога);
  • HIWORD(wp)=EN_SETFOCUS — текстовое поле получило фокус;
  • HIWORD(wp)=EN_CHANGE — пользователь изменил текст в поле;
  • HIWORD(wp)=EN_ERRSPACE — закончилось место, отведенное под текстовый буфер управляющего элемента.

«LISTBOX» Окно-список используется для отображения списка имен (например, имен файлов). Пользователь может, просматривая список, выделить один или несколько элементов щелчком мыши. При выделении того или иного элемента списка, он подсвечивается, а родительскому окну посылается сообщение-оповещение. Для очень больших списков могут использоваться полосы прокрутки. Примеры:

Соответствующее описание ресурсов: или в короткой форме: Для добавления элемента к списку следует послать ему сообщение LB_ADDSTRING ( wp=0; lp=(LPARAM)(LPCSTR)lpsz — строка для добавления). Для того, чтобы заполнить один из списков, показанных на рисунке, в обработчик сообщения WM_INITDIALOG в диалоговую процедуру был вставлен такой фрагмент: Кроме этого, список «понимает» следующие сообщения:

  • LB_DELETESTRING ( wp=(WPARAM)index; lp=0 ) — удалить элемент с указанным номером;
  • LB_INSERTSTRING ( wp=(WPARAM)index; lp=(LPARAM)(LPCSTR)lpsz ) — вставить указанную строку в список как элемент с индексом index;
  • LB_FINDSTRING ( wp=(WPARAM)indexStart; lp=(LPARAM)(LPTSTR)lpszFind ) — найти элемент, содержащий указанную строку (поиск ведется, начиная с элемента indexStart), результат сообщения — номер элемента, удовлетворяющего критерию, или LB_ERR ;
  • LB_GETCOUNT ( wp=0; lp=0 ) — количество элементов в списке;
  • LB_GETCURSEL ( wp=0; lp=0 ) — выделенный элемент в списке;
  • LB_RESETCONTENT ( wp=0; lp=0 ) — удалить все элементы из списка.

Окно-список посылает родительскому диалогу следующие сообщения-оповещения WM_COMMAND ( LOWORD(wp)=(int) >):

  • HIWORD(wp)=LBN_DBLCLK — пользователь дважды щелкнул мышью по списку;
  • HIWORD(wp)=LBN_SELCHANGE — пользователь выделил другой элемент в списке (или отменил выделение).

«COMBOBOX» Комбобокс — это помесь поля редактирования с окном-списком. Этот элемент содержит поле редактирование и список, который может отображаться все время либо «выпадать» при нажатии на кнопку рядом с полем редактирования. Есть три основных типа комбобоксов:

  • «выпадающий» комбобокс ( CBS_DROPDOWN ) содержит поле редактирования и «выпадающий» список;
  • «выпадающий» список ( CBS_DROPDOWNLIST ) не содержит поля для изменения текста;
  • простой комбобокс ( CBS_SIMPLE ) содержит поле редактирования и обычный список.

Обратите внимание, что в ресурсах значение высоты элемента определяет размер поля редактирования вместе с «выпавшим» списком по вертикали. Короткий вариант этого же объявления ресурсов: Для работы с комбобоксами существуют сообщения, аналогичные списковым: CB_ADDSTRING , CB_DELETESTRING , CB_INSERTSTRING , CB_FINDSTRING , CB_GETCOUNT , CB_GETCURSEL , CB_RESETCONTENT .
Комбобокс посылает родительскому диалогу сообщение оповещение WM_COMMAND со следующими кодами оповещения:

  • CBN_SELCHANGE , когда пользователь выделяет другую строку в комбобоксе (бывает полезным для простых комбобоксов);
  • CBN_SELENDOK , когда пользователь выбрал элемент в выпадающем списке и щелкнул мышкой по выделению (подтвердил выделение), для простых комбобоксов посылается перед каждым CBN_SELCHANGE ;
  • CBN_SELENDCANCEL , когда пользователь закрыл выпадающий список, так и не выбрав никакой элемент;
  • CBN_DROPDOWN , когда открывается выпадающий список;
  • CBN_CLOSEUP , когда выпадающий список был закрыт по той или иной причине.

Кроме предопределенных управляющих элементов, Windows предоставляет еще набор стандартных управляющих элементов посредством библиотеки Common Controls Library (COMCTL32.DLL). Чтобы воспользоваться ей, в тест программы надо включить заголовочный файл commctrl.h и добавить в блок инициализации программы вызов функции:

Управляющие элементы из этой библиотеки, как правило, посылают сообщения-оповещения родительскому диалогу через сообщение WM_NOTIFY ( wp=(int) > — указатель на структуру со специльными параметрами сообщения-оповещения).

Классы управляющих элементов из Common Controls Library: List View Controls ( WC_LISTVIEW ) Элемент просмотра списков — это окно отображающее совокупность элементов. Каждый элемент может быть представлен текстовой меткой и (необязательно) иконкой. Типичный пример использования этого элемента — программа «Проводник». Содержимое того или иного каталога представляется в виде элемента просмотра списков. Есть четыре основных стиля для этого элемента:

  • крупные иконки — стиль LVS_ICON ;
  • мелкие иконки — стиль LVS_SMALLICON ;
  • список — стиль LVS_LIST ;
  • таблица — стиль LVS_REPORT .

Приведенные в примере списки заполнялись в диалоговой процедуре при инициализации диалога ( WM_INITDIALOG ): Status Windows ( STATUSCLASSNAME ) Поле статуса — это горизонтальное окно в нижней части родительского окна, которое программа обычно использует для отображения каких-либо характеристик, параметров или небольших текстовых сообщений.

В примере 4б можно заменить статическое текстовое поле на поле статуса: При создании поля статуса не требуется указывать ни размер, ни позицию окна, Windows сама выберет эти параметры подходящим образом. Для создания поля статуса можно использовать специальную функцию: С помощью сообщения SB_SETPARTS поле статуса можно разбить на части ( wp=(int)nParts — количество частей; lp=(LPARAM)(int*)widths — указатель на массив размеров частей). В таком случае текст для каждой части поля статуса задается сообщением SB_SETTEXT ( wp=(int)iPart — номер части; lp=(LPARAM)(LPSTR)lpszText — текстовая строка). Пример: Up-Down Controls ( UPDOWN_CLASS ) Управляющий элемент «up-down» представляет собой пару небольших кнопок-стрелок, нажимая которые, пользователь увеличивает или уменьшает значение. Этот элемент, как правило, связывается с элементом-компаньоном (buddy window), обычно реализованным в виде поля редактирования. Для пользователя элемент «up-down» и его компаньон представляются единым управляющим элементом.
Если при создании элемента «up-down» указать стиль UDS_AUTOBUDDY , то компаньоном будет назначен предыдущий управляющий элемент диалога. Программа может также передать дескриптор окна-компаньона при помощи сообщения UDM_SETBUDDY ( wp=(WPARAM)(HWND)hwndBuddy — дескриптор окна-компаньона; lp=0 ). Если элементу «up-down» назначить стиль UDS_SETBUDDYINT , то он будет автоматически менять текст окна-компаньона, представляющий числовое значение.
Другой способ создать элемент «up-down» — использовать функцию Progress Bars ( PROGRESS_CLASS ) Полоса прогресса — это окно, которое программа может использовать для индикации состояния выполнения какой-либо длительной операции. Окно представляет собой прямоугольник, заполняемый системным цветом слева направо.
Каждый раз, когда приложение посылает этому окну сообщение PBM_STEPIT ( wp=0; lp=0 ), заполнение полосы прогресса продвигается дальше вправо на некоторое значение. Tooltip Controls ( TOOLTIPS_CLASS ) Окно-подсказка — всплывающее окно, содержащее строку описательной информации о том или ином элементе интерфейса программы. Таким элементом интерфейса может быть конкретное окно (управляющий элемент) или прямоугольный участок клиентской области какого-либо окна. Большую часть времени подсказка скрыта. Она появляется, когда пользователь задерживат курсор мыши над тем или иным элементом интерфейса программы более, чем на полсекунды. Подсказка скрывается, когда пользователь кликает мышью или уводит курсор с этого элемента. Одно окно-подсказка может обслуживать любое количество элементов интерфейса. Чтобы назначить тому или иному элементу интерфейса программы подсказку, надо окну-подсказке послать сообщение TTM_ADDTOOL ( wp=0; lp=(LPARAM)(TOOLINFO*)lpti — указатель на структуру, содержащую информацию об элементе). Пример:
Property Sheets & Tab Controls Элементы вкладки свойств и переключатели вкладок обычно используются совместно. Пример использования вкладок:

Каждая вкладка содержит свой набор управляющих элементов и может задаваться в ресурсах так же, как и отдельный диалог: Для создания диалога с вкладками используется функция PropertySheet , перед вызовом которой надо заполнить соответствующие системные структуры: Для каждой вкладки может быть своя диалоговая процедура, а может быть общая для всех вкладок (как в этом примере). Trackbars ( TRACKBAR_CLASS ) Ползунок (бегунок) используется, если от пользователя требуется получить дискретное значение из определенного диапазона. Маркер ползунка пермещается на заданное программой значение. Пример ползунка показан на первой вкладке предыдущего примера. Ползунки бывают горизонтальные ( TBS_HORZ ) или вертикальные ( TBS_VERT ). Диапазон значений ползунка задается сообщением TBM_SETRANGE ( wp=(BOOL)fRedraw — перерисовать маркер после изменения диапазона; lp=MAKELONG(lMinimum,lMaximum) — диапазон значений: младшее слово — минимальное значение, старшее слово — максимальное значение). Переместить ползунок можно при помощи сообщения TBM_SETPOS ( wp=TRUE ; lp=(LONG)position — новая позиция ползунка). Чтобы получить текущее значение ползунка, следует послать ему сообщение TBM_GETPOS ( wp=0; lp=0 ). Ползунок оповещает родительское окно о событиях через сообщение WM_HSCROLL ( LOWORD(wp)=ScrollCode — код события; HIWORD(wp)=posistion — позиция маркера ползунка; lp=(HWND)hwndTrackBar — дескриптор элемента). Toolbars ( TOOLBARCLASSNAME ) Панель инструментов — это окно, содержащее набор кнопок, посылающих командное сообщение родительскому окну, когда пользователь щелкает по ним. Как правило, кнопки на панели инструментов соответствуют часто используемым командам меню приложения. Панель инструментов располагается ниже строки меню.

Информация о кнопках передается панели инструментов через структуру TBBUTTON , а для создания окна панели инструментов удобно использовать функцию CreateToolbarEx . Rich Edit Controls ( RICHEDIT_CLASS ) Продвинутое поле редактирования является развитием класса «EDIT» стандартных управляющих элементов. Элементы управления этого класса поддерживают форматирование текста (по отдельным символам и по отдельным абзацам) и позволяют внедрять OLE-объекты. Tree View Controls ( WC_TREEVIEW ) Элемент просмотра дерева позволяет представлять информацию об иерархии некоторых объектов (содержание документа, дерево каталогов файловой системы и т.п.) Каждый объект может быть представлен текстовой меткой и иконкой. Объект может иметь иерархию дочерних объектов, которая раскрывается по щелчку на этом элементе.

Стандартные диалоги

Windows предоставляет набор готовых стандартных диалогов посредством библиотеки Common Dialog Boxes Library (COMDLG32.DLL): диалог открытия и сохранения файла, диалог печати документа, диалог выбора цвета, шрифта и т.п. Чтобы создать один из перечисленных диалогов, надо заполнить определенную структуру и вызвать соответствующую функцию из этой библиотеки:

  • BOOL WINAPI ChooseColor(CHOOSECOLOR* lpcc) — создает диалог, отображающий палитру цветов и позволяющий пользователю выбрать тот или иной цвет или создать свой.
  • BOOL WINAPI ChooseFont(CHOOSEFONT* lpcf) — создает диалог, отображающий имена установленных в системе шрифтов, их кегль, стиль начертания и т.п.
  • BOOL WINAPI GetOpenFileName(OPENFILENAME* lpofn) и BOOL WINAPI GetSaveFileNAme(OPENFILENAME* lpofn) — создают диалог, отображающий содержимое того или иного каталога, и позвояющий пользователю выбрать уникальное имя файла для открытия или сохранения.
  • BOOL WINAPI PrintDlg(PRINTDLG* lppd) — создает диалог, позволяющий пользователю установить различные опции печати, например, диапазон страниц, количество копий и др.
  • BOOL WINAPI PageStupDlg(PAGESETUPDLG* lppsd) — создает диалог, позволяющий пользователю выбрать различные параметры страницы: ориентацию, поля, размер бумаги и т.п.
  • HWND WINAPI FindText(FINDREPLACE* lpfr) — создает диалог, позволяющий пользователю ввести строку для поиска и такие опции, как направление поиска.
  • HWND WINAPI ReplaceText(FINDREPLACE* lpfr) — создает диалог, позволяющий пользователю ввести строку для поиска, строку для замены и опции замены (направление поиска, область поиска).

Все перечисленные диалоги, кроме последних двух, — модальные, т.е. указанная функция не вернет значение, пока пользователь тем или иным способом не закроет диалог. Значение TRUE означает, что пользователь закрыл диалог, нажав на «ОК», а соответствующая структура заполнена новыми значениями. Значение FALSE означает, что пользователь нажал на [Esc], выбрал команду системного меню «Закрыть» или нажал кнопку «Отмена», а соответствующая структура осталась неизменной. Пример использования диалога открытия файла:

Немодальный диалог в качестве главного окна

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

Пример 5 демонстрирует использование немодального диалога в приложении типа «блокнот».

Файл example5.h содержит константы-идентификаторы команд меню и элементов диалога.

Файл example5.rc описывает ресурсы программы: иконку, меню и шаблон диалога.

Файл example5.cpp — текст программы.

Что такое Windows API

Интерфейс прикладного программирования WindowsAPI (applicationprogramminginterface) является интерфейсом системного программирования в пользовательском режиме для семейства операционных систем Windows. До выхода 64-разрядных версий Windows программный интерфейс для 32-разрядных версий операционных систем Windows назывался Win32 API, чтобы его можно было отличить от исходной 16-разрядной версии Windows API (которая служила интерфейсом программирования для начальных 16-разрядных версий Windows).

Windows API состоит из нескольких тысяч вызываемых функций, которые разбиты на следующие основные категории:

  • Базовыеслужбы (Base Services).
  • Службыкомпонентов (Component Services).
  • Службы пользовательского интерфейса (User Interface Services).
  • Графические и мультимедийные службы (Graphics and Multimedia Services).
  • Обмен сообщениями и совместная работа (Messaging and Collaboration).
  • Сеть (Networking).
  • Веб-службы (Web Services).

Описание WindowsAPI можно найти в документации по набору инструментальных средств разработки программного обеспечения — WindowsSoftwareDevelopmentKit (SDK). Эта документация доступна на веб-сайте www.msdn.microsoft.com. Она также включена со всеми уровнями подписки в сеть MicrosoftDeveloperNetwork (MSDN), предназначенную для разработчиков.

Технология .NET.

Microsoft .NET Framework состоит из библиотеки классов под названием Framework Class Library (FCL) и управляемой среды выполнения кода —Common Language Runtime (CLR). CLR обладает функциями своевременной компиляции, проверки типов, сборки мусора и обеспечения безопасности доступа к коду. Предлагая эти функции, CLR предоставляет среду разработки, повышающую производительность работы программистов и сокращающую количество наиболее распространенных ошибок программирования.

Среда CLR реализована, как классический COM-сервер, код которого находится в стандартной Windows DLL-библиотеке, предназначенной для работы в пользовательском режиме. Фактически все компоненты .NET Framework реализованы как стандартные Windows DLL-библиотеки пользовательского режима, наложенные поверх неуправляемых функций Windows API. (Ни один из компонентов .NET Framework не работает в режиме ядра.) Взаимоотношения между этими компонентами показаны на рисунке.

Службы, функции и стандартные программы.

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

  • Функции Windows API. Документированные, вызываемые подпрограммы в WindowsAPI. Например, CreateProcess, CreateFile и GetMessage.
  • Собственные системные службы (или системные вызовы). Недокументированные базовые службы в операционной системе, вызываемые при работе в пользовательском режиме. Например, NtCreateUserProcess является внутренней службой, которую функция Windows CreateProcess вызывает для создания нового процесса.
  • Функции поддержки ядра (или подпрограммы). Подпрограммы внутри операционной системы Windows, которые могут быть вызваны только из режима ядра. Например, ExAllocatePoolWithTag является подпрограммой, вызываемой драйверами устройств для выделения памяти из системных динамически распределяемых областей Windows (называемых пулами).
  • Службы Windows. Процессы, запускаемые Диспетчером управления службами (Windowsservicecontrolmanager). Например, служба Диспетчер задач запускается в виде процесса, работающего в пользовательском режиме, в котором поддерживается команда at (аналогичная UNIX-командам at или cron).
  • Библиотеки DLL (dynamic-link libraries — динамически подключаемые библиотеки). Набор вызываемых подпрограмм, связанных вместе в виде двоичного файла, который может быть загружен в динамическом режиме приложениями, которые используют эти подпрограммы. В качестве примера можно привести Msvcrt.dll (библиотеку времени выполнения для приложений, написанных на языке C) и Kernel32.dll (одну из библиотек подсистемы Windows API). DLL-библиотеки широко используются компонентами и приложениями Windows, которые работают в пользовательском режиме. Преимущество, предоставляемое DLL-библиотеками по сравнению со статическими библиотеками, заключается в том, что они могут использоваться сразу несколькими приложениями, и Windows обеспечивает наличие в памяти только одной копии кода DLL-библиотеки для тех приложений, в которых имеются ссылки на эту библиотеку. Следует заметить, что неисполняемые .NET-сборки компилируются как DLL-библиотеки, но без каких-либо экспортируемых подпрограмм. CLR анализирует скомпилированные метаданные для доступа к соответствующим типам и элементам классов.

История Win32 API.

Интересно, что Win32 не планировался в качестве исходного интерфейса программирования для той системы, которая в ту пору называлась Windows NT. Поскольку проект Windows NT запускался в качестве замены для OS/2 версии 2, первоначальным интерфейсом программирования был 32-разрядный OS/2 PresentationManagerAPI. Но через год после запуска проекта произошел взлет поступившей в продажу Microsoft Windows 3.0. В результате этого Microsoft сменила направление и сделала Windows NT будущей заменой семейства продуктов Windows, а не заменой OS/2. В связи с этим и возникла необходимость выработать спецификацию Windows API — до этого, в Windows 3.0, API существовал только в виде 16-разрядного интерфейса.

Хотя в Windows API намечалось введение множества новых функций, недоступных в Windows 3.1, Microsoft решила сделать новый API, по возможности, максимально совместимым по именам, семантике и используемым типам данных с 16-разрядным Windows API, чтобы облегчить бремя переноса существующих 16-разрядных Windows-приложений в Windows NT. Этим, собственно, и объясняется тот факт, что многие имена функций и интерфейсов могут показаться непоследовательными: так нужно было для обеспечения совместимости нового Windows API со старым 16-разрядным Windows API.

WinApi

Программный код большинства примеров данной лекции можно найти в проектах, доступных для просмотра: DocOne6, DocTwo6.

Несколько слов об API, Win32, DLL

API ( Application Programming Interface — интерфейс прикладных программ) — это множество функций, организованных, обычно, в виде DLL . Функции API позволяют организовать интерфейс между прикладной программой и средой, в которой работает эта программа . Вызов функций API позволяет программе получать доступ к ресурсам среды и управлять ее работой. Как правило, API задает стандарт взаимодействия среды и прикладной программы.

Win32 — это название интерфейса, ориентированного на 32-х разрядные приложения и реализованного на таких известных платформах как Windows 95 , Windows 98, Windows NT, Windows CE . Функции, составляющие этот интерфейс , позволяют прикладной программе получать доступ к ресурсам операционной системы и управлять ее работой. Более ранние версии Windows используют интерфейс , известный как Win16 . Конечно, не все функции, составляющие интерфейс Win32, реализованы в полной мере на всех платформах, так что вызов одной и той же функции под NT приведет к определенному результату, а под Windows 95 работает как вызов заглушки . Любое из приложений, работающее в среде Windows , прямо или косвенно вызывает функции, входящие в Win32 API .

Функции, составляющие Win32 интерфейс , организованы в виде нескольких динамически подключаемых библиотек ( DLL ) и исполняемых файлов. Говоря о Win32 API , следует в первую очередь упомянуть три основные библиотеки:

  1. Kernel32.dll. Эта библиотека предназначена для работы с объектами ядра операционной системы и ее функции позволяют управлять памятью и другими системными ресурсами.
  2. User32.dll. Здесь сосредоточены функции для управления окнами — основным видом объектов операционной системы. Обработка сообщений, работа с меню, таймерами, все это выполняют функции этой DLL .
  3. GDI32.dll. Эта библиотека, обеспечивающая графический интерфейс операционной системы ( Graphics Device Interface). Функции управления выводом на экран дисплея, управления выводом принтера, функции для работы со шрифтами — все они входят в состав этой библиотеки.

Заметьте, Win API функции находятся не только в этих библиотеках. С другой стороны API функции не обязательно входят в состав Win32 интерфейса. Например, MAPI интерфейс ( Messaging Application Programming Interface ) составляют функции, предназначенные для обработки сообщений электронной почты, TAPI ( Telephone API ) — функции работы с телефонными сообщениями. MAPI , TAPI , также как и Win32 это некоторый набор функций, задающий определенный стандарт взаимодействия

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

.VBA и Win32 API

Работая на VBA , неявно всегда приходится иметь дело с функциями Win32 API , только вызов их упрятан в вызываемых VBA функциях или методах объектов Office 2000. Так, например, при работе с объектом Shape так или иначе будут вызываться функции GDI32, обеспечивающие работу с графикой, при работе c функциями VBA.Interaction , например GetSettings , SaveSettings и другими, будет вызываться соответствующие функции работы с реестром Windows , хранящиеся в библиотеках User32 и advapi32 . Такой косвенный вызов имеет свои преимущества, обеспечивая определенную безопасность в работе VBA программ. Но в ряде случаев VBA программисту необходим доступ ко всем возможностям операционной системы, предоставляемым Win32 API интерфейсом. Естественно, в этом случае он понимает, что на него ложится большая ответственность в обеспечении корректного вызова функций, поскольку ошибки в вызове могут привести к непредвиденным отказам в работе программы.

Вызов функций и оператор Declare

Элементы ActiveX , COM объекты могут экспонировать свой интерфейс, — свои свойства и методы. Это означает, что они уведомляют, предоставляют информацию клиентам о своем интерфейсе. Технически это обеспечивается тем, что эти объекты, наряду с DLL , сопровождаются TypeLib — библиотекой типов, в которой содержится в требуемом виде информация об интерфейсе объекта . В этом случае, для того чтобы начать работу с объектом, достаточно подключить ссылку на эту библиотеку в меню Tools|References в среде редактора VBE . Эта возможность не раз обсуждалась, когда речь шла о вызове, например, приложения Excel в документах Word. Напомним, что приложения Office 2000 представляют собой ActiveX объекты, построенные на основе COM технологии. Они явно экспонируют свой интерфейс, именно поэтому нет проблем при работе с такими приложениями, вызовами свойств и методов их многочисленных объектов. Библиотеки, составляющие Win32 интерфейс, не сопровождаются библиотеками типов TypeLib . Поэтому необходимо самому программисту уведомить VBA о том, где найти и как следует вызывать ту или иную функцию Win32 API Вызову каждой функции должен предшествовать оператор Declare , описывающий эту функцию. Этот оператор и сама схема вызова библиотечных функций используется при работе с любыми DLL , а не только с теми, которые содержат Win32 API функции. В общем случае в DLL могут храниться как функции, так и процедуры. Два варианта вызова этого оператора соответствуют ссылке на процедуру и на функцию, возвращающую значение. Первый вариант:

Илон Маск рекомендует:  Iis включения на стороне сервера
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL