Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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

© Александр Фролов, Григорий Фролов
Том 11, М.: Диалог-МИФИ, 1993, 269 стр.

[Назад] [Содеожание] [Дальше]

2.1. Приложение TEXTOUT

Наше следующее приложение, которое называется TEXTOUT, отличается от предыдущего только использованной функцией окна. Функция окна теперь обрабатывает сообщение WM_PAINT и выводит в окно текст. Однако дополнительно мы полностью изменили структуру исходных текстов приложения и использовали некоторые возможности языка программирования C++.

Приложение TEXTOUT создает два класса с именами WinApp и Window (здесь имеются в виду классы C++, а не классы окон). Первый класс реализует приложение Windows как таковое, второй используется для создания фундаментальных объектов Windows - окон.

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

Главным файлом, который содержит функцию WinMain, является файл textout.cpp (листинг 2.1).


Листинг 2.1. Файл textout/textout.cpp


// ----------------------------------------
// Вывод текста в окно приложения
// ----------------------------------------
#define STRICT
#include <windows.h>
#include "window.hpp"
#include "winapp.hpp"
#include "textout.hpp"
// Имя класса окна
char szClassName[]   = "TextoutAppClass";
// Заголовок окна
char szWindowTitle[] = "Textout Application";
// =====================================
// Функция WinMain
// =====================================
#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine, int       nCmdShow)
{
  // Указатель на объект класса Window - главное
  // окно приложения
  Window *PMainWindow;
  // Создаем объект класса WinApp - наше приложение
  WinApp App(hInstance, hPrevInstance, lpszCmdLine, nCmdShow);
  // Регистрируем класс для главного окна приложения
  App.RegisterWndClass(szClassName, (WNDPROC)WndProc);
  // Проверяем ошибки, которые могли возникнуть
  // при регистрации
  if(App.Error()) return App.Error();
  // Создаем объект класса Window - главное
  // окно приложения
  PMainWindow = new Window(hInstance,
        szClassName, szWindowTitle);
  // Проверяем ошибки, которые могли возникнуть
  // при создании окна
  if(PMainWindow->Error()) PMainWindow->Error();
  // Отображаем окно
  PMainWindow->Show(nCmdShow);
  // Посылаем в окно сообщение WM_PAINT
  PMainWindow->Update();
  // Запускаем цикл обработки сообщений
  App.Go();
  // Завершаем работу приложения
  return App.Error();
}

Этот файл выглядит значительно проще, чем аналогичный из предыдущего приложения.

Дополнительно к файлу windows.h в файл включаются include-файлы с именами window.hpp, winapp.hpp и textout.hpp. Первые два файла содержат соответственно определения классов Window и WinApp. Файл textout.hpp содержит все необходимые определения для файла textout.cpp.

Как и в предыдущем приложении, массивы с именами szClassName и szWindowTitle используются для хранения имени класса главного окна, создаваемого приложением, и заголовка этого же окна.

В функции WinMain определен указатель на объект класса Window с именем PMainWindow. Этот указатель будет хранить адрес объекта - главного окна приложения.

Далее в функции WinMain определяется объект класса WinApp, имеющий имя App. При создании объекта конструктор класса WinApp получает те же самые параметры, что и функция WinMain при старте приложения:

WinApp App(hInstance, hPrevInstance, lpszCmdLine, nCmdShow);

После этого выполняется регистрация класса окна, для чего вызывается функция-метод с именем RegisterWndClass, определенная в классе WinApp. В качестве параметров функции передаются указатель на имя класса szClassName и адрес функции окна WndProc.

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

После проверки ошибок, возможных на этапе регистрации класса окна, функция WinMain создает главное окно приложения, являющееся объектом класса Window:

PMainWindow = new Window(hInstance, 
  szClassName, szWindowTitle);

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

После проверки ошибок функция WinMain вызывает методы Show и Update из класса Window. Метод Show отображает окно, вызывая уже известную вам функцию ShowWindow. Метод Update посылает в функцию окна сообщение WM_PAINT, вызывая функцию UpdateWindow, которая вам также знакома.

Далее вызывается функция-метод Go, определенная в классе WinApp, которая запускает цикл обработки сообщений.

После завершения цикла обработки сообщения функция WinMain возвращает управление Windows, завершая работу приложения.

Include-файл textout.hpp (листинг 2.2) содержит прототип функции окна WndProc, необходимый при создании класса окна.


Листинг 2.2. Файл textout\textout.hpp


// Прототип функции WndProc
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

Класс WinApp определяется в файле winapp.hpp (листинг 2.3).


Листинг 2.3. Файл textout\winapp.cpp


// =====================================
// Определение класса WinApp
// =====================================
class WinApp
{
  MSG  msg;   // структура для работы с сообщениями
  int  errno; // флаг ошибки
  HINSTANCE hInstance;  // идентификатор приложения
  
public:
  // Конструктор
  WinApp(HINSTANCE, HINSTANCE, LPSTR, int);
  // Регистрация класса окна
  BOOL RegisterWndClass(LPSTR, WNDPROC);
  // Запуск цикла обработки сообщений
  WORD Go(void);
  // Проверка флага ошибок
  int  Error(void) { return errno; }
};

В классе WinApp используется структура msg, нужная для временного хранения сообщения в цикле обработки сообщений, флаг ошибки errno и идентификатор приложения hInstance.

Кроме конструктора в классе WinApp определены методы RegisterWndClass (регистрация класса окна), Go (запуск цикла обработки сообщений) и Error (проверка флага ошибок). Процедуры регистрации класса окна и цикл обработки сообщения ничем не отличаются от использованных в предыдущем приложении.

Исходные тексты функций-методов класса WinApp приведены в листинге 2.4.


Листинг 2.4. Файл textout\winapp.cpp


// =====================================
// Функции-методы для класса WinApp
// =====================================
#define STRICT
#include <windows.h>
#include "winapp.hpp"
// -------------------------------------
// Конструктор класса Window
// -------------------------------------
#pragma argsused
WinApp::WinApp(HINSTANCE hInst, HINSTANCE hPrevInstance,
               LPSTR     lpszCmdLine, int nCmdShow)
{
  // Сбрасываем флаг ошибки
  errno = 0;
  // Сохраняем идентификатор приложения
  hInstance = hInst;
}
// -------------------------------------
// Регистрация класса окна
// -------------------------------------
BOOL
WinApp::RegisterWndClass(LPSTR szClassName, WNDPROC WndProc)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна
  wc.style         = 0;
  wc.lpfnWndProc   = (WNDPROC) WndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName  = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;
  aWndClass = RegisterClass(&wc);
  return (aWndClass != 0);
}
// -------------------------------------
// Запуск цикла обработки сообщений
// -------------------------------------
WORD
WinApp::Go(void)
{
  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

Конструктор класса WinApp сбрасывает флаг ошибки и сохраняет в переменной hInstance идентификатор текущей копии приложения, переданный ему функцией WinMain.

Функция-метод RegisterWndClass регистрирует класс окна, используя для этого указатель на имя класса и указатель на функцию окна. Регистрация выполняется, как и в предыдущем приложении, при помощи функции RegisterClass.

Функция-метод Go запускает цикл обработки сообщений, полностью идентичный использованному в предыдущем приложении.

Так как приложение может создавать много окно, удобно определить в качестве класса окно. Файл window.hpp содержит определение класса Window, который предназначен для создания окон (листинг 2.5).


Листинг 2.5. Файл textout\window.hpp


// =====================================
// Определение класса Window
// =====================================
class Window
{
  HWND hwnd; // Идентификатор окна
  int errno; // Флаг ошибки
public:
  // Конструктор
  Window(HINSTANCE, LPSTR, LPSTR);
  // Проверка флага ошибки
  int  Error(void)        { return errno; }
  // Отображение окна
  void Show(int nCmdShow) { ShowWindow(hwnd, nCmdShow); }
  // Передача функции WndProc сообщения WM_PAINT
  void Update(void)       { UpdateWindow(hwnd); }
};

В классе Window определена переменная типа HWND для хранения идентификатора окна и флаг ошибок errno. Там же определены конструктор, функции-методы Show (отображение окна), Update (передача в функцию окна сообщения WM_PAINT) и Error (проверка флага ошибок).

Исходный текст конструктора класса Window находится в файле window.cpp (листинг 2.6).


Листинг 2.6. Файл textout\window.cpp


// =====================================
// Функции-методы для класса Window
// =====================================
#define STRICT
#include <windows.h>
#include "window.hpp"
// -------------------------------------
// Конструктор класса Window
// -------------------------------------
Window::Window(HINSTANCE hInstance, LPSTR szClassName,
               LPSTR szWindowTitle)
{
  // Сбрасываем флаг ошибки
  errno = 0;
  // Создаем окно
  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем размеры и расположение
    CW_USEDEFAULT,       // окна, принятые по умолчанию 
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                              // параметры
  // Если при создании окна были ошибки,
  // устанавливаем флаг ошибки
  if(!hwnd)
  {  
    errno = 1;
    return;
  }
}

Конструктор класса Window сбрасывает флаг ошибок и создает окно, вызывая для этого известную вам функцию CreateWindow. Если при создании окна были ошибки, устанавливается флаг ошибок.

До настоящего момента в приложении TEXOUT вам не встречалось ничего нового по сравнению с предыдущим приложением, за исключением того, что мы подготовили его для объектно-ориентированного программирования. Однако нашей задачей было научиться выводить в окно текст.

Как мы и говорили, эта операция выполняется функцией окна (листинг 2.7).


Листинг 2.7. Файл textout\winproc.cpp


// =====================================
// Функция WndProc
// Функция выполняет обработку сообщений главного
// окна приложения
// =====================================
#define STRICT
#include <windows.h>
LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;             // индекс контекста устройства
  PAINTSTRUCT ps;      // структура для рисования
  switch (msg)
  {
    case WM_PAINT:
    {
      // Получаем индекс контекста устройства
      hdc = BeginPaint(hwnd, &ps);
      // Выводим текстовую строку
      TextOut(hdc, 10, 20, "Сообщение WM_PAINT", 18);
      // Отдаем индекс контекста устройства
      EndPaint(hwnd, &ps);
      return 0;
    }
    case WM_LBUTTONDOWN:
    {
      // Получаем индекс контекста устройства
      hdc = GetDC(hwnd);
      // Выводим текстовую строку
      TextOut(hdc, 10, 40, "Сообщение WM_LBUTTONDOWN", 24);
      // Отдаем индекс контекста устройства
      ReleaseDC(hwnd, hdc);
      return 0;
    }
    case WM_RBUTTONDOWN:
    {
      hdc = GetDC(hwnd);
      TextOut(hdc, 10, 60, "Сообщение WM_RBUTTONDOWN", 24);
      ReleaseDC(hwnd, hdc);
      return 0;
    }
    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

Эта функция обрабатывает сообщение WM_PAINT, а также уже известные вам сообщения, попадающие в очередь сообщения, когда вы выполняете щелчок клавишами мыши над окном приложения - WM_LBUTTONDOWN и WM_RBUTTONDOWN. Во время обработки этих сообщений приложение выполняет вывод текста в окно, вызывая специально предназначенную для этого функцию TextOut, входящую в состав программного интерфейса Windows.

Последний файл, входящий в проект приложения, - это файл определения модуля с именем textout.def (листинг 2.8), который почти полностью повторяет аналогичный файл из предыдущего приложения.


Листинг 2.8. Файл textout\textout.def


; =============================
; Файл определения модуля
; =============================
NAME        TEXTOUT
DESCRIPTION 'Приложение TEXTOUT, (C) 1994, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
STACKSIZE   5120
HEAPSIZE    1024
CODE        preload moveable discardable
DATA        preload moveable multiple

[Назад] [Содеожание] [Дальше]