Главная arrow С++ (часть 3) arrow Панель управления потоком

Панель управления потоком

Код панели управления потоком приведен в листинге 3.1. Он хранится в файле tcp.cpp.
IgjfcTHHr 3.1. Панель управления потоком ]
#include <map> ¦include <windows.h> ¦include "panel.h" using namespace std;
const int NUMPRIORITTES = 5; const int OFFSET = 2;
// Массив строк для списка приоритетов.
char priorities[NUMPRIORITIES][80] = {•
"Lowest",
"Below Normal",
"Normal",
"Above Normal",
"Highest" };
// Класс для панели управления потоком, class ThrdCtrlPanel {
// Данные управляемого потока, struct Threadlnfo   {
HANDLE hThread; // дескриптор потока int priority; // текущий приоритет bool suspended; // true, если   поток приостановлен Threadlnfo(HANDLE ht, int p, bool s)  { hThread = ht; priority = p; suspended = s;
}
};
// Это отображение (map) содержит данные типа Threadlnfo для каждой // активной панели управления потоком, static map<HWND, ThreadInfo> dialogmap;
public:
// Конструирует панель управления. ThrdCtrlPanel(HINSTANCE hlnst, HANDLE hThrd); // callback-функция панели управления.
static LRESULT CALLBACK ThreadPanel(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
};
// Определяет статический элемент dialogmap. map<HWND, ThrdCtrlPanel::ThreadInfo> ThrdCtrlPanel::dialogmap;
// Создает панель управления потоком. ThrdCtrlPanel::ThrdCtrlPanel(HINSTANCE hlnst, HANDLE hThrd)
{
Threadlnfo ti(hThrd,
GetThreadPriori ty(hThrd)+OFFSET, false);
// Окно-владелец - рабочий стол.
jjflND hDialog = CreateDialog(hInst,  "ThreadPanelDB", NULL,
(DLGPROC) ThreadPanel);
// Помещает информацию об этом диалоговом окне в отображение тар. dialogmap.insert(pair<HWND, ThreadInfo>(hDialog, ti));
// Устанавливает заголовок панели управления, char str[80] = "Control Panel for Thread "; char str2[4];
_itoa(dialogmap.size(), str2, 10) ; strcat(str, str2); SetWindowText(hDialog, str);
// Сдвигает каждый экземпляр диалогового окна. MoveWindow(hDialog, 30*dialogmap.size(),
30*dialogmap.size(),
300, 250, 1);
// Обновляет значение приоритета в списке. SendDlgltemMessage(hDialog, IDD_LB,. LB_SETCURSEL, (WPARAM) ti.priority, 0);
// Увеличивает приоритет для гарантированного управления. Вы' можете // изменить или удалить этот оператор в соответствии // с характеристиками вашей среды исполнения. SetThreadPriority (GetCurrentThread (),
THREAD_PRIORITY_ABOVE_NORMAL);
}
11 callback-функция диалогового окна панели управления.
IjRESULT CALLBACK ThrdCtrlPanel:: ThreadPanel (HWND hvmd,
UINT message, WPARAM wParam, LPARAM lParam)
{
int i;
HWND hpbRes, hpbSus, hpbTerm; switch (message)  { case WM_INITDIALOG:
// Инициализирует список значений приоритета. for(i=0; i<NUMPRIORITIES; i++)  { SendDlgltemMessage(hwnd, IDD_LB,
LB_ADDSTRING,  0,   (LPARAM)  priorities[i]);
}
// Устанавливает кнопки приостановки и возобновления потока. hpbSus = GetDlgltern(hwnd. IDD_SUSPEND); hpbRes = GetDlgltern(hwnd, IDD_RESUME);
EnableWindow(hpbSus, true); // делает доступной кнопку Suspend EnableWindow(hpbRes, false); // делает недоступной Resume, return 1; case WM_COMMAND:
map<HWND, ThreadInfo>::iterator p = dialogmap.find(hwnd);
switch (LOWORD (wParam))  { case IDD_TERMINATE:
TerminateThread(p->second.hThread, 0);
/
11 Делает кнопку Terminate недоступной. hpbTerm = GetDlgl tern (hwnd,  IDD_TERMINATE); } EnableWindow(hpbTerm,  false); //не доступна
// Делает кнопки Suspend и Resume недоступными. hpbSus = GetDlgItem(hwnd,  IDD_SUSPEND); hpbRes = GetDlgltern (hwnd, IDD_RESUME); EnableWindow(hpbSus, false); //не доступна Suspend EnableWindow(hpbRes, false); // не доступна Resume
return 1; case IDD_SUSPEND:
SuspendThread(p->second.hThread);
// Устанавливает состояние кнопок Suspend и Resume.
hpbSus = GetDlgItem(hwnd,  IDD_SUSPEND); hpbRes = GetDlgltemfhwnd, IDD_RESUME); EnableWindow (hpbSus, false); //не доступна Suspend EnableWindow(hpbRes, true);    // делает доступной Resume
p->second.suspended = true; return 1; case IDD_RESUME:
ResumeThread(p->second.hThread);
// Устанавливает состояние кнопок Suspend и Resume. hpbSus = GetDlgltemfhwnd,  IDD_JSUSPEND) ; hpbRes = GetDlgItem(hwnd,  IDD_RESUME);
EnableWindow(hpbSus, true);    // делает доступной Suspend EnableWindow(hpbRes, false); //не доступна Resume
p->second.suspended = false; return 1; case IDD_LB:
// Если выбран элемент списка, // изменяет приоритет потока, i f(HIWORD(wParam)==LBN_DBLCLK)  { p->second.priority = SendDlgItemMessage(hwnd, IDD_LB, LB_GETCURSEL, 0,  0);
SetThreadPriori ty(p->second.hThread,
p->second.priority-OFFSET);
}
return 1; case IDCANCEL:
// Если поток приостановлен, когда панель закрывается,
// возобновляет поток, чтобы предотвратить возникновение тупика.
if(p->second.suspended)
ResumeThread(p->second.hThread); p->second.suspended = false;
}
// Удаляет этот поток из списка, dialogmap.erase(hwnd);
// Закрывает панель. DestroyWindow(hwnd); return 1;
}
}
return 0;
}
Панели управления требуется следующий файл ресурсов, названный tcp.rc. #include <windows.h> #include "panel.h"
ThreadPanelDB DIALOGEX 20, 20, 140, 110 CAPTION "Thread Control Panel"
STYLE WS_BORDER  | WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU {
DEFPUSHBUTTON "Done",  IDCANCEL,  55,  80,  33,  14 PUSHBUTTON "Terminate",  I DETERMINATE,  10,  20,  42,  12 PUSHBUTTON "Suspend",  IDD_SUSPEND,  10,  35,  42,  12 PUSHBUTTON "Resume", IDD_RESUME,  10, 50, 42,  12 LISTBOX IDD_LB,  65,  20,  63,  42,  LBSJNOTIFY | WS_VISIBLE  |
WS_BORDER I WS_VSCROLL | WS_TABSTOP CTEXT "Thread Priority", IDD_TEXT1, 65, 8, 64,  10 CTEXT "Change State", IDD_TEXT2, 0, 8, 64, 10
}
Панель управления использует следующий заголовочный файл, названный panel.h.
tdefine IDD_LB 200
#define IDD_TERMINATE 202
tdefine IDD_SUSPEND 204
#define IDD_RESUME 206
#define IDD_TEXT1 208
ttdefine IDD_TEXT2 209
Для применения панели управления выполните следующие действия.
1. Включите файл tcp.cpp в вашу программу.
2. Включите tcp.rc в файл ресурсов вашей программы.
3, Создайте поток или потоки, которыми хотите управлять.
4# Инициируйте объект типа ThrdCtrlPanel для каждого потока.
Каждый объект типа ThrdCtrlPanel связывает поток с диалоговым окном,
которое управляет им.
В больших проектах, в которых многочисленные файлы нуждаются в доступе к объектам типа ThrdCtrlPanel, вам может потребоваться файл tcp.h, который содержит объявление этого объекта. Текст файла приведен далее. //Заголовочный файл для класса ThrdCtrlPanel class ThrdCtrlPanel { public:
// Конструирует панель управления.
ThrdCtrlPanel(HINSTANCE hlnst, HANDLE hThrd);
// callback-функция панели управления.
static LRESULT CALLBACK ThreadPanel(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
};
Пристальный взгляд
на панель управления потоком
Давайте внимательнее посмотрим на код панели управления потоком. Он начинается со следующих глобальных объявлений:
const int NUMPRIORITIES = 5; Const int OFFSET = 2;
// Массив строк для списка приоритетов, char priorities [NUMPRIORITIES][80] = {
"Lowest",
"Below Normal",
"Normal",
"Above Normal",
"Highest"
};
Массив priorities содержит строки, которые связаны со значениями приоритета. Он инициализирует список внутри панели управления, который отображает текущее значение приоритета. Количество возможных значений задано в переменной NUMPRIORITIES И ДЛЯ операционной системы Windows равно 5. Таким образом, переменная NUMPRIORITIES определяет число разных значений приоритета, которые может иметь поток (если вы решите адаптировать код для применения в другой операционной системе, возможно, потребуется другое количество). С помощью панели управления вы можете задать одно из следующих значений приоритета:
П THREAD_PRIORITY_ HIGHEST;
П THREAD_PRIORITY_ABOVE_NORMAL; ? THREAD_PRIORITY_NORMAL; П THREAD_PRIORITY_BELOW_NORMAL; П THREAD_PRIORITY_LOWEST.
Два оставшихся значения приоритета, THREAD_PRIORITY_TIME_CRITICAL И THREAD_PRIORITY_IDLE, не поддерживаются панелью управления из-за малого практического значения. Если вы хотите создать приложение, строго соблюдающее заданные параметры времени или сильно от них зависящее (time-critical), лучше присвоить ему класс приоритета, позволяющий функционировать в режиме реального времени (time-critical)1.
Переменная OFFSET определяет величину сдвига, которая будет использоваться при переходе от индексов списка к значениям приоритета. Вам следует помнить, что нормальному приоритету соответствует числовое значение 0. Высший приоритет (THREAD_PRIORITY_HIGHEST) равен 2, а низший (THREAD. PRIORITY_LOWEST) — -2. Поскольку индексы в списке начинаются с 0, необходим сдвиг для преобразования номера элемента списка в значение приоритета.
Далее объявляется класс ThrdCtrlPanel. Объявление начинается следующим образом.
// Класс для панели управления потоком, class ThrdCtrlPanel {
// Данные управляемого потока, struct Threadlnfo   {
HANDLE hThread; // дескриптор потока int priority; // текущий приоритет bool suspended; // true, если приостановлен Threadlnfo(HANDLE ht, int p, bool s)    { hThread = ht; priority = p; suspended = s;
}
Ц Это отображение (тар) содержит данные типа Threadlnfo для каждой // активной панели управления потоком, static map<HWND, ThreadInfo> dialogmap;
Данные управляемого потока содержатся в структуре типа Threadlnfo. Дескриптор потока хранится в переменной hThread. Его приоритет — в переменной priority. Если поток приостановлен, переменная suspended равна true, в противном случае — false.
Статический член dialogmap — контейнер библиотеки STL типа тар (отображение), который связывает данные потока с дескриптором диалогового окна, используемого для управления потоком. Поскольку несколько панелей управления могут быть активны в одно и то же время, должен быть способ определить, какой поток управляется какой панелью. Переменная dialogmap и служит для этой цели.
Конструктор класса ThrdCtrlPanel
Далее приведен код конструктора класса ThrdCtrlPanel. Конструктор передает дескриптор экземпляра приложения (instance handle) и дескриптор управляемого потока. Дескриптор экземпляра необходим для создания диалогового окна панели управления.
// Создает панель управления потоком. ThrdCtrlPanel: .-ThrdCtrlPanel (HINSTANCE hlnst, HANDLE hThrd)
{
Threadlnfo ti(hThrd,
GetThreadPriority(hThrd)+OFFSET, false);
// Окно-владелец - рабочий стол.
HWND hDialog = CreateDialog(hlnst,  "ThreadPanelDB", NULL.
(DLGPROC) ThreadPanel);
11 Помещает информацию об этом диалоговом окне в отображение тар. dialogmap.insert(pair<HWND, ThreadInfo>(hDialog, ti));
11 Устанавливает заголовок панели управления. char str[80] = "Control Panel for Thread "; char str2[4];    •
_itoa(dialogmap.size(), str2, 10); streat(str. str2); SetWindowText(hDialog, str);
// Сдвигает каждый экземпляр диалогового окна. MoveWindow(hDialog, 30*dialogmap.size(),
30*dialogmap.size(),
300, 250,  1);
// Обновляет значение приоритета в списке. SendDlgltemMessage(hDialog, IDD_LB, LB_SETCURSEL, (WPARAM)  ti.priority, 0);
// Увеличивает приоритет для гарантированного управления. //Вы можете изменить или удалить этот оператор в соответствии //с характеристиками вашей среды исполнения. SetThreadPriority(GetCurrentThread(),
THREAD_PRIORITY_ABOVE_NORMAL);
}
Конструктор начинается с создания экземпляра структуры Threadlnfo с именем ti, который содержит начальные характеристики потока. Обратите внимание на то, что значение приоритета получается с помощью вызова функции GetThreadPriorityO для управляемого потока. Далее создается диалоговое окно панели управления с помощью функции createDialogO. Windows API-функция createDialogO создает немодальное диалоговое окно, не зависящее от приложения, его создавшего. Она возвращает дескриптор этого окна, который запоминается в переменной hDialog. Затем переменная hDialog и данные потока, содержащиеся в переменной ti, сохраняются в отображении dialogmap. Таким образом, поток связывается с диалоговым окном, которое им управляет.
Далее устанавливается заголовок диалогового окна для отображения номера потока. Номер потока формируется на основе номера элемента в отображении dialogmap. Альтернативой, которую вы можете попробовать реализовать, служит явная передача имени для каждого потока конструктору класса ThrdCtrlPanel. Для примера, разрабатываемого в этой главе, достаточно номера потока.
Далее окно панели управления слегка сдвигается с помощью другой Windows API-функции MoveWindow (). Такой сдвиг позволит нескольким панелям отображаться на экране без полного перекрытия ранее созданной панели вновь создаваемой. Значение приоритета потока выводится в списке с помощью вызова API-функции SendDlgltemMessage ().
3 конце приоритет основного потока повышается до значения THREAD_ PRIORITY_ABOVE_NORMAL. Этот уровень гарантирует то, что приложение подучит достаточное количество времени процессора для того, чтобы обеспечить пользовательский ввод вне зависимости от того, какой приоритет у управляемого потока. Этот шаг нужен не во всех случаях. Необходимость его выполнения вы можете установить опытным путем.