Операционная система 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 |

