платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 16 - Объект события

Предыдущая тема Следующая тема Перейти вниз

платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 16 - Объект события

Сообщение  electrik в Пн Июн 29, 2009 7:37 pm

Win32 API. Урок 16. Объект события

Мы изучим, что такое объект события и как использовать его в мультитредной программе.

Теория:

В предыдущем туториале я продемонстрировал, как треды взаимодействуют друг с другом через собственные windows-сообщения. Я пропустил два других метода:
глобальная переменная и объект события. В этом туториале мы используем оба.

Объект события - это что-то вроде переключателя: у него есть только два состояния: вкл и выкл. Вы создаете объект события и помещаете его в коде соответствующего треда, где наблюдаете за состоянием объекта. Если объект события выключен, ждущие его треды "спят". В подобном состоянии треды мало загружают CPU.

Вы можете создать объект события, вызвав функцию CreateEvent, которая имеет следующий синтаксис:

function CreateEvent _
(byval lpEventAttributes as LPSECURITY_ATTRIBUTES, _
byval bManualReset as BOOL, _
byval bInitialState as BOOL, _
byval lpName as LPCSTR) as HANDLE

• lpEventAttributes --> Если вы укажете значение NULL, у создаваемого объекта будут установки безопасности по умолчанию.
• bManualReset --> Если вы хотите, чтобы Windows автоматически переключал объект события в "выключено", вы должны присвоить этому параметру значение FALSE. Иначе вам надо будет выключить объект вручную с помощью вызова ResetEvent.
• bInitialState --> Если вы хотите, чтобы объект события при создании был установлен в положение "включено", укажите TRUE в качестве данного параметра, в противном случае объект события будет установлен в положение "выключен".
lpName --> Указатель на ASCIIZ-строку, которая будет именем объекта события. Это имя будет использоваться, когда вы захотите вызвать OpenEvent.

Если вызов прошел успешно, CreateEvent возвратит хэндл на созданный объект события. В противном случае она возвратит NULL.

Вы можете изменять состояние объекта события с помощью двух API-функций: SetEvent и ResetEvent. Функция SetEvent устанавливает объект события в положение "включено". ResetEvent делает обратное.

Когда объект события создан, вы должны поместить вызов функции WaitForSingleObject в тред, который должен следить за состоянием объекта события. Эта функция
имеет следующий синтаксис:

function WaitForSingleObject _
(byval hObject as HANDLE, _
byval dwTimeout as DWORD) as DWORD

• hObject --> Хэндл одного из синхронизационных объектов. Объект события - это вид синхронизационного события.
• dwTimeout --> Указывает в миллисекундах время, которое эта функция будет ждать, пока объект события не перейдет во включенное состояние. Если указанное время пройдет, а объект события все еще выключен, WaitForSingleObject вернет управление. Если вы хотите, чтобы функция наблюдала за объектом бесконечно, вы должны указать значение INFINITE в качестве этого параметра.

Пример:

Нижеприведенный пример отображает окно, ожидающее пока пользователь не выберет какую-нибудь команду из меню. Если пользователь выберет "run thread", тред
начнет подсчет. Когда он закончит, появится сообщение, информирующее пользователя о том, что работа выполнена. Во время того, как проводится подсчет, пользователь
может выбрать команду "stop thread", чтобы остановить тред.

файл objevent.bas

Код:

' объект события
#include "windows.bi"
#include "objevent.bi"
declare function WinMain ( byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
  byval szCmdLine as LPSTR, _
  byval iCmdShow as integer ) as integer
declare sub ThreadProc(byval param as uinteger)

' начало программы
dim shared hwnd as HANDLE ' хэндл окна
dim shared hMenu as Handle
dim shared hThread as handle ' хэндл треда
dim shared ThreadID as uInteger ' идентификатор треда
dim shared hEventStart as HANDLE
dim shared EventStop as BOOL = FALSE ' флаг события
end WinMain( GetModuleHandle( NULL ) ,NULL,GetCommandLine() , SW_SHOWNORMAL ) ' вызвать основную функцию
' здесь заканчивается программа

' процедура окна
function  WndProc _
(byval hwnd as HWND, _ ' хэндл окна
byval uMsg as UINT, _ ' сообщение
byval wParam as WPARAM, _ ' дополнительный  параметр сообщений
byval lParam as LPARAM) as LRESULT ' дополнительный параметр сообщений

function = 0
select case uMsg  'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
case WM_CREATE ' если получили сообщение WM_CREATE
    hEventStart = CreateEvent _ ' создадим событие
    (NULL, _ ' атрибуты безопасности
    FALSE, _ ' автоматически переключить объект события в "выключено"
    FALSE, _ ' при создании объекта события, установить его в положение "выключен"
    NULL) ' имя объекта события
hThread = CreateThread _ ' создадим тред
(NULL, _ ' атрибуты  безопасности
NULL, _ ' размер стека
cPtr(LPTHREAD_START_ROUTINE,@ThreadProc), _ ' указатель на функцию
NULL, _ ' параметр передаваемый функции
0, _ ' дополнительные флаги
@ThreadID) ' указатель на переменную, куда поместится идентификатор треда
CloseHandle _ ' закроем хэндл
(hThread) ' хэндл треда

case WM_COMMAND ' если получили сообщение WM_COMMAND
if lParam = 0 then ' если равно 0, значит идут сообщения от меню
    select case loword(wParam) ' начинаем обрабатывать элементы меню
case IDM_START_THREAD ' если в меню выбрали Run thread Thread
SetEvent _ ' установим событие
(hEventStart) ' хэндл события
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_START_THREAD, _ ' идентификатор меню
MF_GRAYED) ' сделаем недоступным
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_STOP_THREAD, _ ' идентификатор меню
MF_ENABLED) ' сделаем доступным
case IDM_STOP_THREAD ' если в меню выбрали stop thread
EventStop = TRUE ' установим флаг события в TRUE
EnableMenuItem _
(hMenu, _ ' хэндл меню
IDM_START_THREAD, _ ' идентификатор меню
MF_ENABLED) ' сделаем доступным
EnableMenuItem _
(hMenu, _ ' хэндл меню
IDM_STOP_THREAD, _ ' идентификатор меню
MF_GRAYED) ' сделаем недоступным

case IDM_EXIT
DestroyWindow(hWnd)
end select
end if
case WM_FINISH ' если сообщение WM_FINISH
    MessageBox(NULL,SuccessString,AppName,MB_OK)
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function


'функция WinMain
function WinMain _
(byval hInst as HINSTANCE, _ ' хэндл программы
byval hPrevInst as HINSTANCE, _ 'в win32 всегда 0
byval szCmdLine as LPSTR, _  'указатель на командную строку
byval iCmdShow as integer ) as integer  ' состояние окна при первом появлении

dim wc as WNDCLASSEX ' структура параметров окна
dim wMsg as MSG  ' структура сообщений

 'структура класса окна wc
' заполняем структуру wc
wc.cbSize = SIZEOF( WNDCLASSEX )  ' размер структуры WNDCLASSEX
wc.style = CS_HREDRAW or CS_VREDRAW  ' Стиль окна
wc.lpfnWndProc = @WndProc ' Адрес процедуры окна WndProc
wc.cbClsExtra = NULL  ' резервирование  дополнительных байт за концом структуры
wc.cbWndExtra = NULL
wc.hInstance = hInst  ' хэндл модуля
wc.hbrBackground = cast(HGDIOBJ, COLOR_WINDOW+1) ' Цвет фона
wc.lpszMenuName = @MyMenu ' хэндл меню
wc.lpszClassName = @ClassName ' имя класса окна
wc.hIcon = LoadIcon( NULL,IDI_APPLICATION ) ' Хэндл иконки
wc.hIconSm = wc.hIcon 'Хэндл маленькой иконки
wc.hCursor = LoadCursor( NULL,IDC_ARROW) ' Хэндл курсора


' регистрация нашего класса окна
if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if

' Создадим окно
hwnd = CreateWindowEx _
(NULL, _ ' дополнительные стили
ClassName, _ ' строка с именем класса окна
AppName, _ ' строка с именем окна
WS_OVERLAPPEDWINDOW, _ ' стиль окна
CW_USEDEFAULT, _ ' X
CW_USEDEFAULT, _ ' Y
CW_USEDEFAULT, _ ' ширина окна
CW_USEDEFAULT, _ ' высота окна
NULL, _ ' хэндл родительского окна
NULL, _ ' хэндл меню
hInst, _ ' хэндл модуля
NULL) ' указатель на структуру данных

ShowWindow( hwnd,iCmdShow) ' отобразить наше окно на десктопе
UpdateWindow( hwnd) ' обновить клиентскую область
hMenu = GetMenu(hwnd) ' получим хэндл меню
while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )  'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend
function = wMsg.wParam
end function

sub ThreadProc(byval param as uInteger)
dim in as integer
    dim de as integer
do
de=600000000
WaitForSingleObject _ ' ' следим за состоянием объекта события
(hEventStart, _ ' хэндл события
INFINITE) ' наблюдаем бесконечно
while de <> 0 ' пока de неравно 0
if EventStop <> TRUE then ' если флаг неравно TRUE
        in +=1
        de -=1
in -=3
else
    MessageBox(hwnd,@StopString,@AppName,MB_OK) ' выведем строку
    EventStop = FALSE ' поставим флаг в FALSE
continue do ' идем к началу петли
end if
wend
postMessage _ ' добавим в очередь сообщений
    (hwnd, _ ' хэндл окна
    WM_FINISH, _ ' сообщение WM_FINISH
    NULL,NULL)
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_START_THREAD, _ ' идентификатор меню
MF_ENABLED) ' сделаем доступным
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' идентификатор меню
IDM_STOP_THREAD, _ ' идентификатор меню
MF_GRAYED) ' сделаем недоступным
loop
end sub

файл objevent.bi

Код:

#define ClassName "Win32FreeBasicEventClass" ' класс окна
#define AppName "Win32 FreeBasic Event Example" ' название программы
#define MyMenu "FirstMenu"
#define SuccessString "The calculation is completed!" ' эту строку покажем после завершения расчета
#define StopString "The thread is stopped" ' ну а эту после завершения треда
#define WM_FINISH WM_USER + &h100
#define IDM_START_THREAD 32000
#define IDM_STOP_THREAD 32001
#define IDM_EXIT 32002

файл objevent.rc

Код:

#include "l16_objevent.bi"
FirstMenu MENU
{
POPUP "&Thread"
{
MENUITEM "&Run thread",IDM_START_THREAD
MENUITEM "&Stop thread",IDM_STOP_THREAD
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
}

Анализ:

В этом примере я демонстрирую другую технику работы с тредами.

case WM_CREATE
   hEventStart = CreateEvent _
   (NULL, _
   FALSE, _
   FALSE, _
   NULL)
hThread = CreateThread _
(NULL, _
NULL, _
cPtr(LPTHREAD_START_ROUTINE,@ThreadProc), _
NULL, _
0, _
@ThreadID)
CloseHandle _
(hThread)

Вы можете видеть, что я создал объект события и тред во время обработки сообщения WM_CREATE. Я создаю объект события, установленного в состояние "выключено" и обладающего свойством автоматического выключения. После того, как объект события создан, я создаю тред. Тем не менее, тред не начинает выполняться немедленно, так как он ждет, пока не включится объект события:

sub ThreadProc(byval param as uInteger)
dim in as integer
   dim de as integer
do
de=600000000
WaitForSingleObject _ '
(hEventStart, _
INFINITE)

WaitForSingleObject. ждет, пока не включится объект события, а затем возвращается. Это означает, что даже если тред создан, мы помещаем его в спящее состояние. Когда пользователь выбирает в меню команду "run thread", мы включаем объект события:

case IDM_START_THREAD
SetEvent _
(hEventStart)

Вызов SetEvent включает объект события, после чего WaitForSingleObject возвращается и тред начинает выполняться. Когда пользователь выбирает команду "stop thread", мы устанавливаем значение глобальной переменной в TRUE.

if EventStop <> TRUE then
       in +=1
       de -=1
in -=3
else
   MessageBox(hwnd,@StopString,@AppName,MB_OK)
   EventStop = FALSE
continue do
end if

Это останавливает тред и снова передает управление функции WaitForSingleObject. Заметьте, что мы не должны вручную выключать объект, так как мы указали при вызове функции CreateEvent, что значение bManualReset равно FALSE.

[C] Iczelion, пер. Aquila.

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 36
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Предыдущая тема Следующая тема Вернуться к началу


 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения