Код панели управления потоком приведен в листинге 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. Этот уровень гарантирует то, что приложение подучит достаточное количество времени процессора для того, чтобы обеспечить пользовательский ввод вне зависимости от того, какой приоритет у управляемого потока. Этот шаг нужен не во всех случаях. Необходимость его выполнения вы можете установить опытным путем.
|