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

Технология разработки программных систем

..pdf
Скачиваний:
21
Добавлен:
05.02.2023
Размер:
1.31 Mб
Скачать

141

struct SWT {

HANDLE hevBeg; // событие начала

HANDLE hevEnd; // событие завершения

CRITICAL_SECTION cs; // дополнительные поля

};

Структура, помимо полей, определяемых решаемой задачей, содержит два дескриптора событий и критическую секцию.

В главном потоке объявляются две статические переменные swt и hThread, а также проводится инициализация критической секции и создание объектов событий для управления рабочим потоком. Оба события используют "ручное" изменение состояния и первоначально установлены в "сброшено".

static

SWT

swt;

static

HANDLE

hThread;

swt.hevBeg = CreateEvent(NULL, TRUE, FALSE, NULL); swt.hevEnd = CreateEvent(NULL, TRUE, FALSE, NULL); InitializeCriticalSection(&swt.cs);

Затем запускается рабочий поток с обслуживающей функцией ThreadFunc(), которой в качестве параметра передается указатель на структуру swt.

hThread = CreateThread(NULL,0,

ThreadFunc,&swt,0,&idThread);

После этого, возможно в другом обработчике функции главного окна приложения, устанавливается событие hevBeg, что является сигналом рабочему потоку к началу исполнения своего кода.

SetEvent(swt.hevBeg); // Событие установлено

Теперь рассмотрим функцию рабочего потока

DWORD WINAPI ThreadFunc (LPVOID pData)

{

//преобразуем указатель

SWT* pswt = (SWT*)pData;

//бесконечно ожидаем наступления события

/*1*/ WaitForSingleObject(pswt->hevBeg, INFINITE);

 

// продолжаем цикл пока не установлено

/*2*/

// события завершения рабочего потока

while (WaitForSingleObject(pswt->hevEnd, 0)

 

!= WAIT_OBJECT_0) {

 

// вводим критическую секцию и ждем когда

 

// остальные потоки будут заблокированы

 

EnterCriticalSection(&pswt->cs);

142

//некие действия

//в "монопольном" режиме

//сбрасываем критическую секцию

LeaveCriticalSection(&pswt->cs);

}

//сбрасываем событие

ResetEvent(pswt->hEvEnd);

//выход из функции автоматически

//завершает рабочий поток

return 0;

}

В цикле, помеченном через /*1*/, функция рабочего потока будет находиться до тех пор, пока главный поток не установит событие hevBeg. Цикл, помеченный /*2*/, организован иным образом. Функция ожидания только проверяет состояние события hevEnd. Если оно не установлено, то выполняется тело цикла, в противном случае – цикл заканчивается, что ведет к выходу из функции рабочего потока и завершению самого потока.

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

SetEvent(swt.hevEnd);

while (WaitForSingleObject(swt.hevEnd, 200) == WAIT_OBJECT_0);

// в данный момент рабочий поток завершен

Затем выполняется закрытие объектов

CloseHandle(hThread);

CloseHandle(swt.hevBeg);

CloseHandle(swt.hevEnd);

DeleteCriticalSection(&swt.cs);

Используете функцию ожидания Sleep(), только для рабочих потоков. При ее применении в главном потоке он блокируется и не обрабатывает поступающие сообщения.

143

13. Приложение "Тестер файлов"

В заключение рассмотрим исходный текст приложения, предназначенного для проверки целостности файлов на магнитном носителе, например, на дискете. Конечно, операционная система включает в свой состав служебную программу проверки диска. Однако она выполняет проверку для всей поверхности, что порой требует достаточно много времени. Данная утилита проверяет – путем чтения – только дисковые файлы, значит, в некоторых случаях, может быть более эффективной по затратам компьютерных ресурсов. С другой стороны, автор создавал данное приложение с учебными целями и не претендует, чтобы утилита считалась полностью системной.

Перечислим основные идеи и особенности приложения:

1.Для задания имени драйвера, на котором проводится тестирование файлов, используется командная строка приложения. Так как информация из командной строки потребуется в функции окна, определен указатель, объявленный как глобальная переменная.

2.Функция WinMain() обычна. Цикл обработки сообщений тоже не

содержит каких-либо особенностей.

3.В обработчике WM_CREATE функции главного окна создается Win32 элемент управления ListView, который позволяет отображать различную информацию в колонках каждой строки. Приложение использует четыре колонки: имя файла, статус проверки, длина файла и время его создания. Здесь же создаются две кнопки для запуска процесса проверки и его остановки.

4.При нажатии кнопки начала проверки или нажатии клавиши "Enter", главный процесс запускает рабочий поток, который выбирает все файлы на указанном разделе и выполняет их проверку обычным чтением. Если встречается директория, то выполняется рекурсивный вызов функции проверки для этой директории. Результаты проверки отображаются в виде новой строки элемента ListView.

5.При нажатии кнопки останова, процесс сбрасывает дескриптор окна

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

Проект включает три файла:

TestDisk.cpp

исходный код приложения;

Resource.h

файл идентификаторов;

TestDisk.rc

файл ресурсов приложения.

144

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

IDS_ERROR 1

IDS_APP_NAME 2

ID_BUTT_BEG 101

ID_BUTT_END 102

ID_LISTVIEW 103

Файл TestDisk.rc для этого проекта содержит только один ресурс – строковый:

STRINGTABLE DISCARDABLE BEGIN

IDS_ERROR "Ошибка"

IDS_APP_NAME "Тестер файлов на диске " ID_BUTT_BEG "Проверить"

ID_BUTT_END "Прервать"

END

Далее приводится полный текст файла TestDisk.cpp.

13.1. Функция WinMain()

#define STRICT

#define WIN32_LEAN_AND_MEAN #include <Windows.h>

// Заголовочный файл для Win32 элементов управления

#include <CommCtrl.h> #include "Resource.h"

// Используем два таймера

#define TIMER_CLOSE 4444 #define TIMER_WAIT 4445

//Структура для отображения информации

//о тестируемом файле в ListView struct NEW_FILE_DATA {

TCHAR szName [MAX_PATH]; TCHAR szLen [16]; TCHAR szDate [32]; TCHAR szCheck[16];

};

//Структура для параметров

//функции рабочего потока

struct THREADPARAM {

HWND

hWnd;

HWND

hwndList;

DWORD

idThread;

TCHAR

szRoot[MAX_PATH];

};

 

145

////////////////////////////////////////////////////////

// Глабальные переменные

HINSTANCE g_hInst;

LPCSTR g_szCmd;

////////////////////////////////////////////////////////

//Прототипы функций

int

InitApplication

(LPCTSTR szClass);

void

ViewError

(HWND hWnd);

LPTSTR

StrFromRC

(int IDstr);

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

{

//Приложение использует командную строку,

//через которую можно задать диск для проверки.

//Например, "TestDisk.exe D:\"

//В этом случае тестируются файлы на диске D:

//Если параметр не задан, используется A:\

//Сохраняем глобальные переменные

g_hInst = hInstance; g_szCmd = lpCmdLine; if (!*g_szCmd)

g_szCmd = TEXT("a:\\");

//Подключаем библиотеку Win32 стандартных элементов,

//но испотльзовать будем только ListView

INITCOMMONCONTROLSEX commItems; commItems.dwSize = sizeof(commItems); commItems.dwICC = ICC_LISTVIEW_CLASSES; InitCommonControlsEx(&commItems);

// Регистрируем класса главного окна

LPCTSTR szClass = TEXT("FileTester32"); if ( !InitApplication(szClass) ) {

// Возникла ошибка

ViewError(NULL); return -1;

}

// Создание главного окна int proc = 20;//%

int xW = ::GetSystemMetrics(SM_CXSCREEN); int yW = ::GetSystemMetrics(SM_CYSCREEN); int x = (proc*xW)/100;

int y = (proc*yW)/100;

//Шаблон имени окна хранится в ресурсах.

//Добавляем имя тестируемого диска. TCHAR szTitle[128];

lstrcpy(szTitle, StrFromRC(IDS_APP_NAME));

146

lstrcat(szTitle, g_szCmd);

HWND hWnd = ::CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, szClass, szTitle,

WS_OVERLAPPEDWINDOW, x, y, ((100-2*proc)*xW)/100, ((100-2*proc)*yW)/100,

NULL, NULL, g_hInst, NULL); if (!hWnd) {

// Возникла ошибка

ViewError(NULL); return -1;

}

::ShowWindow(hWnd, nCmdShow); ::UpdateWindow(hWnd);

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

MSG msg;

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

}

return 0;

}

13.2.Функция главного окна

//Прототипы функций, используемые в WndProc() HWND CreateListView (HWND hwndParent);

void InitListView (HWND hwndList);

void ResizeListView (HWND hwndList, HWND hwndParent);

void

SwitchButton

(HWND hWnd, TCHAR chMode);

DWORD

WINAPI ThreadFunc (LPVOID pData);

LRESULT CALLBACK WndProc(HWND hWnd,

UINT message, WPARAM wParam, LPARAM lParam)

{

//Используем две статические переменные

//для дескриптора рабочего потока

//и структуры параметров для него.

static

HANDLE

hThread;

static

THREADPARAM

parm;

switch (message) {

case WM_CREATE :

// Создаем элемент управления ListView. parm.hwndList = CreateListView(hWnd); if (parm.hwndList) {

//На одном месте создаем две кнопки

//с разными идентификаторами.

147

//Первая будет запускать процесс проверки,

//а вторая - останавливать.

//Пряча одну и показывая другую,

//организуем эффект переключения.

HWND hButt = ::CreateWindow(TEXT("BUTTON"),

StrFromRC(ID_BUTT_BEG),

WS_CHILD | WS_TABSTOP | WS_VISIBLE, 10, 6, 80, 24,

hWnd, (HMENU)ID_BUTT_BEG, g_hInst, NULL);

if (hButt) {

//Заменяем шрифт для кнопки на тот,

//который используется для ListView. WPARAM pFont = ::SendMessage(parm.hwndList,

WM_GETFONT, 0, 0); ::SendMessage(hButt, WM_SETFONT, pFont,

MAKELPARAM(TRUE, 0));

}

hButt = ::CreateWindow(TEXT("BUTTON"), StrFromRC(ID_BUTT_END), WS_CHILD | WS_TABSTOP,

10, 6, 80, 24,

hWnd, (HMENU)ID_BUTT_END, g_hInst, NULL);

if (hButt) {

WPARAM pFont = ::SendMessage(parm.hwndList, WM_GETFONT, 0, 0);

SendMessage(hButt, WM_SETFONT, pFont, MAKELPARAM(TRUE, 0));

}

}

break;

case WM_SIZE :

// Изменяем размеры ListView. ResizeListView(parm.hwndList, hWnd); return 0;

case WM_SETFOCUS :

// Передаем фокус на элемент ListView. ::SetFocus(parm.hwndList);

break;

case WM_COMMAND : ::SetFocus(parm.hwndList);

if (LOWORD(wParam) == ID_BUTT_BEG) { // Запускаем рабочий поток. InitListView(parm.hwndList); lstrcpy(parm.szRoot, g_szCmd); parm.hWnd = hWnd;

if (hThread) ::CloseHandle(hThread);

148

hThread = ::CreateThread(NULL, 0, ThreadFunc, &parm, 0, &parm.idThread);

SwitchButton(hWnd, TEXT('E'));

}

else if (LOWORD(wParam) == ID_BUTT_END) {

//Останавливаем рабочий поток

//и запускаем таймер ожидания. parm.hWnd = 0;

::SetTimer(hWnd, TIMER_WAIT, 100, NULL); SwitchButton(hWnd, TEXT('B'));

}

return 0;

case WM_NOTIFY : {

NMHDR& nm = *(NMHDR*)lParam;

if (nm.hwndFrom == parm.hwndList && nm.code == LVN_KEYDOWN) {

// Определяем какая клавиша нажата. LV_KEYDOWN& km = *(LV_KEYDOWN*)lParam; if (km.wVKey == VK_RETURN) {

//Нажата клавиша "Enter".

//Имитируем нажатие соответствующей кнопки. ::PostMessage(hWnd, WM_COMMAND,

MAKEWPARAM(((parm.idThread) ? ID_BUTT_END : ID_BUTT_BEG), BN_CLICKED), 0);

}

else if (km.wVKey == VK_ESCAPE)

//Нажата клавиша "Esc".

//Имитируем нажатие соответствующей кнопки. ::PostMessage(hWnd, WM_CLOSE, 0, 0);

return 0;

}

break; }

case WM_TIMER : ::KillTimer(hWnd, wParam); if (parm.idThread) {

// Продолжаем ждать окончания рабочего потока. ::SetTimer(hWnd, wParam, 100, NULL);

}

else {

// Поток завершен. ::CloseHandle(hThread); hThread = NULL;

if (wParam == TIMER_CLOSE) ::PostMessage(hWnd, WM_CLOSE, 0, 0);

}

return 0;

case WM_CLOSE :

if (parm.idThread) {

// Рабочий поток еще активен.

149

// Запускаем таймер ожидания окончания. parm.hWnd = 0;

::SetTimer(hWnd, TIMER_CLOSE, 100, NULL); return 0;

}

break;

case WM_DESTROY : if (hThread)

::CloseHandle(hThread);

::PostQuitMessage(0); return 0;

}

return ::DefWindowProc(hWnd, message, wParam, lParam);

}

13.3. Вспомогательные функции

int InitApplication (LPCTSTR szClass) // Регистрация класса главного окна

{

WNDCLASS wc; ::ZeroMemory(&wc, sizeof(wc));

wc.style

= 0;

wc.lpfnWndProc

= WndProc;

wc.hInstance

= g_hInst;

wc.hIcon

=

::LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor

=

::LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); wc.lpszClassName = szClass;

return ::RegisterClass(&wc);

}

LPTSTR StrFromRC (int IDstr)

// Загружает строку из ресурсов приложения.

{

static TCHAR szBuff[256]; ::LoadString(g_hInst, (UINT)IDstr, szBuff,

sizeof(szBuff));

return szBuff;

}

void ViewError (HWND hWnd)

// Отображает сообщение о любой системной ошибке.

{

TCHAR szErrs[16*1024]; ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,

NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szErrs, sizeof(szErrs), NULL);

150

::MessageBox((hWnd) ? hWnd : GetFocus(), szErrs, StrFromRC(IDS_ERROR), MB_OK | MB_ICONSTOP);

}

void SwitchButton (HWND hWnd, TCHAR chMode)

//"Переключает" кнопки,

//скрывая одну и показывая другую.

{

HWND hButBeg = ::GetDlgItem(hWnd, ID_BUTT_BEG); HWND hButEnd = ::GetDlgItem(hWnd, ID_BUTT_END); if (!hButBeg || !hButEnd) return;

if (chMode == TEXT('B')) { ::ShowWindow(hButBeg, SW_SHOW); ::ShowWindow(hButEnd, SW_HIDE);

}

else {

::ShowWindow(hButBeg, SW_HIDE); ::ShowWindow(hButEnd, SW_SHOW);

}

}

HWND CreateListView (HWND hwndParent) // Создает элемент ListView

{

DWORD dwStyle = WS_CHILD | WS_BORDER | WS_TABSTOP | WS_VISIBLE | LVS_REPORT | LVS_NOSORTHEADER;

HWND hwndList = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, // class name

TEXT(""), dwStyle, 0, 0, 0, 0, hwndParent,

(HMENU)ID_LISTVIEW, g_hInst, NULL); if(!hwndList)

return NULL;

ResizeListView(hwndList, hwndParent);

// Создаем 4 колонки. LV_COLUMN lvColumn;

int

col

=

0;

lvColumn.mask

=

LVCF_FMT | LVCF_WIDTH | LVCF_TEXT |

LVCF_SUBITEM;

lvColumn.fmt = LVCFMT_LEFT; lvColumn.cx = 240; lvColumn.pszText = TEXT("Файл");

ListView_InsertColumn(hwndList, col++, &lvColumn);

lvColumn.fmt = LVCFMT_LEFT; lvColumn.cx = 60; lvColumn.pszText = TEXT("Статус");

ListView_InsertColumn(hwndList, col++, &lvColumn);

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