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

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

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

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

6.4. Приложение MOUSENC

Приложение MOUSENC демонстрирует обработку сообщений WM_NCHITTEST и WM_MOUSEMOVE.

Основной файл приложения приведен в листинге 6.4.


Листинг 6.4. Файл mousenc\mousenc.cpp


// ----------------------------------------
// Обработка сообщений от мыши
// для внешней (non-client) области окна
// ----------------------------------------

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

BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char const szClassName[]   = "MOUSENCAppClass";
char const szWindowTitle[] = "MOUSENC Application";

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

int PASCAL
WinMain(HINSTANCE hInstance,
   HINSTANCE hPrevInstance,
   LPSTR     lpszCmdLine,
   int       nCmdShow)
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  if(!InitApp(hInstance))
      return FALSE;

  hwnd = CreateWindow(
    szClassName, szWindowTitle, 
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT,
    CW_USEDEFAULT, CW_USEDEFAULT,
    0, 0, hInstance, NULL); 

  if(!hwnd)
    return FALSE;

  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  while(GetMessage(&msg, 0, 0, 0))
  {
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// =====================================

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

  memset(&wc, 0, sizeof(wc));

  // Задаем стиль класса окна, позволяющий
  // получать сообщения о двойных
  // щелчках мыши
  wc.style         = CS_HREDRAW | CS_VREDRAW
                   | CS_DBLCLKS;

  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);
}

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

Функция главного окна определена в файле wndproc.cpp (листинг 6.5).


Листинг 6.5. Файл mousenc\wndproc.cpp


// =====================================
// Функция WndProc
// =====================================

#define STRICT
#include <windows.h>

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC        hdc;
  WORD       xPosScr, yPosScr, nSizeScr;
  WORD       xPos, yPos, nSize;
  BYTE       szBuf[80];

  static TEXTMETRIC tm;
  static int cxChar, cyChar;

  switch (msg)
  {
  case WM_CREATE:
    {
      // Получаем контекст отображения,
      // необходимый для определения метрик шрифта
      hdc = GetDC(hwnd);

      // Выбираем шрифт с фиксированной шириной букв
      SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

      // Заполняем структуру информацией
      // о метрике шрифта, выбранного в
      // контекст отображения
      GetTextMetrics(hdc, &tm);

      // Запоминаем значение ширины для
      // самого широкого символа
      cxChar = tm.tmMaxCharWidth;

      // Запоминаем значение высоты букв с
      // учетом межстрочного интервала
      cyChar = tm.tmHeight + tm.tmExternalLeading;

      // Освобождаем контекст
      ReleaseDC(hwnd, hdc);

      return 0;
    }

    case WM_NCHITTEST:
    {
      // Если убрать знак комментария со следующей
      // строки, окно можно будет передвигать не только
      // при помощи заголовка окна, но и просто
      // установив курсор мыши в любую область окна

      // return HTCAPTION;

      // Сохраняем координаты курсора мыши
      xPosScr  = LOWORD(lParam);
      yPosScr  = HIWORD(lParam);

      hdc = GetDC(hwnd);

      // Подготавливаем текстовую строку, содержащую
      // координаты курсора мыши
      nSize = wsprintf(szBuf, "(%-3d, %-3d)",
      xPosScr, yPosScr);

      // Выбираем шрифт с фиксированной шириной букв
      SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

      // Выводим экранные координаты курсора мыши
      TextOut(hdc, cxChar, 0, szBuf, nSize);

      ReleaseDC(hwnd, hdc);
      break;
    }

    case WM_MOUSEMOVE:
    {
      // Сохраняем координаты курсора мыши
      xPos   = LOWORD(lParam);
      yPos   = HIWORD(lParam);

      hdc = GetDC(hwnd);

      // Подготавливаем текстовую строку, содержащую
      // координаты курсора мыши
      nSize = wsprintf(szBuf, "(%-3d, %-3d)", xPos, yPos);

      // Выбираем шрифт с фиксированной шириной букв
      SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

      // Выводим оконные координаты курсора мыши
      TextOut(hdc, cxChar, cyChar, szBuf, nSize);

      ReleaseDC(hwnd, hdc);
      break;
    }

    // Двойной щелчок левой клавишей мыши
    // завершает работу приложения
    case WM_LBUTTONDBLCLK:
    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

При создании окна функция окна получает сообщение WM_CREATE. Обработчик этого сообщения выбирает системный шрифт с фиксированной шириной букв и определяет метрики выбранного шрифта. Метрики шрифта сохраняются для дальнейшего использования.

В самом начале обработчика сообщения WM_NCHITTEST имеется строка, закрытая символом комментария:

// return HTCAPTION;

Если убрать комментарий, обработчик примет следующий вид:

case WM_NCHITTEST:
{
  return HTCAPTION;
}

В этом случае в каком бы месте окна вы ни щелкнули мышью, Windows будет думать, что вы щелкнули в области заголовка окна. В этом случае Windows вызовет процедуру перемещения окна, которая обычно вызывается, когда вы перемещаете окно за его заголовок.

Фактически здесь используется объектная ориентированность Windows, позволяющая вам подменить метод, с помощью которого Windows определяет расположение курсора мыши относительно элементов окна.

Как это можно использовать на практике?

Вы, например, можете создать окно, не имеющее заголовка, но которое тем не менее можно перемещать при помощи мыши. Вспомните внешний вид, который можно придать стандартному приложению Windows с именем Clock (рис. 6.2).

Рис. 6.2. Приложение Clock

Несмотря на то что главное окно приложения Clock в данном случае не имеет заголовка, его все же можно перемещать по экрану. Аналогичного эффекта можете добиться и вы, если соответствующим образом обработаете сообщение WM_NCHITTEST.

Если же оставить обработчик в таком виде, как он представлен в нашем примере, после прихода сообщения WM_NCHITTEST в левом верхнем углу окна будут отображены текущие экранные координаты курсора (если курсор находится внутри окна).

Обработчик сообщения WM_MOUSEMOVE тоже отображает текущие координаты курсора мыши (строкой ниже), но только в оконных, а не экранных координатах (рис. 6.3).

Рис. 6.3. Главное окно приложения MOUSENC

Обратите внимание, что работа обработчика сообщения WM_NCHITTEST завершается передачей управления функции DefWindowProc. Если этого не сделать, Windows не сможет правильно определить расположение курсора и, следовательно, не сможет выполнить такие операции, как перемещение окна и изменение размера окна.

Обработчик сообщения WM_LBUTTONDBLCLK завершает работу приложения, вызывая функцию PostQuitMessage.

Файл определения модуля для приложения MOUSENC приведен в листинге 6.6.


Листинг 6.6. Файл mousenc\mousenc.def


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

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