Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
.docx
Скачиваний:
5
Добавлен:
30.10.2018
Размер:
824.42 Кб
Скачать

20.Таймери Windows і служба часу.

У прикладних задачах часто виникає необхідність тимчасової синхронізації тих чи інших процесів. Уявімо собі, наприклад, що комп'ютер використовується для управління експериментальної або виробничої установкою. Тоді однією з функцій керуючої програми може бути періодичний висновок на екран поточного стану установки - значень діючих у ній електричних або магнітних полів, тисків, температур і т. д. Інший приклад "одноразової" тимчасової синхронізації - запуск (або завершення) якого-небудь процесу через заданий інтервал часу, наприклад, через 10 с або 15 хв. Якщо інтервал часу необхідно витримати з високою точністю, то для його завдання доводиться використовувати спеціальні апаратні засоби (автономні або пов'язані з комп'ютером) - вимірювачі тимчасовий інтервалів, коли ж високої точності не потрібно, то цілком можна скористатися машинним таймером. Безпосередній доступ до фізичного машинному таймером, як і до інших апаратних засобів комп'ютера, в Windows заборонено, однак Windows надає прикладному програмістові функції, що дозволяють встановити в додатку необхідну кількість програмних таймерів, за допомогою яких програма може забезпечити тимчасову синхронізацію і завдання временньГх інтервалів.

Встановлення таймера

Додаток встановлює, чи активізує, таймер, викликаючи функцію SetTimerf (). Ця функція має наступний прототип: UINT SetTimer (

 HWNDhwnd, / / ​​Дескриптор вікна, з яким пов'язаний цей таймер

UINTidTimer, / / ​​Ідентифікатор таймера

UINTuTimeout, / / ​​Період спрацьовування таймера

TIMERPROCtmprc / / Прикладна процедура обслуговування таймера);

Через параметр hwnd системі Windows передається дескриптор вікна, для якого ус-встановлюється даний таймер. Сама функція SetTimer () може бути викликана в будь-якому місці програми, проте, вказавши дескриптор hwnd, ми пов'язуємо таймер з конкретним вікном, у віконну функцію якого будуть надходити повідомлення WMTIMER.Параметр idTimer визначає номер, який ми присвоюємо даному таймером. При установці єдиного таймера цей номер не має значення і на його місці можна вказати 0, але за наявності декількох таймерів номер, присвоєний таймером, дозволяє визначити, від якого саме таймера прийшло дане повідомлення WM_TIMER. Параметр uTimeout дозволяє встановити період спрацювання даного таймера. Таймер після свого встановлення починає періодично з інтервалом uTimeout генерувати повідомлення WM TIMER, що надходять у вікно hwnd. Цей временнбй інтервал задається в мілісекундах, що, строго кажучи, не має сенсу, оскільки період відліку часу програмним таймером має величину близько 50 мс. Останній параметр, tmprc, дає можливість організувати обслуговування таймера трохи інакше. Параметр є ім'я CALLBACK-функції, яка повинна бути визначена в програмі і містити процедуру прикладної обробки переривання від таймера.Якщо цей параметр вказаний, то при кожному спрацьовуванні таймера система Windows буде безпосередньо, в обхід віконної функції, викликати функцію tmprc (). Якщо врахувати, що повідомлення WMTIMER надсилаються функцією Dispatch Message () у віконну функцію в останню чергу, коли в черзі повідомлень прогвання немає інших повідомлень, а CALLBACK-функція викликається безпосередньо, то ясно, що другий спосіб повинен забезпечити більш оперативну обробку сигналів таймера. Якщо повідомлення від таймера передбачається обробляти звичайним чином, по-засобом віконної функції, то на місці останнього параметра функції SetTimerO вка зується NULL. Функція SetTimerO. у разі свого успішного виконання (нормальної установки таймера) повертає номер даного таймера, тобто фактично значення другого параметра.Як вже зазначалося, таймер після свого встановлення починає періодично генерувати повідомлення із заданим інтервалом; якщо таймер треба зупинити, використовується функція KillTimer (hwnd, idTimer) з дескриптором вікна та номером таймера в якості параметрів. Зокрема, можна зупинити таймер у прикладній функції обробки його повідомлення. У цьому випадку ми отримаємо режим завдання одноразового тимчасового інтервалу.

Мультимедійні таймери

Основним недоліком звичайних таймерів Windows є невисока гранична частота (18,2 Гц) і, як наслідок цього, низька тимчасовий дозвіл (55 мс). Якщо скористатися таким таймером для плавного переміщення зображення будь-якого об'єкта по екрану, то при вирішенні екрана 800x600 пікселів об'єкт витратить на проходження всього екрану гоорізонталі більше 40 с, тобто рух його буде надзвичайно повільним. Для отримання більш високих швидкостей переміщення і для відліку інтервалів часу з більш високою точністю можна використовувати мультимедійні таймери, граничне вирішення яких становить 1 мс, що відповідає частоті 1 кГц. Для роботи з мультимедійними таймерами передбачена невелика група спеціальних функцій, імена яких починаються зі слова time (починається зі строчнойбукви, що нетипово для імен функцій Windows). Розглянемо кілька варіантів установки і використання мультимедійного таймера.Ізмереніе інтервалів часу У процесі оптимізації програм може виникнути необхідність виміряти час виконання того чи іншого фрагмента програми. Для цього можна використовувати мультимедійну функцію timeGeiTime (), яка повертає час у мілісекундах, що минув від останнього завантаження операційної системи:

DWOR D tl, t2, t3; / / Змінні для запису часу

tl = timeGetTime ();...// Контрольований фрагмент програми

t2 = timeGetTime ();

t3 = t2-tl; / / Час виконання контрольованого фрагмента в мс

Далі не складе труднощів за допомогою функції wsprintf () перетворити отриманий час у символьну рядок і вивести або у вікно повідомлення, або безпосередньо в головне або якесь інше вікно програми. Організація періодичного процесу Встановлення та використання мультимедійного таймера вимагає виконання цілого ряду дій. Перш за все за допомогою функції timeBeginPeriod () задається необхідний тимчасовий дозвіл встановленого таймера в мілісекундах. Хоча мінімальне значення параметра цієї функції становить 1 мс, проте слід мати на увазі, що встановлений таймер активно використовує ресурси операційної системи і при малому значенні часу дозволу або при встановленні декількох таймерів системі може не вистачити ресурсів, що призведе до її аварійної зупинки. Наступним етапом є установка тимчасового події, яка виконується за допомогою функції timeSetEvent (). В якості параметрів цієї функції вказується, зокрема, часовий інтервал спрацьовування таймера, а також адресу тієї прикладної функції зворотного виклику, яка буде активізуватися при кожному його спрацьовуванні. Знищення мультимедійного таймера вимагає виклику двох функцій: timeEndPeriodO, що скасовує встановлене раніше дозвіл таймера, і timeKillEvent (), яка припиняє дію всіх системних процесів, пов'язаних з роботою мультимедійного таймера. Фрагмент програми, в якій встановлюється мультимедійний таймер, може виглядати таким чином:

timeBeginPeriodd); / / 'Встановимомаксимальнийдозвіл

MMRESUL T mmr = timeSetEvent (10, l, TimeProc, 0, TIME_PERIODIC);

Якпараметрифункції timeSetEvent () вказуєтьсяперіодйогоспрацьовування (10 мсвприкладі), значеннявстановленогоранішедозволи (1 мс), ім'яприкладноїфункціїобробкиперериваньвідтаймера (наприклад, TimeProc), довільнеданекористувача, якебудепередановцюфункцію (у нас 0), а також символічна константа, що задає режим роботи таймера. Функція установки таймера повертає (в змінну типу MMRESULT) його номер, який призначається системою і використовується потім нами при знищенні даного таймера.Прикладна функція TimeProc (), викликається в даному прикладі кожні 10 мс, призначена для виконання необхідних періодичних дій. Складність, однак, полягає в тому, що в цій функції заборонений виклик будь-яких функцій Windows, крім мультимедійних, а також функції PostQuitMessage (). У результаті типовий текст функції TimeProc () виглядає наступним чином:

void CALLBACK TimeProc (UINT, UINT, DWORD, DWORD, DWORD) {

PostMessagelhwnd, WMJJSER, (WPARAM) parml, (LPARAM) parm2);}

Виклик функції PostMessage () призводить до посилки у вікно hwnd нашого застосування повідомлення користувача з кодом WMUSER, до складу якого входять два довільних параметра parml і parm2. Для Windows код WM_USER (він дорівнює 0x400), при використанні його в рамках вікон прикладних класів, нічого не означає, тому що стандартні повідомлення Windows мають коди від 0 до WMUSER-1, проте ми можемо обробляти повідомлення WM_USER в нашій віконної функції нарівні з рештою (системними) повідомленнями: LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,

WPARAM wParam, LPARAM lParam) {

switch (msg) {

HANDLE_MSG (hwnd, WM_CREATE, OnCreate);

HANDLE_MSG (hwnd, WM_PAINT, OnPaint);

HANDLE_MSG (hwnd, WM_DESTROY, OnDestroy);

... / / Обробкаіншихповідомлень Windows

default:return (DefWindowProc (hwnd, msg, wParam, lParam));

case WM_USER:

OnUser (wParam, lParam);

Визов прикладної функції обробки повідомлень 11 від мультимедійного таймера На виконання прикладної функції OnUser (), якій через її аргументи wParam і lParam передаються (за необхідності) наші параметри parml і parm2, вже не накла-вуватися ніяких обмежень; в ній можна виконувати будь-які дії і , зокрема, викликати будь-які функції Windows. Слід тільки мати на увазі, що функція OnUser () не викликається безпосередньо перериванням від таймера; повідомлення WMUSER надходить в чергу повідомлень додатку, а функція OnUser () буде викликана, лише коли дійде черга до обробки цього повідомлення. Описаний тут механізм іноді називають відстроченої або відкладеної обробкою. Після того, як необхідність у періодичних діях відпала, мультимедійний таймер необхідно знищити:

timeEndPeriod (l); / / Вказується заданий раніше дозвіл

timeKillEvent (mmr); / / Вказується номер даного таймера

Завдання одноразового інтервалу часу При необхідності відпрацювання одноразового інтервалу часу необхідно виконати всі описані вище дії, тільки в якості режиму встановлення тимчасового події вказується константа TIMEONESHOT:

timeBeginPeriod (l); / / Встановимомаксимальнийдозвіл

mmr = timeSetEvent (1000, 1, TimeProc, 0, TIME_ONESHOT);

void CALLBACK TimeProc (UINT, UINT, DWORD, DWORD, DWORD) {

PostMessagethwnd, WM_USER, (WPARAM) parml, (LPARAM) parm2);

time EndPeriod (l); / / скасування встановленого раніше дозволи

.. timeKillEvent (mmr); / / У припущенні, що mmr - глобальна змінна

У даному варіанті повідомлення WMUSER надсилається лише один раз після закінчення точно 1с (з похибкою в 1 мс). У прикладній функції TimeProc обробки цього зі спілкування, крім активізації змістовних дій (за допомогою посилки повідомлення WM_USER), необхідно скасувати встановлене раніше тимчасовий дозвіл, як це і показано в наведених вище рядках.

21 Створити програму у Windows (Win32 API), яка забезпечує роботу з наступними ресурсами: строковий ресурс, піктограму, курсор миші. Строковий ресурс потрібен використовуватися в якості заголовка вікна програми; піктограма – відображається в левом верхнім куті вікна програми; курсор миші – використовується для візуалізації покажчика миші при його проходженні по вікну програми.

Resource.rc

#include "resource.h"

IDC_CURSOR1 CURSOR "cursor1.cur"

IDI_ICON1 ICON "icon1.ico"

STRINGTABLE

BEGIN

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

END

Resource.h

#define IDS_WINCAPTION 101

#define IDC_CURSOR1 102

#define IDI_ICON1 103

Main.cpp

#include <windows.h>

#include "resource.h"

HINSTANCE hInst;

LRESULT CALLBACK WinProc(HWND, UINT, UINT, LONG);

LRESULT CALLBACK WinProc (HWND hWnd, UINT Message, UINT wParam, LONG lParam )

{

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

switch(Message)

{

case WM_DESTROY:

PostQuitMessage(0);

return 0;

}

return DefWindowProc(hWnd,Message,wParam, lParam);

}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR IpszCmdParam, int nCmdShow)

{

HWND hWnd;

WNDCLASS WndClass;

MSG Msg;

char szClassName[] = "ResourcesDemo";

hInst = hInstance;

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

WndClass.style = CS_HREDRAW | CS_VREDRAW;

WndClass.lpfnWndProc = WinProc;

WndClass.cbClsExtra = 0;

WndClass.cbWndExtra = 0;

WndClass.hInstance = hInst ;

WndClass.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_ICON1));

WndClass.hCursor = LoadCursor (hInst, MAKEINTRESOURCE(IDC_CURSOR1));

WndClass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);

WndClass.lpszMenuName = NULL;

WndClass.lpszClassName = szClassName;

if ( !RegisterClass(&WndClass) )

{

MessageBox(NULL,"Cannot register class", "Error", MB_OK);

return 0;

}

char szWinCaption[64];

LoadString(hInst, IDS_WINCAPTION, szWinCaption, 64);

// создает окно

hWnd = CreateWindow(szClassName, szWinCaption,

WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance,NULL);

// сообщение об ошибке

if(!hWnd)

{

MessageBox(NULL,"Cannot create window", "Error",MB_OK);

return 0;

}

/* показует наше окно */

ShowWindow(hWnd,nCmdShow);

UpdateWindow(hWnd);

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

while(GetMessage(&Msg, NULL, 0, 0))

{

TranslateMessage(&Msg);

DispatchMessage(&Msg);

}

return Msg.wParam;

}

22.Ресурси: меню та діалоги. Робота з редактором ресурсів Visual C++.

Определение меню в виде ресурса

Меню можно создать одним из трех способов:

● на основе шаблона меню, определенного в файле описания ресурсов;

● при помощи функций CreateMenu и AppendMenu;

● на основе шаблона меню, определяемого в памяти во время выполнения программы при помощи функции LoadMenuIndirect.

Шаблон меню

Определение меню в файле описания ресурсов имеет следующий вид:

имя_меню MENU DISCARDABLE

BEGIN

Описание 0-го пункта

Описание 1-го пункта

Описание (n-1)-гo пункта

END

Описание пункта-подменю имеет следующий вид:

POPUP имя_пункта [, параметры]

BEGIN

Описание 0-го пункта

Описание 1-го пункта

Описание (n-1)-гo пункта

END

Описание пункта-команды имеет следующий вид:

MENUITEM имя_пункта идентификатор [. параметры]

Если в имени пункта встречается символ &, то следующий за ним символ является мнемоническим символом.

Если вместо имени пункта использовано слово SEPARATOR, это приведет к тому, что вместо пункта меню будет отображена горизонтальная разделительная линия между группами пунктов. Шаблон меню создается автоматически при использовании редактора меню.

Элементы меню

Любой элемент меню может быть описан с помощью структуры MENUITEMINFO:

typedef struct

{

UINT cbSize;

UINT fMask;

UINT fType;

UINT fState;

UINT wID;

HMENU hSubMenu;

HNITMAP hbmpChecked;

HNITMAP hbmpUnchecked;

DWORD dwItemData;

LPTSTR dwTypeData;

UINT cch;

} MENUITEMINFO;

Например:

static MENUITEMINFO mii;

В поле cbsize записывают размер структуры MENUITEMINFO в байтах:

mii.cbSize=sizeof(MENUITEMINFO);

Значение поля fMask указывает операционной системе, со значениями каких полей структуры mii нужно работать. Смысл работы зависит от действия, которое выполняют над элементом меню. Значение поля fMask представляет собой комбинацию констант из следующей таблицы:

При создании элемента меню значение поля fMask задают следующим образом:

mii.fMask=MIIM_STATE | MIIM_TYPE | MIIM_SUBMENU | MIIM_ID;

В данном случае операционная система при создании элемента будет учитывать значения полей fState, fType, dwTypeData, hSubMenu и wID структуры mii.

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

mii.fMask=MIIM_STATE | MIIM_ID;

В данном случае операционная система при создании элемента будет учитывать значения полей fState и wID структуры mii.

Вопрос 23 Створити програму у Windows (Win32 API), яка забезпечує роботу з наступними ресурсами: акселератори. Акселератори клавіш керування курсором "уверх" и "униз" - для зменшення та збільшення інтервалу між повідомленнями WM_TIMER.

Resource.rc

#include <windows.h>

#include "resource.h"

IDR_ACCELERATOR1 ACCELERATORS

BEGIN

VK_UP, ID_ACCELUP, VIRTKEY, NOINVERT

VK_DOWN, ID_ACCELDOWN, VIRTKEY, NOINVERT

END

Resource.h

#define IDR_ACCELERATOR1 101

#define ID_ACCELUP 40001

#define ID_ACCELDOWN 40002

#define IDT_TIMER1 1

Main.cpp

#include <windows.h>

#include <stdio.h>

#include "resource.h"

HINSTANCE hInst;

int TimerInt;

LRESULT CALLBACK WinProc(HWND, UINT, UINT, LONG);

char Clock[] = {'|', '/', '-', '\\'};

int CurrChar;

LRESULT CALLBACK WinProc (HWND hWnd, UINT Message, UINT wParam, LONG lParam )

{

char caption[64];

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

switch(Message)

{

case WM_TIMER:

CurrChar++;

if (CurrChar > 3) CurrChar = 0;

sprintf(caption, "Acselerators Demo %c", Clock[CurrChar]);

SetWindowText(hWnd, caption);

break;

case WM_COMMAND:

switch (LOWORD(wParam))

{

case ID_ACCELUP:

TimerInt += 100;

SetTimer(hWnd, IDT_TIMER1, TimerInt, NULL);

break;

case ID_ACCELDOWN:

TimerInt -= 100;

SetTimer(hWnd, IDT_TIMER1, TimerInt, NULL);

break;

} break;

case WM_DESTROY:

PostQuitMessage(0);

return 0;

}

return DefWindowProc(hWnd,Message,wParam, lParam);

}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR IpszCmdParam, int nCmdShow)

{

HWND hWnd;

WNDCLASS WndClass;

MSG Msg;

char szClassName[] = "AcseleratorsDemo";

hInst = hInstance;

HACCEL hAccel;

TimerInt = 1000;

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

WndClass.style = CS_HREDRAW | CS_VREDRAW;

WndClass.lpfnWndProc = WinProc;

WndClass.cbClsExtra = 0;

WndClass.cbWndExtra = 0;

WndClass.hInstance = hInst ;

WndClass.hIcon = LoadIcon (0, IDI_APPLICATION);

WndClass.hCursor = LoadCursor (0, IDC_ARROW);

WndClass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);

WndClass.lpszMenuName = NULL;

WndClass.lpszClassName = szClassName;

if ( !RegisterClass(&WndClass) )

{

MessageBox(NULL,"Cannot register class", "Error", MB_OK);

return 0;

}

// создает окно

hWnd = CreateWindow(szClassName, "Acselerators Demo |",

WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance,NULL);

// сообщение об ошибке

if(!hWnd)

{

MessageBox(NULL,"Cannot create window", "Error",MB_OK);

return 0;

}

/* показует наше окно */

ShowWindow(hWnd,nCmdShow);

UpdateWindow(hWnd);

SetTimer(hWnd, IDT_TIMER1, TimerInt, NULL);

hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_ACCELERATOR1));

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

while(GetMessage(&Msg, NULL, 0, 0))

{

if (!TranslateAccelerator(hWnd, hAccel, &Msg))

{

TranslateMessage(&Msg);

DispatchMessage(&Msg);

}

}

return Msg.wParam;

}

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