BlnheritHandle BOOL:Если возвращаемый дескриптор наследуется новыми порожденны­ми процессами, устанавливается значение TRUE.В противном случае – значение FALSE

CrProces.h

#define IDM_EXIT 100

#define IDM_TEST 200

#define IDM_ABOUT 301

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM);

CrProces.rc

#include "windows.h"

#include "CrProces.h"

MYAPP ICON DISCARDABLE "GENERIC.ICO"

MYAPP MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "E&xit", IDM_EXIT

END

MENUITEM "&Test!", IDM_TEST

POPUP "&Help"

BEGIN

MENUITEM "&About My Application...", IDM_ABOUT

END

END

ABOUTBOX DIALOG 22, 17, 171, 43

STYLE DS_MODALFRAME | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU

CAPTION "My Application"

FONT 8, "MS Sans Serif"

{

CONTROL "MyApp", -1, "STATIC", SS_ICON | WS_CHILD | WS_VISIBLE, 3, 2, 16, 16

CONTROL "Generic Application", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 28, 4, 100, 8

CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 116, 26, 50, 14

}

CrProces.c

#include <windows.h>

#include "CrProces.h"

HINSTANCE hInst; // current instance

LPCTSTR lpszAppName = "MyApp";

LPCTSTR lpszTitle = "My Application";

int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

MSG msg;

HWND hWnd;

WNDCLASSEX wc;

// Register the main application window class.

//............................................

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC)WndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon( hInstance, lpszAppName );

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wc.lpszMenuName = lpszAppName;

wc.lpszClassName = lpszAppName;

wc.cbSize = sizeof(WNDCLASSEX);

wc.hIconSm = LoadImage( hInstance, lpszAppName,

IMAGE_ICON, 16, 16,

LR_DEFAULTCOLOR );

if ( !RegisterClassEx( &wc ) )

return( FALSE );

hInst = hInstance;

// Create the main application window.

//....................................

hWnd = CreateWindow( lpszAppName,

lpszTitle,

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, 0,

CW_USEDEFAULT, 0,

NULL,

NULL,

hInstance,

NULL

);

if ( !hWnd )

return( FALSE );

ShowWindow( hWnd, nCmdShow );

UpdateWindow( hWnd );

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

{

TranslateMessage( &msg );

DispatchMessage( &msg );

}

return( msg.wParam );

}

HWND hList = NULL;

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )

{

switch( uMsg )

{

case WM_CREATE :

// Create list box.

//.................

hList = CreateWindowEx( WS_EX_CLIENTEDGE, "LISTBOX", "",

LBS_STANDARD | LBS_NOINTEGRALHEIGHT |

WS_CHILD | WS_VISIBLE,

0, 0, 10, 10,

hWnd, (HMENU)101,

hInst, NULL );

break;

case WM_SIZE :

if ( wParam != SIZE_MINIMIZED )

MoveWindow( hList, 0, 0, LOWORD( lParam ), HIWORD( lParam ), TRUE );

break;

case WM_COMMAND :

switch( LOWORD( wParam ) )

{

case IDM_TEST:

{

STARTUPINFO si;

PROCESS_INFORMATION pi;

// Initialize structures.

//.......................

ZeroMemory( &si, sizeof(STARTUPINFO) );

ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );

si.cb = sizeof( STARTUPINFO );

si.dwFlags = STARTF_USESHOWWINDOW;

si.wShowWindow = SW_SHOWNORMAL;

CreateProcess( NULL, "CALC", NULL, NULL, FALSE,

0, NULL, NULL, &si, &pi );

WaitForInputIdle( GetCurrentProcess(), INFINITE );

// Loop until process terminates.

//...............................

if ( pi.hProcess )

{

DWORD dwExitCode = STILL_ACTIVE;

while ( dwExitCode == STILL_ACTIVE )

{

WaitForSingleObject( pi.hProcess, 1000 );

GetExitCodeProcess( pi.hProcess, &dwExitCode );

SendMessage( hList, LB_INSERTSTRING, 0, (LPARAM)"Waiting for Calc." );

UpdateWindow( hWnd );

}

SendMessage( hList, LB_INSERTSTRING, 0, (LPARAM)"Calc is Finished" );

}

}

break;

case IDM_ABOUT :

DialogBox( hInst, "AboutBox", hWnd, (DLGPROC)About );

break;

case IDM_EXIT :

DestroyWindow( hWnd );

break;

}

break;

case WM_DESTROY :

PostQuitMessage(0);

break;

default :

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

}

return( 0L );

}

LRESULT CALLBACK About( HWND hDlg,

UINT message,

WPARAM wParam,

LPARAM lParam)

{

switch (message)

{

case WM_INITDIALOG:

return (TRUE);

case WM_COMMAND:

if ( LOWORD(wParam) == IDOK

|| LOWORD(wParam) == IDCANCEL)

{

EndDialog(hDlg, TRUE);

return (TRUE);

}

break;

}

return (FALSE);

}

Функция CreateThreadсоздает новую нить внутри текущего процесса. Эта функ­ция дает процессу возможность распределить работу, выполняемую приложением, но нескольким нитям ради ускорения времени выполнения приложения. Нити, как пра­вило, используются для выполнения фоновых задач, в частности, для разбиения тек­ста на страницы, выполняемого текстовым редактором. С помощью нитей подобное разбиение на страницы происходит по мере ввода пользователем текста документа, причем пользователь не ощущает в этом случае никаких задержек.

Нити разделяют вместе с процессом общее адресное пространство и системные ресурсы. Однако у них имеется собственный стек, а в Windows NT/2000 – и собствен­ные дескрипторы защиты. Для создания нити требуется следующая информация:

* Размер стека нити

* Атрибуты защиты нити. Для указания установленной по умолчанию защиты в Windows NT/2000 либо в среде Windows 9.x, где защита не поддерживается, можно использовано значение NULL.

* Адрес процедуры, с которой начинается выполнение. Это должна быть функция, которая получает в качестве единственного параметра 32-разрядное значение.

* Дополнительное 32-разрядное значение, которое передается в процедуру нити.

* Флаги, которые, помимо прочего, допускают установку уровня приоритета нити.

* Адрес для хранения идентификатора нити. Идентификатор нити представляет со­бой уникальное системное значение.

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

Объекты синхронизации

В связи с тем что Windows 9.x и Windows NT/2000 присуща вытесняющая многоза­дачность, возможность доступа двух нитей к одной и той же области памяти внутри процесса оказывается проблематичной. Некоторые нити не имеют доступа к памяти либо выполняют операции, которые оказывают влияние на другие нити внутри процес­са. Однако если нити не разделяют общие ресурсы процесса или системные ресурсы, либо они должны выполняться согласованно, то в этом случае требуется определенная их синхронизация. Для совместной работы нитей без разрушения памяти в интерфей­се Win32 API предоставляются объекты синхронизации (synchronization objects). Объекты синхронизации обеспечивают доступ к системным ресурсам, которые могут находить­ся под управлением нитей одних и тех же либо других процессов.

Тремя основными типами объектов синхронизации являются мьютексы, семафо­ры и события. Эти объекты синхронизации отличаются друг от друга условием уста­новки сигнального состояния. Наиболее распространенные объекты синхронизации перечислены в табл. 1.

Таблица 1. Основные объекты синхронизации процессов

Объект Назначение
Мьютекс Разрешает взаимно исключающий доступ
Семафор Разрешает доступ к известному числу нитей
Событие Разрешает доступ к любому числу ожидающих нитей

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

Функции ожидания (wait functions) представляют собой набор вызовов API, которые приостанавливают выполнение нитей до тех пор, пока не станет истинным заданный ряд условий. Функции ожидания проверяют сигнальное состояние объектов синхро­низации. Если указанный объект оказывается в сигнальном состоянии, функция ожи­дания завершается, а выполнение нити продолжается. В противном случае функция ожидания будет поддерживать нить в хорошо оптимизированном цикле, опрашивая состояние объекта синхронизации до тех пор, пока оно не станет сигнальным либо не истечет время ожидания.

Название мъютекса (mutex object) происходит от понятия "взаимное исключение" (mutual exclusion). Мьютекс может одновременно принадлежать только одной нити. Следовательно, если мьютекс принадлежит одной нити, а другая нить запрашивает права владения им, то запрашивающая нить может получить эти права на мьютекс лишь в том случае, когда владеющая им нить уступит свои права. Известный мьютекс Winl6Lockили Winl6Mutexиспользуется в Windows 9.x для защиты нереентрабельного 16-разрядного кода в интерфейсе GDI и USERот опасности одновременного выполнения. В примере, приведенном в описании функции CreateMutexдалее в гла­ве, продемонстрировано применение мьютексов.

Семафор (semaphore object) начинает действовать с назначенного для него началь­ного отсчета. Всякий раз когда нить получает права владения этим объектом (через функцию ожидания), счетчик в семафоре уменьшается на единицу. И всякий раз ког­да нить уступает свои права владения этим объектом, счетчик в семафоре увеличива­ется на единицу. Как только счетчик в семафоре достигает нуля, семафор блокиру­ется в несигнальном состоянии и ни одна из нитей не может получить к нему доступ.

Одним из удачных примером применения семафора служит управление распреде­лением конечных ресурсов. Управление запросом каждого ресурса выполняет сема­фор, действие которого автоматически прекращается по достижении запрограммиро­ванного предела. Всякий раз когда запрашивающей нити уже не требуется ресурс, семафор освобождается. Значение счетчика в семафоре всегда сравнимо с количе­ством доступных ресурсов. Семафоры используются в примере, приведенном в опи­сании функции CreateSemaphoreдалее в главе.

События (event objects) позволяют синхронизировать доступ к ресурсу со стороны любого числа нитей. При этом доступ к ресурсу предоставляется программным путем. Существует два вида событий: события, сбрасываемые вручную, и события, сбрасы­ваемые автоматически.

Сбрасываемое вручную событие (manual-reset event object) устанавливается в сигналь­ное состояние программным путем через вызов функции SetEvent.Событие перехо­дит в несигнальное состояние после обращения к функции ResetEvent.Функция PulseEventустанавливает и сбрасывает события по очереди. Сбрасываемые вручную события разрешают всем нитям, ожидающим наступления такого события, продол­жить свое выполнение. Сбрасываемые вручную события используются в примере, приведенном в описании функции CreateEventдалее в главе.

Автоматически сбрасываемое событие (auto-reset event object) сбрасывается самосто­ятельно после освобождения одной ожидающей нити. Все остальные нити продолжают ожидать до тех пор, пока данное событие опять не перейдет в несигнальное состоя­ние. Автоматически сбрасываемые события разрешают управляемый последователь­ный доступ к синхронизированному коду нитям разных процессов подобно тому, как объекты критических участков кода разрешают нитям одного и того же процесса пос­ледовательный доступ к коду. Автоматически сбрасываемые события используются в примере, приведенном в описании функции OpenEventдалее в этой главе.

Кроме того, в приложении в качестве объектов синхронизации можно использовать объекты, перечисленные в табл. 2. Они не переходят в несигнальное состояние до тех пор, пока не возникнет заданное условие.

Таблица 2. Другие объекты синхронизации

Объект Сигнальное условие
Уведомление об изменении Переходит в сигнальное состояние, когда происходит заданное изменение в структуре файловой системы
Ввод с консоли Переходит в сигнальное состояние по готовности непрочитанного ввода с консоли
Процесс Переходит в сигнальное состояние при завершении процесса
Нить Переходит в сигнальное состояние при завершении нити

Защита процесса от нереентрабельного кода

В связи с тем что глобальные данные процесса разделяются среди всех нитей, по­требуется принять меры предосторожности во избежание конфликтов при доступе к данным, которые могли бы привести к сбою программы. Одним из средств, реализо­ванных в Win32 API для защиты от нереентрабельного кода, является объект крити­ческого участка кода. Критический участок кода (critical section) представляет собой такой объект, который используется только для синхронизации нитей одного процесса. Объект критического участка кода создается с помощью функции InitializeCriticalSection.

После создания объекта критического участка кода для получения доступа к защи­щенному коду в нити необходимо использовать функцию EnterCriticalSection интер­фейса API. Если в пределах критического участка кода выполняется другая нить, тог­да нить, запрашивающая доступ к критическому участку кода, должна ожидать до тех пор, пока нить, владеющая данным объектом, не уступит свои права на него, обра­тившись для этого к LeaveCriticalSection.

Описание функций управления процессами, нитями и волокнами

Все функции интерфейса Win32 API, предназначенные для управления процесса­ми, нитями и волокнами, перечислены в табл. 3.

Таблица 3. Сводка функций управления процессами, нитями и волокнами

Функция Назначение
Attach Threadlnput Переадресует ввод из одной нити в другую нить
CancelWartableTimer Отменяет таймер ожидания, устанавливая его в неактивное состояние
CovertThreadToFiber Преобразует текущую нить в волокно
CreateEvent Создает объект события
CreateFiber Создает объект волокна
CreafeMutex Создает объект мьютекса
CreateProcess Создает объект процесса и нити и запускает процесс
CreateProcessAsUser Создает объект процесса и нити в контексте защиты, установленной для данного пользователя
CreateRemoteThread Создает нить, которая выполняется в адресном пространстве другого процесса
CreateSemaphore Создает объект семафора
CreateThread Создает объект нити и запускает нить
Create WaitableTimer Создает объект таймера ожидания
DeleteCriticalSection Удаляет неактивный объект критического участка кода
DeleteFiber Удаляет объект волокна
DuplicateHandle Копирует дескриптор любого объекта с тем, чтобы он мог использоваться другим процессом
EnterCriticalSection Проверяет логическое состояние критического участка кода, и если им никто не владеет, осуществляется вход в этот участок кода
ExitProcess Завершает процесс и все нити процесса и возвращает код завершения
ExitThread Завершает одну нить процесса
GetCurrentFiber Возвращает текущее волокно нити
GetCurrentProcess Возвращает дескриптор текущего процесса
GetCurrentProcessID Возвращает идентификатор текущего процесса
GetCurrentThread Возвращает дескриптор текущей нити
GetCurrentThreadID Возвращает идентификатор текущей нити
GetExitCodeProcess Возвращает код завершения указанного процесса. Эта функция может также использоваться для установления факта завершения процесса
GetExitCodeThread Возвращает код завершения указанной нити. Эта функция может также использоваться для установления факта завершения нити
GetFiberData Возвращает данные, связанные с объектом волокна
GelPriorityClass Определяет класс приоритета процесса
GetProcessAffinityMask Возвращает маску родства процесса
GetProcessHeap Возвращает дескриптор кучи вызывающего процесса
GetProcessHeaps Возвращает массив дескрипторов, которые содержат все кучи, принадлежащие вызывающему процессу
GetProcessPriorityBoost Возвращает повышение приоритета для процесса
GetProcessShutdownParameters Возвращает параметры закрытия процесса
GetProcessTimes Возвращает временные характеристики выполнения процесса
GetProcessVersion Возвращает ожидаемую процессом версию Windows
GetProcessWorkingSetSize Возвращает размер рабочего множества для процесса
GetQueueStatus Возвращает входное состояние очереди нити
Функция Назначение
GetThreadContext Используется отладчиком для сохранения машинного контекста нити
GetThreadPriority Возвращает класс приоритета данной нити
GetThreadPriorityBoost Возвращает повышение приоритета нити
GetThreadSelectorEntry Используется отладчиком для возврата элемента локальной таблицы дескрипторов (LDT) по заданному адресу сегмента указанной нити
GetThreadTimes Возвращает временные характеристики выполнения нити
InitializeCrrticalSection Создает объект критического участка кода для защиты части мультинитевого процесса от одновременного выполнения нескольких нитей
InterlockedCompareExchange Сравнивает длинные значения синхронизированных переменных
InterlockedDecrement Уменьшает длинное значение синхронизированной переменной
InterlockedExchange Меняет местами указанное значение и длинное значение синхронизированной переменной
InterlockedExchangeAdd Прибавляет указанное значение к длинному значению синхронизированной переменной
Interlockedlncrement Увеличивает длинное значение синхронизированной переменной
LeaveCriticalSection Отмечает конец кода, защищенного объектом критического участка кода
MsgWaitForMultipleObjects Выполняет возврат, когда любые или все объекты переходят в сигнальное состояние, конкретный тип входных данных появляется во входной очереди указанной нити либо истекает время ожидания
MsgWaitForMultipieObjectsEx То же, что и функция MsgWaitForMuitipleObjects, за исключением того, что к возврату из данной функции приводит также завершение ввода-вывода или асинхронный вызов процедуры (АРС)
OpenEvent Возвращает дескриптор именованного объекта события
OpenMutex Возвращает дескриптор именованного объекта мьютекса
OpenProcess Возвращает дескриптор указанного объекта
OpenSemaphore Возвращает дескриптор поименованного объекта семафора
OpenWaitableTimer Возвращает дескриптор объекта таймера ожидания
PulseEvent Переводит событие из несигнального в сигнальное состояние, а затем опять в несигнальное состояние. Эта функция применяется для освобождения ожидающих нитей и повторной установки блока событий
QueueUserAPC Размещает в очереди АРС указанной нити объект асинхронного вызова процедуры (АРС) из пользовательского режима
RaiseException Сигнализирует нити о программно формируемом исключении, которое может быть обработано приложением. Функция RaiseException применяется также в качестве канала связи с отладчиком
ReadProcessMemory Читает содержимое памяти из адресного пространства процесса
RegisterHotKey Создает оперативную клавишу для данной нити
ReleaseMutex Возвращает мьютекс в сигнальное состояние
ReleaseSemaphore Увеличивает счетчик семафора
ResetEvent Переводит событие в несигнальное состояние
ResumeThread Возобновляет выполнение приостановленной нити
SetEvent Переводит событие в сигнальное состояние
SetPriorityClass Устанавливает класс приоритета процесса
SetProcessAffinityMask Устанавливает маску родства процесса
SetProcessPriorityBoost Устанавливает повышение приоритета для процесса
SetProcessShufdownParameters Устанавливает параметры закрытия процесса
SetProcessWorkingSetSize Устанавливает размер рабочего множества для процесса
SetThreadAffinityMask Устанавливает маску родства нити
SetThreadContext Устанавливает контекст нити
SetThreadldealProcessor Устанавливает идеальный процессор для нити
SetThreadPriority Устанавливает класс приоритета нити
SetThreadPriorityBoost Устанавливает повышение приоритета для нити
SetUnhandledExceptionFilter Разрешает приложению установить процедуру перехвата необработанных исключений
SetWartableTimer Активизирует объект таймера ожидания
SignalObjectAndWait Переводит один объект в сигнальное состояние и ожидает перехода в сигнальное состояние другого объекта
Sleep Приостанавливает выполнение нити на заданный промежуток в миллисекундах. При этом может быть запланировано выполнение другой нити
SleepEx То же что и Sleep за исключением того, что к возврату из данной функции может также привести завершение ввода-вывода или асинхронный вызов процедуры (АРС)
SuspendThread Приостанавливает выполнение процесса
SwitchToFiber Планирует запуск волокна
TerminateProcess Завершает процесс
TerminateThread Завершает выполнение нити
TIsAlloc Выделяет индекс локальной памяти нити
TIsFree Освобождает индекс локальной памяти нити
TIsGetValue Возвращает объект памяти, назначенный для локальной памяти нити по указанному индексу
TIsGetValue Устанавливает объект памяти, назначенный для локальной памяти нити по указанному индексу
Функция Назначение
TryEnterCrrticalSection Пытается войти в критический участок кода, и если это удается, то приобретает права владения критическим участком кода
UnhandledExceptionFiter Вызывает используемый по умолчанию фильтр необработанных сообщений или отладчик, если данная программа отлаживается
UnregisteredHotKey Удаляет оперативную клавишу нити
WattForlnputldle Ожидает до тех пор, пока для нити не завершится ввод
WaitForMultipleObjects Ожидает перехода в сигнальное состояние одного или более указанных объектов синхронизации
WaitForMultipleObjectsEx То же, что и функция WaitForMultipleObjects, за исключением того, что к возврату из данной функции может также привести завершение ввода-вывода или асинхронный вызов процедуры (АРС)
WaitForSingleObject Ожидает перехода в сигнальное состояние одного объекта синхронизации
WaitForSingleObjectEx То же, что и функция WaitForSingleObject, за исключением того, что к возврату из данной функции может также привести завершение ввода-вывода или асинхронный вызов процедуры (АРС)
WriteProcessMemory Используется, главным образом, отладчиком для непосредственной записи данных в память процесса

CreateEvent

Описание Функция CreateEventсоздает поименованный или безымянный объект события, который может программным путем быть переведен в сигнальное или несигнальное состояние с помощью функции SetEvent.События ис­пользуются в приложении для уведомления нитей о соответствующих дей­ствиях, а также для синхронизации нитей. Например, одна нить создает событие и устанавливает его в несигнальное состояние. Другая нить ис­пользует дескриптор события, полученный с помощью функции WaitFor-SingleObject,для ожидания момента перехода этого события в сигнальное состояние. После того как первая нить установит событие в сигнальное со­стояние, вторая нить продолжит свое выполнение.

Синтаксис HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpSecurityAttribs, BOOL bManualReset, BOOL nInitialState, LPCTSTR lpszEventName)

Возвращаемое значение HANDLE:Если выполнение рассматриваемой функции оказывается успешным, возвращается дескриптор объекта события. В противном случае возвращается NULL. Если же поименованный объект события существовал до вызова рассматриваемой функции, функция GetLastErrorвозвращает код ошибки ERROR_ALREADY_EXIST,а иначе - 0.

Включаемый файл winbase.h

См. также CreateProcess, DuplicateHandle, OpenEvent, ResetEvent, SetEvent, WaitForSingleObject, WaitForMultipleObjects.

Пример В приведенном ниже примере показано, каким образом сбрасываемые вручную события и семафоры используются для написания синхронизи­рованного кода, предназначенного для управления доступом процесса к ресурсам. Имеются четыре пункта меню: Read, Write, Quit и Help. Чтобы было удобнее наблюдать за тем, как действует синхронизация, необходи­мо запустить на выполнение три экземпляра приложения и расположить их на рабочем столе без перекрытий. Затем в двух экземплярах следует выбрать пункт меню Read, а в третьем экземпляре – пункт меню Write. Между читающими процессами конфликт отсутствует, однако записыва­ющий процесс должен ожидать до тех пор, пока читающие процессы не завершатся, прежде чем продолжить свое выполнение. Далее следует выб­рать операцию записи и две операции чтения. В этом случае операции чтения должны ожидать до тех пор, пока не завершится операция запи­си. Текущее состояние каждого про­цесса отображается в строке заго­ловка соответствующего экземпляра приложения.

CrEvent.h

#define IDM_EXIT 100

#define IDM_READ 200

#define IDM_WRITE 201

#define IDM_ABOUT 301

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM);

CrEvent.rc

#include "windows.h"

#include "CrEvent.h"

MYAPP ICON DISCARDABLE "GENERIC.ICO"

MYAPP MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "&Read", IDM_READ

MENUITEM "&Write", IDM_WRITE

MENUITEM SEPARATOR

MENUITEM "E&xit", IDM_EXIT

END

POPUP "&Help"

BEGIN

MENUITEM "&About My Application...", IDM_ABOUT

END

END

ABOUTBOX DIALOG 22, 17, 171, 43

STYLE DS_MODALFRAME | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU

CAPTION "My Application"

FONT 8, "MS Sans Serif"

{

CONTROL "MyApp", -1, "STATIC", SS_ICON | WS_CHILD | WS_VISIBLE, 3, 2, 16, 16

CONTROL "Generic Application", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 28, 4, 100, 8

CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 116, 26, 50, 14

}

CrEvent.c

#include <windows.h>

#include "CrEvent.h"

HINSTANCE hInst; // current instance

LPCTSTR lpszAppName = "MyApp";

LPCTSTR lpszTitle = "My Application";

int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

MSG msg;

HWND hWnd;

WNDCLASSEX wc;

// Register the main application window class.

//............................................

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC)WndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon( hInstance, lpszAppName );

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wc.lpszMenuName = lpszAppName;

wc.lpszClassName = lpszAppName;

wc.cbSize = sizeof(WNDCLASSEX);

wc.hIconSm = LoadImage( hInstance, lpszAppName,

IMAGE_ICON, 16, 16,

LR_DEFAULTCOLOR );

if ( !RegisterClassEx( &wc ) )

return( FALSE );

hInst = hInstance;

// Create the main application window.

//....................................

hWnd = CreateWindow( lpszAppName,

lpszTitle,

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, 0,

CW_USEDEFAULT, 0,

NULL,

NULL,

hInstance,

NULL

);

if ( !hWnd )

return( FALSE );

ShowWindow( hWnd, nCmdShow );

UpdateWindow( hWnd );

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

{

TranslateMessage( &msg );

DispatchMessage( &msg );

}

return( msg.wParam );

}

#define SEMAPHORE_MAX_COUNT 10

LPCTSTR lpszReadSem = "Read Semaphore";

LPCTSTR lpszWriteEvent = "Write Event";

HANDLE hSemRead = NULL;

HANDLE hEventWrite = NULL;

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )

{

switch( uMsg )

{

case WM_CREATE:

// Make the two synchronization objects.

//......................................

hSemRead = CreateSemaphore( NULL, 10, 10, lpszReadSem );

hEventWrite = CreateEvent( NULL, TRUE, TRUE, lpszWriteEvent );

break;

case WM_COMMAND :

switch( LOWORD( wParam ) )

{

case IDM_READ:

{

HANDLE hTmpSemRead;

HANDLE hTmpEventWrite;

hTmpSemRead= OpenSemaphore( SEMAPHORE_ALL_ACCESS, FALSE, lpszReadSem );

hTmpEventWrite = OpenEvent( SYNCHRONIZE, FALSE, lpszWriteEvent );

SetWindowText( hWnd, "Waiting for all Writes to Finish" );

// Check that write manual reset event is signaled.

//.................................................

WaitForSingleObject( hEventWrite, INFINITE );

// Wait for semaphore.

//....................

WaitForSingleObject( hSemRead, INFINITE );

// Do the simulated read.

//.......................

SetWindowText( hWnd, "Reading" );

Sleep( 5000 );

// Release semaphore.

//...................

ReleaseSemaphore( hSemRead, 1, NULL );

SetWindowText( hWnd, "Done Reading" );

// Close the temporary handles.

//.............................

CloseHandle( hTmpSemRead );

CloseHandle( hTmpEventWrite );

}

break;

case IDM_WRITE:

{

DWORD dwSemaphoreCount = 0;

HANDLE hTmpSemRead;

HANDLE hTmpEventWrite;

hTmpSemRead = OpenSemaphore( SEMAPHORE_ALL_ACCESS, FALSE, lpszReadSem );

hTmpEventWrite = OpenEvent( SYNCHRONIZE, FALSE, lpszWriteEvent );

// Wait for manual-reset event: it becomes nonsignaled.

//.....................................................

SetWindowText( hWnd, "Waiting for Write Event" );

WaitForSingleObject( hEventWrite, INFINITE );

ResetEvent( hEventWrite );

SetWindowText( hWnd, "Waiting for All Reads to Finish" );

// Release semaphore until its count is the maximum allowed.

//..........................................................

while ( dwSemaphoreCount != SEMAPHORE_MAX_COUNT )

{

WaitForSingleObject( hSemRead, INFINITE );

ReleaseSemaphore( hSemRead, 1, &dwSemaphoreCount );

dwSemaphoreCount++;

}

SetWindowText( hWnd, "Writing" );

// Do the simulated write.

//........................

Sleep( 10000 );

SetWindowText( hWnd, "Done Writing" );

// SetEvent: event object becomes signaled.

//.........................................

SetEvent( hEventWrite );

// Close the handles.

//...................

CloseHandle( hTmpSemRead );

CloseHandle( hTmpEventWrite );

}

break;

case IDM_ABOUT :

DialogBox( hInst, "AboutBox", hWnd, (DLGPROC)About );

break;

case IDM_EXIT :

DestroyWindow( hWnd );

break;

}

break;

case WM_DESTROY :

if ( hSemRead )

CloseHandle( hSemRead );

if ( hEventWrite )

CloseHandle( hEventWrite );

PostQuitMessage(0);

break;

default :

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

}

return( 0L );

}

LRESULT CALLBACK About( HWND hDlg,

UINT message,

WPARAM wParam,

LPARAM lParam)

{

switch (message)

{

case WM_INITDIALOG:

return (TRUE);

case WM_COMMAND:

if ( LOWORD(wParam) == IDOK

|| LOWORD(wParam) == IDCANCEL)

{

EndDialog(hDlg, TRUE);

return (TRUE);

}

break;

}

return (FALSE);

}

Определение структуры SECURITY_ATTRIBUTES

typedef struct _SECURITY_ATTRIBUTES {

DWORD nLength;

LPVOID lpSecurityDescriptor;

BOOL blnheritHandle; } SECURITY_ATTRIBUTES;

Члены

nLength DWORD:Размер структуры в байтах.

lpSecurityDescriptor LPVOID:Указатель на дескриптор защиты объекта, который управляет совместным использованием этого объекта. Если установлено значение NULL,для объекта можно назначить используемый по умолчанию деск­риптор защиты вызывающего процесса. В Windows 9.x этот член игнори­руется.

blnheritHandle BOOL:Если возвращаемый дескриптор наследуется новыми порожденны­ми процессами, устанавливается значение TRUE.В противном случае – значение FALSE.

CreateMutex

Описание Функция CreateMutexсоздает поименованный или безымянный объект мьютекса, который используется для синхронизации процессов. Мьютекс переходит в сигнальное состояние, когда он не принадлежит ни одной из нитей. Нить, которой мьютекс принадлежит, может указать один и тот же мьютекс в повторяющихся вызовах функции ожидания, не блокируя свое выполнение. Как правило, по отношению к одному и тому же мьютексу это не делается, однако такой способ препятствует самоблокировке нити, когда она ожидает мьютекс, который ей уже принадлежит. Чтобы уступить свои права на мьютекс, нить должна обратиться к функции ReleaseMutex в момент, когда мьютекс удовлетворяет условие ожидания. Возвращаемый рассматриваемой функцией дескриптор имеет доступ типа MUTEX_ALL_ACCESSк новому мьютексу и может использоваться в лю­бой функции, которой требуется дескриптор мьютекса. Любая нить вызы­вающего процесса может указать дескриптор мьютекса при вызове одной из функций ожидания. Для закрытия мьютекса, когда он уже больше не нужен, применяется функция CloseHandle.Когда все открытые дескрипто­ры мьютекса закрываются, мьютекс удаляется.

Синтаксис HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpSecurityAttribs, BOOL bInitialOwner, LPCTSTR lpszMutexName)

Возвращаемое значение HANDLE:Если выполнение рассматриваемой функции успешно, возвращается дескриптор объекта мьютекса. В противном случае возвращается NULL.Если поименованный объект мьютекса уже существовал до вызо­ва рассматриваемой функции, GetLastErrorвозвращает код ошибки ERROR_ALREADY_EXIST;в противном случае возвращается 0. Для вы­борки расширенной информации об ошибке используется функция Get­LastError.

Включаемый файл winbase.h

См. также CreateProcess, DuplicateHandle, OpenMutex, ReleaseMutex, WaitForSingleObject, WaitForMultipleObjects.

Пример В приведенном ниже простом примере демонстрируется применение мьютексов. Когда выбирается пункт меню Test!, начинает свое выполнение нить, которая ожидает доступа к мьютексу, переходя в неактивное состо­яние, а затем освобождает мьютекс. А при выборе пункт меню Test! несколько раз демонстрируется конфликт нитей за обладание мьютексом. В данном случае мьютекс вынуждает нити выполняться после­довательно.

См. \local-sources\chap25\25-03.txt

CrMutex.h

#define IDM_EXIT 100

#define IDM_TEST 200

#define IDM_ABOUT 301

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM);

CrMutex.rc

#include "windows.h"

#include "CrMutex.h"

MYAPP ICON DISCARDABLE "GENERIC.ICO"

MYAPP MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "E&xit", IDM_EXIT

END

MENUITEM "&Test!", IDM_TEST

POPUP "&Help"

BEGIN

MENUITEM "&About My Application...", IDM_ABOUT

END

END

ABOUTBOX DIALOG 22, 17, 171, 43

STYLE DS_MODALFRAME | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU

CAPTION "My Application"

FONT 8, "MS Sans Serif"

{

CONTROL "MyApp", -1, "STATIC", SS_ICON | WS_CHILD | WS_VISIBLE, 3, 2, 16, 16

CONTROL "Generic Application", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 28, 4, 100, 8

CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 116, 26, 50, 14

}

CrMutex.c

#include <windows.h>

#include "CrMutex.h"

HINSTANCE hInst; // current instance

LPCTSTR lpszAppName = "MyApp";

LPCTSTR lpszTitle = "My Application";

int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

MSG msg;

HWND hWnd;

WNDCLASSEX wc;

// Register the main application window class.

//............................................

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC)WndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon( hInstance, lpszAppName );

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wc.lpszMenuName = lpszAppName;

wc.lpszClassName = lpszAppName;

wc.cbSize = sizeof(WNDCLASSEX);

wc.hIconSm = LoadImage( hInstance, lpszAppName,

IMAGE_ICON, 16, 16,

LR_DEFAULTCOLOR );

if ( !RegisterClassEx( &wc ) )

return( FALSE );

hInst = hInstance;

// Create the main application window.

//....................................

hWnd = CreateWindow( lpszAppName,

lpszTitle,

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, 0,

CW_USEDEFAULT, 0,

NULL,

NULL,

hInstance,

NULL

);

if ( !hWnd )

return( FALSE );

ShowWindow( hWnd, nCmdShow );

UpdateWindow( hWnd );

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

{

TranslateMessage( &msg );

DispatchMessage( &msg );

}

return( msg.wParam );

}

LPCTSTR lpszMutex = "Example Mutex";

HANDLE hMutex = NULL;

// Child thread procedure waits until mutex becomes signaled,

// holds the object for five seconds, and then releases it.

//...........................................................

DWORD WINAPI ChildThreadProc( LPDWORD lpData )

{

TCHAR szBuffer[128];

HWND hWnd = (HWND)lpData;

HANDLE hTmpMutex = OpenMutex( MUTEX_ALL_ACCESS, FALSE, lpszMutex );

wsprintf( szBuffer,"Thread %x waiting for Mutex %x",

GetCurrentThreadId(), hMutex );

SendMessage( hWnd, WM_USER, 0, (LPARAM)szBuffer );

// Wait for signaled mutex.

//.........................

WaitForSingleObject( hMutex, INFINITE );

wsprintf( szBuffer,"Thread %x got mutex!", GetCurrentThreadId() );

SendMessage( hWnd, WM_USER, 0, (LPARAM)szBuffer );

// Shut out other threads.

//........................

Sleep( 5000 );

// Release mutex.

//...............

wsprintf( szBuffer,"Thread %x is done with mutex", GetCurrentThreadId() );

SendMessage( hWnd, WM_USER, 0, (LPARAM) szBuffer );

ReleaseMutex( hMutex );

CloseHandle( hMutex );

return( 0 );

}

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )

{

static HWND hList = NULL;

static int nMsgNum = 0;

switch( uMsg )

{

case WM_CREATE :

// Create list box.

//.................

hList = CreateWindowEx( WS_EX_CLIENTEDGE, "LISTBOX", "",

LBS_STANDARD | LBS_NOINTEGRALHEIGHT |

WS_CHILD | WS_VISIBLE,

0, 0, 10, 10,

hWnd, (HMENU)101,

hInst, NULL );

hMutex = CreateMutex( NULL, FALSE, lpszMutex );

break;

case WM_SIZE :

if ( wParam != SIZE_MINIMIZED )

MoveWindow( hList, 0, 0, LOWORD( lParam ), HIWORD( lParam ), TRUE );

break;

case WM_USER :

{

TCHAR szBuffer[128];

wsprintf( szBuffer, "%3d: %s", ++nMsgNum, (LPTSTR)lParam );

SendMessage( hList, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)szBuffer );

}

break;

case WM_COMMAND :

switch( LOWORD( wParam ) )

{

case IDM_TEST:

{

DWORD id;

// Make a thread.

//...............

CreateThread( NULL, 0, ChildThreadProc, hWnd, 0, &id );

}

break;

case IDM_ABOUT :

DialogBox( hInst, "AboutBox", hWnd, (DLGPROC)About );

break;

case IDM_EXIT :

DestroyWindow( hWnd );

break;

}

break;

case WM_DESTROY :

if ( hMutex )

CloseHandle( hMutex );

PostQuitMessage(0);

break;

default :

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

}

return( 0L );

}

LRESULT CALLBACK About( HWND hDlg,

UINT message,

WPARAM wParam,

LPARAM lParam)

{

switch (message)

{

case WM_INITDIALOG:

return (TRUE);

case WM_COMMAND:

if ( LOWORD(wParam) == IDOK

|| LOWORD(wParam) == IDCANCEL)

{

EndDialog(hDlg, TRUE);

return (TRUE);

}

break;

}

return (FALSE);

}

CreateSemaphore

Описание Функция CreateSemaphoreсоздает поименованный или безымянный объект семафора. Семафор, в котором для синхронизации используется счетчик, устанавливается в сигнальное состояние, когда значение счетчика в нем оказывается больше нуля, и в несигнальное состояние при дости­жении счетчиком нуля. Всякий раз когда семафор выбирается (например, с помощью WaitForSingleObject),счетчик в семафоре уменьшается. Каж­дый раз когда семафор освобождается с использованием функции Release-Semaphore,счетчик в семафоре увеличивается. Счетчик не может быть меньше нуля или больше значения, определяемого параметром ISemMax-Count.

Синтаксис DWORD CreateSemaphore(LPSECURITY_ATTRIBUTES lpThreadSecurity, LONG lSemInitialCount, LONG lSemMaxCount, LPCTSTR lpszSemName)

Включаемый файл winbase.h

См. также CloseHandle, DuplicateHandle, OpenSemaphore, ReleaseSemaphore, WaitForSingleObject, WaitForMultipleObjects.

Пример В приведенном ниже примере семафоры применяются для предоставления возможности выполнения процедуры порожденной нити только четырем нитям. Когда пользователь выбирает пункт меню Test!, создается новая нить. Процедура этой нити сначала пытается получить семафор с макси­мальным значением счетчика, равным четырем, выполняя цикл до тех пор, пока эта операция не завершится успешно. Независимо от того, сколько раз выбирается пункт меню Test!, защищенный код позволит од­новременно выполняться только четырем нитям.

CrSem.h

#define IDM_EXIT 100

#define IDM_TEST 200

#define IDM_ABOUT 301

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM);

CrSem.rc

#include "windows.h"

#include "CrSem.h"

MYAPP ICON DISCARDABLE "GENERIC.ICO"

MYAPP MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "E&xit", IDM_EXIT

END

MENUITEM "&Test!", IDM_TEST

POPUP "&Help"

BEGIN

MENUITEM "&About My Application...", IDM_ABOUT

END

END

ABOUTBOX DIALOG 22, 17, 171, 43

STYLE DS_MODALFRAME | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU

CAPTION "My Application"

FONT 8, "MS Sans Serif"

{

CONTROL "MyApp", -1, "STATIC", SS_ICON | WS_CHILD | WS_VISIBLE, 3, 2, 16, 16

CONTROL "Generic Application", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 28, 4, 100, 8

CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 116, 26, 50, 14

}

CrSem.c

#include <windows.h>

#include "CrSem.h"

HINSTANCE hInst; // current instance

LPCTSTR lpszAppName = "MyApp";

LPCTSTR lpszTitle = "My Application";

int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

MSG msg;

HWND hWnd;

WNDCLASSEX wc;

// Register the main application window class.

//............................................

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC)WndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon( hInstance, lpszAppName );

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wc.lpszMenuName = lpszAppName;

wc.lpszClassName = lpszAppName;

wc.cbSize = sizeof(WNDCLASSEX);

wc.hIconSm = LoadImage( hInstance, lpszAppName,

IMAGE_ICON, 16, 16,

LR_DEFAULTCOLOR );

if ( !RegisterClassEx( &wc ) )

return( FALSE );

hInst = hInstance;

// Create the main application window.

//....................................

hWnd = CreateWindow( lpszAppName,

lpszTitle,

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, 0,

CW_USEDEFAULT, 0,

NULL,

NULL,

hInstance,

NULL

);

if ( !hWnd )

return( FALSE );

ShowWindow( hWnd, nCmdShow );

UpdateWindow( hWnd );

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

{

TranslateMessage( &msg );

DispatchMessage( &msg );

}

return( msg.wParam );

}

LPCTSTR lpszSemaphore = "Test Semaphore";

// This is a child thread procedure that waits for a semaphore,

// holds the semaphore for five seconds, and releases the semaphore.

// Threads that cannot get semaphores will wait until other threads exit.

//.......................................................................

DWORD WINAPI ChildThreadProc( LPDWORD lpData )

{

TCHAR szBuffer[256];

DWORD dwSemCount = 0;

HWND hList = (HWND)lpData;

HANDLE hSemaphore = OpenSemaphore( SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, FALSE, lpszSemaphore );

wsprintf( szBuffer,"Thread %x waiting for semaphore %x",

GetCurrentThreadId(), hSemaphore );

SendMessage( hList, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)szBuffer );

// Check for signaled semaphore.

//..............................

WaitForSingleObject( hSemaphore, INFINITE );

wsprintf( szBuffer,"Thread %x got semaphore", GetCurrentThreadId() );

SendMessage( hList, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)szBuffer );

Sleep( 5000 );

// Release semaphore.

//...................

ReleaseSemaphore( hSemaphore, 1, &dwSemCount );

wsprintf( szBuffer,"Thread %x is done with semaphore. Its count was %ld.",

GetCurrentThreadId(), dwSemCount );

SendMessage( hList, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)szBuffer );

CloseHandle( hSemaphore );

return( 0 );

}

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )

{

static HWND hList = NULL;

static HANDLE hSemaphore = NULL;

switch( uMsg )

{

case WM_CREATE:

// Create list box.

//.................

hList = CreateWindowEx( WS_EX_CLIENTEDGE, "LISTBOX", "",

LBS_STANDARD | LBS_NOINTEGRALHEIGHT |

WS_CHILD | WS_VISIBLE,

0, 0, 10, 10,

hWnd, (HMENU)101,

hInst, NULL );

hSemaphore = CreateSemaphore( NULL, 4, 4, lpszSemaphore );

break;

case WM_SIZE :

if ( wParam != SIZE_MINIMIZED )

MoveWindow( hList, 0, 0, LOWORD( lParam ), HIWORD( lParam ), TRUE );

break;

case WM_COMMAND :

switch( LOWORD( wParam ) )

{

case IDM_TEST:

{

DWORD dwChildId;

CreateThread( NULL, 0, ChildThreadProc, hList, 0, &dwChildId );

}

break;

case IDM_ABOUT :

DialogBox( hInst, "AboutBox", hWnd, (DLGPROC)About );

break;

case IDM_EXIT :

DestroyWindow( hWnd );

break;

}

break;

case WM_DESTROY :

if ( hSemaphore )

CloseHandle( hSemaphore );

PostQuitMessage(0);

break;

default :

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

}

return( 0L );

}

LRESULT CALLBACK About( HWND hDlg,

UINT message,

WPARAM wParam,

LPARAM lParam)

{

switch (message)

{

case WM_INITDIALOG:

return (TRUE);

case WM_COMMAND:

if ( LOWORD(wParam) == IDOK

|| LOWORD(wParam) == IDCANCEL)

{

EndDialog(hDlg, TRUE);

return (TRUE);

}

break;

}

return (FALSE);

}

CreateThread

Описание Функция CreateThreadсоздает нить, которая будет выполняться в адресном пространстве вызывающего процесса. Объект созданной нити может быть использован в качестве объекта синхронизации. Он создается в не­сигнальном состоянии и переходит в сигнальное состояние по завершении нити. Выполнение нити начинается с функции, определяемой параметром lpStartAddr. Если эта функция возвращает значение, тогда это значение типа DWORD,которое используется для завершения нити неявным вызо­вом ExitThread.Для получения значения, возвращаемого нитью, применя­ется функция GetExitCodeThread.Нить создается с приоритетом

THREAD_PRIORITY_NORMAL.Для получения и установки значения приоритета нити применяются функции GetThreadPriority и SetThreadPriority.Объект нити остается в системе до тех пор, пока не завершится нить и не закроются все дескрипторы в результате вызова функции CloseHandle.

Синтаксис HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadSecurity, DWORD cbStack, LPTHREAD_START_ROUTINE lpStartAddr, LPVOID lpvThreadParam, DWORD dwCreateFlags, LPDWORD lpdwThreadId)

Возвращаемое значение HANDLE:Если выполнение рассматриваемой функции оказывается успешным, возвращается дескриптор нового объекта нити. В противном случае возвращается NULL.Для выборки расширенной информации об ошибке используется функция GetLastError.

Включаемый файл winbase.h

См. также CloseHandle, CreateProcess,CreateRemoteThread, ExitThread, GetExitCodeThread, GetLastError, OpenThreadToken, SetErrorMode, TerminateProcess, TerminateThread.

Пример См. пример применения функции CreateSemaphore.

EnterCriticalSection

Описание Функция EnterCriticalSectionожидает получения прав владения указанным объектом критического участка кода. Возврат из функции осуществляет­ся после того, как права владения будут предоставлены вызывающей нити. Объекты критического участка кода вводят в действие взаимно исключа­ющую синхронизацию нитей одного процесса. После того как нить овла­деет объектом критического участка кода, она может вызвать функцию EnterCriticalSectionдля этого же объекта критического участка кода, что однако не приводит к ее самоблокировке. Функция LeaveCriticalSectionдолжна вызываться столько раз, сколько нить входит в критический участок кода, прежде чем другие нити смогут получить права владения данным объектом. Чтобы разрешить взаимно исключающий доступ к разделяемому ресурсу, каждая нить обращается к EnterCriticalSectionлибо к TryEnterCriticalSectionдля запроса прав владения критическим участком кода перед выполнением любого участка кода, в котором требуется дос­туп к защищенному ресурсу.

Синтаксис VOID EnterCriticalSection (LPCRITICAL_SECTION lpcs)

Параметры lpcs LPCRITICAL_SECTION:Указатель на объект критического участка кода.

Включаемый файл winbase.h

См. также CreateMutex, DeleteCriticalSection, InitializeCriticalSection, LeaveCriticalSection.

Пример В приведенном ниже примере выполнение критического участка кода (ожидание в течение пяти секунд) внутри процедуры порожденной нити разрешается только одной нити. Всякий раз, когда пользователь выбирает пункт меню Test!, создается новая нить. Объект критического участка кода в процедуре этой нити упорядочивает выполнение данного участка кода.

См. \local-sources\chap25\25-07.txt

EntCrSec.h

#define IDM_EXIT 100

#define IDM_TEST 200

#define IDM_ABOUT 301

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM);

EntCrSec.rc

#include "windows.h"

#include "EntCrSec.h"

MYAPP ICON DISCARDABLE "GENERIC.ICO"

MYAPP MENU DISCARDABLE

BEGIN

POPUP "&File"

BEGIN

MENUITEM "E&xit", IDM_EXIT

END

MENUITEM "&Test!", IDM_TEST

POPUP "&Help"

BEGIN

MENUITEM "&About My Application...", IDM_ABOUT

END

END

ABOUTBOX DIALOG 22, 17, 171, 43

STYLE DS_MODALFRAME | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU

Наши рекомендации