Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

sp_lab2

.pdf
Скачиваний:
4
Добавлен:
01.03.2016
Размер:
159.88 Кб
Скачать

2 ОБРАБОТКА СООБЩЕНИЙ WINDOWS.

2.1 Цель работы

Изучить механизмы передачи сообщений в ОС windows. Получить практические навыки обработки сообщений от клавиатуры, мыши и таймера.

2.2Теоретические сведения

2.2.1Синхронизация событий клавиатуры

Приложение «узнает» о нажатиях клавиш посредством сообщений, которые посылаются оконной функции. Когда пользователь нажимает и отпускает клавиши, драйвер клавиатуры передает информацию о нажатии клавиш в Windows. Windows сохраняет эту информацию (в виде сообщений) в системной очереди сообщений. Затем она передает сообщения клавиатуры, по одному за раз, в очередь сообщений программы, содержащей окно, имеющее фокус ввода (input focus). Программа, в свою очередь, отправляет сообщения соответствующей оконной функции с помощью функции DispatchMessage().

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

Приложению не нужно отслеживать все нажатия клавиш, потому, что Windows извещает программу об эффекте, вызванном их нажатием. Например, когда пользователь выбирает пункт меню, Windows посылает программе сообщение, что выбран пункт меню, независимо от того, был ли он выбран с помощью мыши или клавиатуры. В окнах диалога и внутри окон верхнего уровня могут содержаться окна редактирования (edit) для ввода текста (это окна, в которых пользователь набирает строки символов). Windows управляет всей логикой окон редактирования и дает программе окончательное содержимое этих окон, после того, как пользователь завершит ввод текста.

2.2.2 Категории клавиатурных сообщений

Сообщения, которые приложение получает от Windows о событиях от клавиатуры, различаются на аппаратные и символьные. Такое положение соответствует двум представлениям о клавиатуре. Во-первых, клавиатуру можно считать набором клавиш. В клавиатуре имеется только одна клавиша «A». Нажатие на эту клавишу является аппаратным событием, отпускание этой клавиши является аппаратным событием. Во-вторых, клавиатура также является устройством ввода, которое генерирует отображаемые символы.

Клавиша «A», в зависимости от состояния клавиш «Ctrl», «Shift» и «CapsLock», может стать источником нескольких символов. Обычно, этим сомволом является строчная «a». Если же нажата клавиша «Shift» или установлен режим «CapsLock», то этим символом является прописная «A». Если нажата клавиша «Ctrl», этим символом является «Ctrl+A».

Для сочетаний двух аппаратных событий, которые генерируют отображаемые символы, Windows посылает программе оба аппаратных и одно символьное сообщение. Некоторые клавиши не генерируют символов. Это клавиши переключения, функциональные клавиши, клавиши управления курсором и специальные клавиши, такие как «Insert» и «Delete». Для таких клавиш Windows генерирует только аппаратные сообщения.

2.2.3 Аппаратные сообщения

Когда пользователь нажимает клавишу, Windows помещает сообщение WM_KEYDOWN, либо сообщение WM_SYSKEYDOWN в очередь сообщений окна, имеющего фокус ввода. Когда клавиша отпускается, Windows помещает в очередь сообщение WM_KEYUP, либо сообщение WM_SYSKEYUP.

Несистемные аппаратные сообщения: WM_KEYDOWN, WM_KEYUP Cистемные аппаратные сообщения: WM_SYSKEYDOWN, WM_SYSKEYUP

Системные аппаратные сообщения WM_SYSKEYDOWN и WM_SYSKEYUP более важны для Windows, чем для приложений. Эти сообщения генерируются при нажатии клавиш в сочетании с клавишей «Alt» и вызывают опции меню программы или системного меню, или используются для системных функций, таких как смена активного приложения («Alt+Tab»).

Программы обычно игнорируют сообщения WM_SYSKEYDOWN и WM_SYSKEYUP и передают их в функцию DefWindowProc(). Оконная функция, получает другие сообщения, которые являются результатом этих аппаратных сообщений клавиатуры (например, выбор меню).

При необходимости включить в текст программы инструкции для обработки системных аппаратных сообщений клавиатуры следует после обработки этих сообщений передать их в функцию DefWindowProc(), чтобы Windows могла по-прежнему их использовать в обычных целях:

Несистемные сообщения WM_KEYDOWN и WM_KEYUP обычно генерируются для клавиш, которые нажимаются и отпускаются без участия клавиши «Alt». Обычно сообщения о нажатии и отпускании появляются парами. Однако если пользователь оставит клавишу нажатой так, чтобы включился автоповтор, то Windows посылает оконной функции серию сообщений WM_KEYDOWN и одно сообщение WM_KEYUP, когда клавиша будет отпущена. Ниже приведен фрагмент оконной функции, который обрабатывает нажатие клавиш:

case WM_KEYDOWN:

MessageBox(hWnd, "Message WM_KEYDOWN", "Key", MB_OK); break;

case WM_KEYUP:

MessageBox(hWnd, "Message WM_KEYUP", "Key", MB_OK); break;

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

Параметры wParam и lParam аппаратных сообщений клавиатуры ничего не сообщают о состоянии клавиш «Shift», «Ctrl», «Alt» и клавишпереключателей «CapsLock», «NumLock», «ScrollLock». Приложение может получить текущее состояние любой виртуальной клавиши с помощью функции GetKeyState(). Функция GetKeyState() не отражает состояние клавиатуры в реальном времени и возвращает состояние клавиши на момент, прихода сообщения. Функция GetKeyState() не позволяет получать информацию о клавиатуре независимо сообщений. Программе необходимо получить сообщение от клавиатуры до вызова функции GetKeyState().

Если старший бит возвращаемого значения равен 1 — клавиша нажата если равен 0 — клавиша отпущена. Если младший бит равен 1 — клавиша включена если равен 0 отключена.

Информацию о текущем положении клавиши можно получить при помощи функции GetAsyncKeyState(). Ниже приведен фрагмент оконной функции, в котором обрабатываются нажатия комбинаций клавиш «Shift»+«Space» и «Ctrl»+«F1»:

case WM_KEYDOWN:{

int v_key = (int) wParam; // Код виртуальной клавиши switch(v_key) {

// Нажатие клавиши Space (пробел) case VK_SPACE:

// Состояние на момент события if(GetKeyState(VK_SHIFT)<0){

} MessageBox(hWnd, "<Shift+Space>", "<Space>", MB_OK); break;

// Нажатие клавиши F1 для определения case VK_F1:

// Текущее состояние if(GetAsyncKeyState(VK_CONTROL)<0){

} MessageBox(hWnd, "<Ctrl+F1>", "<F1>", MB_OK);

}

break;

break;

}

2.2.4 Символьные сообщения

ОС Windows обеспечивает преобразования аппаратных сообщений клавиатуры в символьные сообщения с учетом информации о положении клавиш «Shift», «CapsLock». Преобразование выполняется в цикле обработки сообщений.

while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMesage(&msg);

}

Функция TranslateMessage() преобразует аппаратные сообщения клавиатуры в символьные сообщения. Если этим сообщением является WM_KEYDOWN или нажатие клавиши в сочетании с клавишей «Shift» генерирует символ, тогда TranslateMessage() помещает символьное сообщение в очередь сообщений. Символьное сообщение будет следующим, после сообщения о нажатии клавиши, которое функция GetMessage() извлечет из очереди.

Несистемное символьное сообщения — WM_CHAR, системное – WM_SYSCHAR. Сообщение WM_CHAR является следствием сообщения WM_KEYDOWN. Параметр оконной функции wParam содержит код символа ASCII.

Следующий фрагмент оконной функции иллюстрирует обработку символьного сообщения WM_CHAR:

case WM_CHAR:{

char code = (char) wParam; // ASCII-код символа char mas[]="\0";

switch(code){ case 'A':

MessageBox(hWnd, "Character A", "MSG", MB_OK); break;

case 'a':

MessageBox(hWnd, "Character a", "MSG", MB_OK); break;

default:

mas[0] = code;

}

MessageBox(hWnd, mas, "MSG", MB_OK);

break;

}

2.2.5 Обработка сообщений мыши

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

}
break;
}

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

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

Таблица 1 — Сообщения мыши

Нажатие

Отпускание

Двойное нажатие

 

 

(Double click)

 

 

 

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

 

 

 

WM_RBUTTONDOWN

WM_RBUTTONUP

WM_RBUTTONDBLCLK

 

 

 

WM_MBUTTONDOWN

WM_MBUTTONUP

WM_MBUTTONDBLCLK

 

 

 

Для всех сообщений, связанных с рабочей областью, значение параметра lParam содержит координаты мыши относительно верхнего левого угла рабочей области окна. Младшее слово – это координата Х, а старшее – координата Y Координаты X и Y можно извлечь из параметра lParam с помощью макросов LOWORD и HIWORD.

Значение параметра wParam показывает состояние кнопок мыши и клавиш «Shift», «Ctrl». Параметр wParam можно проверить с помощью битовых масок, определенных в заголовочных файлах:

MK_LBUTTON

– левая кнопка нажата

MK_RBUTTON

– правая кнопка нажата

MK_MBUTTON

– средняя кнопка нажата

MK_SHIFT

– клавиша «Shift» нажата

MK_CONTROL

– клавиша «Ctrl» нажата

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

case WM_MOUSEMOVE:{

//

Cостояние кнопок мыши

UINT key_state = wParam;

int x_pos = LOWORD(lParam); //

Горизонтальная позиция курсора

int y_pos = HIWORD(lParam); //

Вертикальная позиция курсора

if(key_state & MK_RBUTTON){

 

 

HDC hDC=GetDC(hWnd);

 

0);

SetPixel(hDC, x_pos, y_pos,

ReleaseDC(hWnd, hDC);

 

 

Обработка комбинаций клавиш типа «клавиатура+мышь» показана в следующем примере. Фрагмент программы обрабатывает нажатия «Shift»+«Rclick» и «Ctrl»+«Rclick»

case WM_RBUTTONDOWN:{

UINT key_state = wParam; // Состояние кнопок мыши if (key_state & MK_SHIFT){

}

MessageBox(hWnd, "<Shift>+<Rclick>", "MSG", MB_OK);

if (key_state & MK_CONTROL){

}

MessageBox(hWnd, "<Ctrl>+<Rclick>", "MSG", MB_OK);

break;

}

2.2.6 Двойное нажатие мыши (Double click)

Оконная функция будет получать сообщения двойного нажатия мыши только в случае заданного идентификатора CS_DBLCLKS при задании стиля класса окна (перед вызовом функции RegisterClass), например:

wndclass.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;

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

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDOWN

WM_LBUTTONUP

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

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

WM_LBUTTONUP

Сообщение WM_LBUTTONDBLCLK просто заменяет второе сообщение WM_LBUTTONDOWN.

2.2.7 Захват мыши

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

необходимо произвести захват мыши.

Для захвата мыши используется функция SetCapture(). После вызова этой функции, Windows посылает все сообщения мыши в оконную функцию того окна, чей дескриптор окна был передан в функцию SetCapture(). Сообщения мыши в захваченном состоянии всегда остаются сообщениями рабочей области, даже если мышь оказывается за пределами рабочей области окна. Параметр lParam содержит положение мыши в координатах рабочей области. Эти координаты, могут стать отрицательными, если мышь окажется левее или выше рабочей области. Освободить мышь, (возвратить обработку мыши в нормальный режим), можно при помощи функции ReleaseCapture().

2.2.8 Использование таймера

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

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

Инициализировать таймер программе можно при помощи вызова функции SetTimer(). Функция SetTimer содержит параметр, задающий интервал в миллисекундах – это значение определяет период, с которым Windows посылает программе сообщения WM_TIMER.

Для остановки потока сообщений от таймера необходимо вызвать функцию KillTimer(). Вызов KillTimer() очищает очередь сообщений от всех необработанных сообщений WM_TIMER.

Сообщения таймера ставятся в очередь сообщений приложения и обрабатываются как все остальные сообщения. Поэтому, если приложение задает интервал 1000 миллисекунд, то не гарантируется получение сообщения каждую секунду (интервал будет колебаться). Если приложение занято больше чем секунду, то оно вообще может не получить ни одного сообщения WM_TIMER в течение этого времени. Таймер можно использовать одним из двух способов, в зависимости от параметров функции SetTimer().

Первый способ использования таймера заставляет Windows посылать сообщения WM_TIMER обычной оконной функции приложения. Вызов функции SetTimer() в этом случае обычно имеет вид:

SetTimer(hWnd, 1, 1000, NULL);

первый параметр – дескриптор окна, которое будет получать сообщения WM_TIMER.

второй параметр - идентификатор таймера, значение которого должно быть отлично от нуля. (в примере он установлен в 1). Если приложению необходимо несколько таймеров, для каждого из них следует использовать свой идентификатор.

третий параметр – 32-разрядное беззнаковое целое, которое задает

интервал в миллисекундах (значение 1000 задает генерацию сообщений WM_TIMER один раз в секунду).

Поток сообщений WM_TIMER можно в любое время остановить (даже во время обработки сообщения WM_TIMER), вызвав функцию:

KillTimer(hWnd, 1);

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

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

Следующий фрагмент оконной функции устанавливает таймер при создании окна (сообщение WM_CREATE), после чего программа начинает получать события WM_TIMER с интервалом 1 секунда. При закрытии окна (сообщение WM_DESTROY) таймер уничтожается.

#define TIMER_ONE 1 case WM_CREATE:

SetTimer(hWnd, TIMER_ONE, 1000, NULL); break;

case WM_TIMER:{

UINT timer_id = wParam;

static char mas[]="0"; if (mas[0]>='9'){

mas[0] = '0'; } else {

}

mas[0]++;

HDC hDC = GetDC(hWnd);

TextOut(hDC, 20, 40, mas, strlen(mas));

ReleaseDC(hWnd, hDC);

break;

}

case WM_DESTROY: KillTimer(hWnd, TIMER_ONE); PostQuitMessage(0);

break;

Второй способ использования таймера заставляет Windows пересылать сообщения другой функции этого же приложения. Функция, которая будет получать «таймерные» сообщения, называется функцией обратного вызова

(call-back). Это функция приложения, которую вызывает Windows. Приложение сообщает Windows адрес этой функции, а позже Windows вызывает ее с определенными параметрами. Функция обратного вызова должна определяться с идентификатором CALLBACK. В случае использования функции обратного вызова для таймера, входными параметрами являются те же параметры, что и параметры оконной ф.

Общий вид функции таймера:

void CALLBACK TimerProc(HWND hWnd, UINT iMsg, UINT iTimerID, DWORD dwTime) {

// обработка сообщений WM_TIMER

}

параметр hWnd – дескриптор окна, задаваемый при вызове функции SetTimer().

параметр iMsg всегда будет равен WM_TIMER, потому, что Windows будет посылать функции TimerProc только сообщения WM_TIMER.

параметр iTimerID – идентификатор таймера.

параметр dwTime – системное время.

При использовании функции обратного вызова для обработки сообщений WM_TIMER, четвертый параметр функции SetTimer() заменяется адресом функции обратного вызова:

SetTimer(hWnd, 1, 1000, (TIMERPROC)TimerProc);

Пример использования функции обратного вызова для обработки сообщений от таймера. (Функцию TimerProc() можно разместить перед функцией WinMain())

void CALLBACK TimerProc(HWND hWnd, UINT iMsg, UINT iTimerID, DWORD dwTime){

static char mas[]="0"; if (mas[0]>='9'){

mas[0] = '0'; } else {

}

mas[0]++;

}

SetWindowText(hWnd, mas);

#define TIMER_ONE 1 case WM_CREATE:

SetTimer(hWnd, TIMER_ONE, 1000, (TIMERPROC)TimerProc); break;

case WM_DESTROY: KillTimer(hWnd, TIMER_ONE); PostQuitMessage(0);

break;

2.3 Порядок выполнения работы

На занятии выполните следующие учебные эксперименты:

Написать программу которая создает стандартное окно windows. Текст программы приведен ниже.

#include <windows.h>

// Заголовок оконной функции

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Функция WinMain

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){

char class_name[] = "main_window"; // Название класса окна

char app_title[] = "Simple window"; // Заголовок главного окна

WNDCLASS wc; // Структура для информации о классе окна

HWND hWnd;

// Дескриптор главного окна приложения

 

MSG msg;

// Структура для хранения сообщения

 

// Заполнение структуры WNDCLASS для регистрации класса окна.

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

 

// Заполнение структуры нулями

wc.lpszClassName = class_name;

 

// Имя класса окон

wc.lpfnWndProc = &WndProc;

 

 

// Адрес оконной функции

wc.style = CS_HREDRAW|CS_VREDRAW;

// Стиль класса окон

wc.hInstance = hInstance;

 

 

// Экземпляр приложения

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

// Пиктограмма для окон

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

// Курсор мыши для окон

wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // Кисть для окон

wc.lpszMenuName = NULL;

 

 

// Ресурс меню окон

// Pегистрация класса окна

 

 

 

RegisterClass(&wc);

 

 

 

// Главное окно приложения

 

 

 

hWnd = CreateWindow(

//

Имя класса окон

 

class_name,

 

app_title,

//

Заголовок окна

 

WS_OVERLAPPEDWINDOW, //

Стиль окна

 

20,

 

//

X-координаты

 

20,

 

//

Y-координаты

 

500,

 

//

Ширина окна

 

400,

 

//

Высота окна

 

NULL,

 

//

Дескриптор родительского окна

NULL,

 

//

Дескриптор меню окна

 

hInstance,

//

Дескриптор экземпляра приложения

NULL);

//

Дополнительная информация

if(!hWnd){

// Окно не создано, отобразить предупреждение

}

MessageBox(NULL, "Create: error", app_title, MB_OK|MB_ICONSTOP); return FALSE;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]