Опубликован: 15.11.2010 | Доступ: свободный | Студентов: 643 / 18 | Оценка: 4.00 / 5.00 | Длительность: 17:30:00
Специальности: Программист
Лекция 9:

Ввод-вывод с использованием WinAPI

9.2. Пример создания "каркаса" приложения средствами WinAPI в C++

9.2.1. "Низкоуровневое" программирование в операционной системе Microsoft Windows

Программирование на языке C++ в операционной системе Microsoft Windows условно можно разделить на "высокоуровневое" и "низкоуровневое". К "высокому уровню программирования" на языке C++ можно отнести программирование с использованием библиотеки MFC в Microsoft Visual C++, библиотеки ObjectVision в Borland C/C++ и других, менее распространённых библиотек. При написании этих библиотек учтён весь опыт программирования интерфейсов, поэтому с помощью этих библиотек легко сделать "рабочий шаблон" для своего проекта, и уже потом наполнять его нужными функциями. Это очень удобно.… Однако автор всё-таки советует для создания небольших утилит с ограниченной функциональностью использовать "низкоуровневое" программирование в Windows.

При программировании на "низком уровне" пользователю приходится напрямую использовать функции API Windows (что в "высокоуровневых" библиотеках делать запрещено) и "вручную заниматься" выделением памяти, созданием/освобождением/закрытием контекста устройств, описывать реакцию приложения на поступившие сообщения о нажатых клавишах, событий от мыши и системных событий. Это всё гораздо сложнее.… Но не стоит расстраиваться. Для создания приложений можно использовать один или несколько предопределённых "шаблонов", которые могут быть созданы для определённых классов задач, и включать в себя все "наиболее важные и наиболее типичные" настройки для узкого круга задач и приложений. Все остальные настройки "наследуются Windows" по-умолчанию. Часть из этих шаблонов, предназначенных для обработки текстов, приведена в данном разделе.

9.2.2. Функция WinMain

Основной функцией создаваемого приложения Microsoft Windows является функция WinMain. Эта функция - аналог функции "main" для MS-DOS. Её задача - определить класс окна, создать главное окно приложения и "присоединить к нему" функцию обработки сообщений Windows. В отличие от функции main все входящие в неё параметры обязательны.

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

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
Листинг 9.1.
  1. Описание и инициализация глобальных переменных;
  2. Вызов функции InitApp для регистрации приложения;
  3. Если инициализация прошла неудачно, то выход с возвращением значения "FALSE" ;
  4. Создаётся главное окно приложения;
  5. Если окно создать не удалось, то выход с возвращением значения "FALSE" ;
  6. Рисуется окно приложения;
  7. Запускается цикл обработки системных сообщений;
  8. После выхода из цикла ставится оператор выхода из программы, с возвращаемым значением msg.wParam ;
  9. Конец функции.

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

9.2.3. Инициализация приложения

Для инициализации приложения Microsoft Windows используется функция InitApp. Назначение этой функции - заполнить структуру WNDCLASS и вызвать функцию регистрации класса окна. В случае неуспешной инициализации функция возвращает значение: "FALSE".

В структуре WNDCLASS заполняются следующие поля:

  1. wc.style - один или несколько предопределённых стилей окна;
  2. wc.lpfnWndProc - указатель на функцию обработки сообщений WndProc ;
  3. wc.cbClsExtra - размер дополнительной области данных для класса окна;
  4. wc.cbWndExtra - размер дополнительной области памяти для каждого окна из данного класса;
  5. wc.hInstance - идентификатор приложения;
  6. wc.hIcon - идентификатор иконки по-умолчанию;
  7. wc.hCursor - идентификатор курсора по-умолчанию;
  8. wc.hbrBackground - цвет окна по-умолчанию;
  9. wc.lpszMenuName - идентификатор меню для данного класса приложений;
  10. wc.lpszClassName - имя класса окна (должно совпадать с классом окна для функции WinMain );

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

9.2.4. Функция WndProc

Наконец, рассмотрим функцию WndProc, которая, как нужно особо отметить, не вызывается "явно" из основной функции WinMain, но которая, тем не менее, управляет работой всего приложения Windows. Эта функция представляет собой "описание реакции системы на системные сообщения" операционной системы Microsoft Windows. Вкратце дадим понятие системных сообщений Microsoft Windows.

"Линейная последовательность вычислений", применяемая в MS-DOS, не годится для программирования многозадачных операционных систем, таких как Windows. Во-первых, ресурсы ЭВМ при многозадачной обработке должны быть эффективно распределены между приложениями, а не предоставляться в "монопольное пользование" отдельной программе. Во-вторых, реализация реакции операционной системы MS-DOS на внешние и внутренние события, такие как прерывания от таймера ("системных часов"), от клавиатуры (на нажатие-отпускание клавиши), от мыши и т.д. далеки от совершенства. Как правило, приложению MS-DOS приходилось ждать, когда операционная система "обработает" эти прерывания. А это приводит к непроизводительному расходу ресурсов. Список неудобств можно продолжить.

Чтобы "обойти" эти недостатки, уже в конце 80-х - начале 90-х годов XX века корпорацией Microsoft был разработан механизм эффективного использования ресурсов ЭВМ на основе архитектуры x86, полностью совместимый с MS-DOS. Этот механизм был использован сначала в файловой оболочке, а затем и в операционной системе Microsoft Windows. В его реализации используются "сообщения", которые генерируются операционной системой Microsoft Windows, ставятся в очередь на исполнение и затем передаются на обработку приложениям. После обработки сообщения приложением оно возвращает результат обратно операционной системе, на основе которого вновь генерируются сообщения и т.д. Поскольку сообщения генерируются постоянно и для всех приложений, им ничего не надо делать, кроме как "слушать" системную очередь сообщений, "отлавливать" своё сообщение и обрабатывать его. А из-за того, что все сообщения помещаются в одну очередь, не будет "опасений" по-поводу того, что сообщение "потеряется", или того, что сообщения поступят приложению не в той последовательности. И роль "всё слышащего уха" приложения Windows выполняет как раз функция WndProc.

Синтаксис функции WndProc следующий:

LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
Листинг 9.2.

где

  • hwnd - идентификатор окна, получающего сообщение;
  • msg - идентификатор сообщения Windows;
  • wParam - первый параметр идентификатора сообщения;
  • lParam - второй параметр идентификатора сообщений.

Среди идентификаторов сообщений важнейшими являются следующие идентификаторы:

  • WM_CREATE - сообщение с этим идентификатором передаётся приложению в момент его создания;
  • WM_PAINT - сообщение с этим номером передаётся приложению в момент "отрисовки" его главного окна;
  • WM_COMMAND - сообщение с этим номером передаётся приложению, когда вызывается меню его главного окна (например, пользователь выбрал один из пунктов системного меню);
  • WM_DESTROY - сообщение с этим номером передаётся системе, когда необходимо "уничтожение" окна Windows;

Для описания реакции на сообщение Windows алгоритм функции WndProc пишется следующим образом:

  1. После инициализации "внутренних переменных" программы реализуется конструкция "множественного выбора" значений идентификатора msg ;
  2. В зависимости от его значения пишется "обработчик" этого сообщения. Им может быть как допустимая функция C/C++, функция API Windows, так и оператор "множественного выбора" по значению первого и второго параметра сообщения;
  3. Оператор "множественного выбора" значений идентификатора msg должен обязательно содержать конструкцию "default". Это означает, что все сообщения, передаваемые операционной системой указанному окну, обработчик которых в программе отсутствует, должны обрабатываться операционной системой по-умолчанию. Эта конструкция обязательно должна иметь вид:
    default:
    	return DefWindowProc(hwnd, msg, wParam, lParam);

Примечание. Фактически, следуя формулировкам объектно-ориентированного программирования, сообщение "WM_CREATE" является конструктором приложения Windows, сообщение "WM_DESTROY" - деструктор приложения, а все остальные сообщения являются как бы "методами" этого приложения.

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

9.2.5. Пример "стандартного каркаса" для приложения Microsoft Windows

Пример "стандартного каркаса" приложения Microsoft Windows, разработанный для компиляторов Borland C/C++ версий от 3.1 до 5.5, представлен в приложении № III к данной лекции. В приложении № IV представлен пример программы для вывода текстовых строк в окно Windows. В приложениях №№ V и VI представлены те же программы, переписанные для системы Microsoft Visual Studio .Net 2003. Эти программы разработаны для 32-х битного режима Microsoft Windows и рационально используют ресурсы новых ЭВМ на основе технологий Intel и AMD.

9.3. Резюме

На этой лекции Вы познакомились с описанием функций ввода-вывода данных при помощи функций WinAPI, а также с "каркасом" для построения собственных приложений средствами WinAPI. Конечно же, Вы пока не умеете работать с контекстом отображения в Windows, метриками текста и т.д., необходимых для программирования на Си в операционных системах Microsoft Windows. Но начало положено…

Дмитрий Карпов
Дмитрий Карпов
Россия, Нижний Новгород
Олег Корсак
Олег Корсак
Латвия, Рига