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