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

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

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

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

3.3. Приложение WSTYLE

Для того чтобы вы смогли проводить эксперименты со стилями окон и классов, мы подготовили приложение WSTYLE (листинг 3.1). В этом приложении создается одно главное окно, одно перекрывающееся (этим окном владеет главное), одно временное (также принадлежащее главному окну) и одно дочернее окно.


Листинг 3.1. Файл wstyle\wstyle.cpp


// ----------------------------------------
// Демонстрация стилей окна
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <mem.h>

// Прототипы функций

BOOL Register(HINSTANCE);
LRESULT CALLBACK _export 
  MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK _export 
  ChildWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK _export 
  PopUpWndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса для главного окна приложения
char const szMainClassName[]    = "WStyleAppClass";

// Имя класса для дочерних окон
char const szChildClassName[]   = "WStyleAppChildClass";

// Имя класса для временных окон
char const szPopUpClassName[]   = "WStyleAppPopUpClass";

// Заголовок главного окна приложения
char const szMainWindowTitle[]  = "WStyle Application";

// Заголовок дочернего окна
char const szChildWindowTitle[] = "Окно Child";

// Заголовок временного окна
char const szPopUpWindowTitle[] = "Окно PopUp";

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine,
        int       nCmdShow)
{
  MSG  msg;
  HWND MainHwnd;   // идентификатор главного окна приложения
  HWND ChildHwnd;  // идентификатор дочернего окна
  HWND PopUpHwnd;  // идентификатор временного окна
  HWND OwnedHwnd;  // идентификатор окна, которым владеет
                   // главное окно приложения

  // Регистрируем классы окон
  if(!Register(hInstance))
      return FALSE;

  // Создаем главное окно приложения
  MainHwnd = CreateWindow(
    szMainClassName,     // имя класса окна
    szMainWindowTitle,   // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем размеры и расположение
    CW_USEDEFAULT,       // окна, принятые по умолчанию 
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                              // параметры

  // Если создать окно не удалось, завершаем приложение
  if(!MainHwnd)
    return FALSE;

  // Делаем окно видимым
  ShowWindow(MainHwnd, nCmdShow);

  // Посылаем в окно сообщение WM_PAINT
  UpdateWindow(MainHwnd);

  // Создаем перекрывающееся окно, которое
  // принадлежит главному окну приложения
  OwnedHwnd = CreateWindow(
    szMainClassName,        // имя класса окна
    "Перекрывающееся окно", // заголовок окна
    WS_OVERLAPPEDWINDOW,    // стиль окна
    20,                     // задаем размеры и расположение
    200,                    // окна
    300,
    100,
    MainHwnd,            // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры

  // Если создать окно не удалось, завершаем приложение
  if(!OwnedHwnd)
    return FALSE;

  // Делаем окно видимым
  ShowWindow(OwnedHwnd, nCmdShow);

  // Посылаем в окно сообщение WM_PAINT
  UpdateWindow(OwnedHwnd);

  // Создаем дочернее окно
  ChildHwnd = CreateWindow(
    szChildClassName,     // имя класса окна
    "Дочернее окно",      // заголовок окна
    WS_CHILDWINDOW | WS_VISIBLE |
    WS_CAPTION,           // стиль окна
    300,                  // задаем размеры и расположение
    20,                   // окна
    200,
    100,
    MainHwnd,            // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры

  // Если создать окно не удалось, завершаем приложение
  if(!ChildHwnd)
    return FALSE;

  // Создаем временное окно
  PopUpHwnd = CreateWindow(
    szPopUpClassName,       // имя класса окна
    "Временное окно",      // заголовок окна
    WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE,  // стиль окна
    100,                    // задаем размеры и расположение
    100,                    // окна
    200,
    100,
    MainHwnd,            // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры

  // Если создать окно не удалось, завершаем приложение
  if(!PopUpHwnd)
    return FALSE;

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, 0, 0, 0))
  {
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция Register
// Выполняет регистрацию классов окна,
// используемых приложением
// =====================================

BOOL
Register(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна
  memset(&wc, 0, sizeof(wc));

// --------------------------------------------------
// Регистрируем класс окна для главного
// и перекрывающегося окна
// --------------------------------------------------
  wc.style = 0;
  wc.lpfnWndProc   = (WNDPROC) MainWndProc;
  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)szMainClassName;

  aWndClass = RegisterClass(&wc);
  if(aWndClass == 0) return FALSE;

// --------------------------------------------------
// Регистрируем класс окна для 
// дочернего окна
// --------------------------------------------------

  wc.lpfnWndProc   = (WNDPROC) ChildWndProc;
  wc.hCursor       = LoadCursor(NULL, IDC_SIZE);
  wc.hbrBackground = GetStockBrush(GRAY_BRUSH);
  wc.lpszClassName = (LPSTR)szChildClassName;

  aWndClass = RegisterClass(&wc);
  if(aWndClass == 0) return FALSE;

// --------------------------------------------------
// Регистрируем класс окна для
// временного окна
// --------------------------------------------------

  wc.lpfnWndProc   = (WNDPROC) PopUpWndProc;
  wc.hCursor       = LoadCursor(NULL, IDC_CROSS);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszClassName = (LPSTR)szPopUpClassName;

  aWndClass = RegisterClass(&wc);
  if(aWndClass == 0) return FALSE;

  return TRUE;
}

// =====================================
// Функция MainWndProc
// Используется главным окном приложения
// и созданным дополнительно
// перекрывающимся окном 
// =====================================

LRESULT CALLBACK _export
MainWndProc(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", 8);
      EndPaint(hwnd, &ps);
      return 0;
    }

    case WM_LBUTTONDOWN:
    {
      hdc = GetDC(hwnd);
      TextOut(hdc, 10, 40, "WM_LBUTTONDOWN", 14);
      ReleaseDC(hwnd, hdc);
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// =====================================
// Функция ChildWndProc
// Используется дочерним окном
// =====================================

LRESULT CALLBACK _export
ChildWndProc(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", 8);
      EndPaint(hwnd, &ps);
      return 0;
    }

    case WM_LBUTTONDOWN:
    {
      hdc = GetDC(hwnd);
      TextOut(hdc, 10, 40, "WM_LBUTTONDOWN", 14);
      ReleaseDC(hwnd, hdc);
      return 0;
    }

    case WM_RBUTTONDOWN:
    {
        MessageBeep(-1); // звуковой сигнал
        return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

// =====================================
// Функция PopUpWndProc
// Используется временным окном
// =====================================

LRESULT CALLBACK _export
PopUpWndProc(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", 8);
      EndPaint(hwnd, &ps);
      return 0;
    }

    case WM_LBUTTONDOWN:
    {
      hdc = GetDC(hwnd);
      TextOut(hdc, 10, 40, "WM_LBUTTONDOWN", 14);
      ReleaseDC(hwnd, hdc);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

Приложение регистрирует три класса окна со следующими именами:

char const szMainClassName[]    = "WStyleAppClass";
char const szChildClassName[]   = "WStyleAppChildClass";
char const szPopUpClassName[]   = "WStyleAppPopUpClass";

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

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

Для каждого класса окон в приложении используется отдельная функция окна. Главное окно и дополнительное временное окно работают с функцией MainWndProc, для дочернего окна используется функция окна с именем ChildWndProc, а для временного - PopUpWndProc.

Для каждого окна определен свой заголовок:

char const szMainWindowTitle[]  = "WStyle Application";
char const szChildWindowTitle[] = "Окно Child";
char const szPopUpWindowTitle[] = "Окно PopUp";

Так как приложение создает четыре различных окна, в функции WinMain определены четыре переменные, предназначенные для хранения идентификаторов окон. Эти переменные имеют имена MainHwnd, ChildHwnd, PopUpHwnd и OwnedHwnd.

Переменная MainHwnd используется для хранения идентификатора главного окна приложения. Переменные ChildHwnd и PopUpHwnd предназначены соответственно для хранения идентификаторов дочернего и временного окна. В переменной OwnedHwnd хранится идентификатор дополнительного перекрывающегося окна, принадлежащего главному окну приложения.

Выполнение приложения начинается с регистрации классов окон, выполняемых функцией Register, определенной в приложении.

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

wc.style = 0;
wc.lpfnWndProc   = (WNDPROC) MainWndProc;
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)szMainClassName;
aWndClass = RegisterClass(&wc);
if(aWndClass == 0) return FALSE;

При регистрации класса для дочернего окна для упрощения программы используется проинициализированная ранее структура wc. В этой структуре необходимо заменить указатель на функцию окна (дочернее окно работает с функцией ChildWndProc) и имя класса (поле lpszClassName). Дополнительно для дочернего окна мы изменили форму курсора мыши и цвет фона:

wc.lpfnWndProc   = (WNDPROC) ChildWndProc;
wc.hCursor       = LoadCursor(NULL, IDC_SIZE);
wc.hbrBackground = GetStockBrush(GRAY_BRUSH);
wc.lpszClassName = (LPSTR)szChildClassName;

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

При регистрации класса временного окна мы указываем адрес функции окна с именем PopUpWndproc, определяем форму курсора в виде перекрестия, используем системный цвет для фона, а также указываем другое имя класса:

wc.lpfnWndProc   = (WNDPROC) PopUpWndProc;
wc.hCursor       = LoadCursor(NULL, IDC_CROSS);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = (LPSTR)szPopUpClassName;

В случае успешной регистрации всех классов функция Register возвращает значение TRUE.

После регистрации классов окна в функции WinMain создаются четыре окна.

Первым создается главное окно приложения:

MainHwnd = CreateWindow(
  szMainClassName,     // имя класса окна
  szMainWindowTitle,   // заголовок окна
  WS_OVERLAPPEDWINDOW, // стиль окна
  CW_USEDEFAULT,       // задаем размеры и расположение
  CW_USEDEFAULT,       // окна, принятые по умолчанию 
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  0,                   // идентификатор родительского окна
  0,                   // идентификатор меню
  hInstance,           // идентификатор приложения
  NULL);               // указатель на дополнительные
                       // параметры

Главное окно приложения представляет собой обычное перекрывающееся окно класса WS_OVERLAPPEDWINDOW. Так как главное окно не может никому принадлежать, для параметра функции CreateWindow, определяющего идентификатор родительского окна или окна-владельца, используется нулевое значение.

Для того чтобы сделать окно видимым и нарисовать его внутреннюю область, вызываются функции ShowWindow и UpdateWindow.

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

Для перекрывающегося окна мы определяем свой заголовок, стиль WS_OVERLAPPEDWINDOW, явно указываем координаты левого верхнего угла окна (20, 200), размеры окна (300, 100), а также (вместо идентификатора родительского окна) идентификатор окна-владельца:

  OwnedHwnd = CreateWindow(
    szMainClassName,        // имя класса окна
    "Перекрывающееся окно", // заголовок окна
    WS_OVERLAPPEDWINDOW,    // стиль окна
    20,                     // задаем размеры и расположение
    200,                    // окна
    300,
    100,
    MainHwnd,            // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                            // параметры

Для того чтобы сделать перекрывающееся окно видимым и нарисовать его внутреннюю область, вызываются функции ShowWindow и UpdateWindow. В качестве параметра им передается идентификатор перекрывающегося окна OwnedHwnd.

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

Далее приложение создает дочернее окно:

ChildHwnd = CreateWindow(
  szChildClassName,     // имя класса окна
  "Дочернее окно",      // заголовок окна
  WS_CHILDWINDOW | WS_VISIBLE |
  WS_CAPTION,           // стиль окна
  300,                  // задаем размеры и расположение
  20,                   // окна
  200,
  100,
  MainHwnd,            // идентификатор родительского окна
  0,                   // идентификатор меню
  hInstance,           // идентификатор приложения
  NULL);               // указатель на дополнительные
                       // параметры

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

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

Временное окно создается аналогично дочернему:

  PopUpHwnd = CreateWindow(
    szPopUpClassName,       // имя класса окна
    "Временное окно",      // заголовок окна
    WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE,  // стиль окна
    100,                    // задаем размеры и расположение
    100,                    // окна
    200,
    100,
    MainHwnd,            // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры

Для стиля временного окна используется комбинация констант WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE. При этом создается временное окно, которое имеет строку заголовка и становится видимым сразу после его создания функцией CreateWindow.

После создания всех окон запускается цикл обработки сообщений.

Окна приложения WSTYLE показаны на рис. 3.1.

Рис. 3.1. Окна приложения WSTYLE

Теперь займемся функциями окна, определенными в приложении.

Функция MainWndProc обрабатывает сообщения WM_PAINT, WM_LBUTTONDOWN и WM_DESTROY.

По сообщению WM_PAINT в окно выводится строка "WM_PAINT". Если в функцию окна приходит сообщение WM_LBUTTONDOWN, в окно выводится строка "WM_LBUTTONDOWN". Заметьте, что строка "WM_LBUTTONDOWN" выводится в то окно, в котором вы нажали на левую клавишу мыши. Если в функцию окна приходит сообщение WM_DESTROY, вызывается функция PostQuitMessage и приложение завершает свою работу.

Функция окна ChildWndProc используется для обработки сообщений, поступающих в дочернее окно. Эта функция также обрабатывает сообщения WM_PAINT и WM_LBUTTONDOWN, выводя соответствующие строки в дочернее окно. Дополнительно функция дочернего окна обрабатывает сообщение WM_RBUTTONDOWN. Если приходит это сообщение, вызывается функция MessageBeep, которая выдает звуковой сигнал.

Функция временного окна PopUpWndProc аналогична функции ChildWndProc, но не обрабатывает сообщение WM_RBUTTONDOWN.

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

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

Файл определения модуля, использованный приложением WSTYLE, приведен в листинге 3.2.


Листинг 3.2. Файл wstyle\wstyle.def


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

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