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. Для выборки расширенной информации об ошибке используется функция GetLastError.
Включаемый файл 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