Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование для Windows NT

© Александр Фролов, Григорий Фролов
Том 26, часть 1, М.: Диалог-МИФИ, 1996, 272 стр.

[Назад] [Содеожание] [Дальше]

Приложение MutexSDI

Для демонстрации способор работы с объектами Mutex мы переделали исходные тексты приложения MultiSDI, в котором синхронизация задач выполнялась при помощи критических секций. В новом приложении MutexSDI вместо критической секции мы использовали объект Mutex. Кроме того, в приложении MutexSDI демонстрируется способ обнаружения работающей копии приложения, основанный на том, что имена объектов Mutex являются глобальными (так же, как и имена объектов-событий, рассмотренных нами ранее).

Исходный текст приложения приведен в листинге 4.3. Так как он сделан из исходного текста приложения MultiSDI, мы опишем только основные отличия.

Листинг 4.3. Файл mutexsdi/mutexsdi.c


#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <process.h>
#include <stdio.h>
#include "resource.h"
#include "afxres.h"
#include "mutexsdi.h"

HINSTANCE hInst;
char szAppName[]   = "MutexMultiSDI";
char szAppTitle[]  = "Multithread SDI Application with Mutex";

// Имя объекта Mutex
char szMutexName[] = "$MyMutex$MutexMultiSDI$";

// Идентификатор объекта Mutex
HANDLE hMutex;

// Признак завершения всех задач
BOOL fTerminate = FALSE;

// Массив идентификаторов запущенных задач
HANDLE hThreads[3];

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  // Сохраняем идентификатор приложения
  hInst = hInstance;

  // Создаем объект Mutex  
  hMutex = CreateMutex(NULL, FALSE, szMutexName);
  if(hMutex == NULL)
  {
    MessageBox(NULL, "CreateMutex Error",
        szAppTitle, MB_OK | MB_ICONEXCLAMATION);
    return 0l;
  }

  // Преверяем, не было ли это приложение запущено ранее
  if(GetLastError() == ERROR_ALREADY_EXISTS)
  {
    MessageBox(NULL, "MutexSDI already started",
        szAppTitle, MB_OK | MB_ICONEXCLAMATION);
    return 0l;
  }

  // Регистрируем класс окна
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), 
    IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), 
    IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
	  return FALSE;
    
  // Создаем главное окно приложения
  hWnd = CreateWindow(szAppName, szAppTitle, 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 
     NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  // Отображаем окно и запускаем цикл 
  // обработки сообщений
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, 
        LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_CREATE,     WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY,    WndProc_OnDestroy);
    HANDLE_MSG(hWnd, WM_PAINT,      WndProc_OnPaint);
    HANDLE_MSG(hWnd, WM_COMMAND,    WndProc_OnCommand);

    default:
      return(DefWindowProc(hWnd, msg, wParam, lParam));
  }
}

// -----------------------------------------------------
// Функция WndProc_OnCreate
// -----------------------------------------------------
BOOL WndProc_OnCreate(HWND hWnd, 
                      LPCREATESTRUCT lpCreateStruct)
{
  // Сбрасываем флаг завершения задач
  fTerminate = FALSE;

  // Запускаем три задачи, сохраняя их идентификаторы
  // в массиве
  hThreads[0] = (HANDLE)_beginthread(PaintEllipse, 
    0, (void*)hWnd);
  hThreads[1] = (HANDLE)_beginthread(PaintRect, 
    0, (void*)hWnd);
  hThreads[2] = (HANDLE)_beginthread(PaintText, 
    0, (void*)hWnd);

  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  // Устанавливаем флаг завершения задач
  fTerminate = TRUE;

  // Дожидаемся завершения всех трех задач
  WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);
  
  // Перед завершением освобождаем идентификатор 
  // объекта Mutex
  CloseHandle(hMutex);
  
  // Останавливаем цикл обработки сообщений, расположенный 
  // в главной задаче
  PostQuitMessage(0);
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnPaint
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnPaint(HWND hWnd)
{
  HDC hdc;
  PAINTSTRUCT ps;
  RECT rc;
  DWORD dwRetCode;

  // Ожидаем, пока объект Mutex не перейдет в
  // отмеченное состояние
  dwRetCode = WaitForSingleObject(hMutex, INFINITE);
  
  // Если не было ошибок, выполняем рисование
  if(dwRetCode == WAIT_OBJECT_0)
  {
    // Перерисовываем внутреннюю область окна
    hdc = BeginPaint(hWnd, &ps);
    GetClientRect(hWnd, &rc);
    DrawText(hdc, "SDI Window", -1, &rc,
      DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    EndPaint(hWnd, &ps);

    // Переводим объект Mutex в неотмеченное состояние
    ReleaseMutex(hMutex);
  }

  return 0;
}
// -----------------------------------------------------
// Функция WndProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  switch (id)
  {
    case ID_FILE_EXIT:  
    {
      // Завершаем работу приложения
      PostQuitMessage(0);
      return 0L;
        break;
    }
	  
    case ID_HELP_ABOUT:
    {
      MessageBox(hWnd, 
        "Multithread SDI Application with Mutex\n"
        "(C) Alexandr Frolov, 1996\n"
        "Email: frolov@glas.apc.org",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
	     return 0L;
	     break;
    }
    default:
      break;
  }
  return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify,
    DefWindowProc);
}

// -----------------------------------------------------
// Функция задачи PaintEllipse
// -----------------------------------------------------
void PaintEllipse(void *hwnd)
{
  HDC hDC;
  RECT rect;
  LONG xLeft, xRight, yTop, yBottom;
  short nRed, nGreen, nBlue;
  HBRUSH hBrush, hOldBrush;
  DWORD dwRetCode;

  srand((unsigned int)hwnd);
  while(!fTerminate) 
  {
    // Ожидаем, пока объект Mutex не перейдет в
    // отмеченное состояние
    dwRetCode = WaitForSingleObject(hMutex, INFINITE);
    
    // Если не было ошибок, выполняем рисование
    if(dwRetCode == WAIT_OBJECT_0)
    {
      hDC = GetDC(hwnd);

      nRed   = rand() % 255;
      nGreen = rand() % 255;
      nBlue  = rand() % 255;
    
      GetWindowRect(hwnd, &rect);
    
      xLeft   = rand() % (rect.left   + 1);
      xRight  = rand() % (rect.right  + 1);
      yTop    = rand() % (rect.top    + 1);
      yBottom = rand() % (rect.bottom + 1);

      hBrush = CreateSolidBrush(RGB(nRed, nGreen, nBlue));
      hOldBrush = SelectObject(hDC, hBrush);
      Ellipse(hDC, min(xLeft, xRight), min(yTop, yBottom),
                   max(xLeft, xRight), max(yTop, yBottom));
      SelectObject(hDC, hOldBrush);
      DeleteObject(hBrush);
      ReleaseDC(hwnd, hDC);

      ReleaseMutex(hMutex);
    }
    // Если ожидание было отменено или произошла ошибка,
    // прерываем цикл
    else
    {
      break;
    }
    Sleep(100);
  }
}

// -----------------------------------------------------
// Функция задачи PaintRect
// -----------------------------------------------------
void PaintRect(void *hwnd)
{
  HDC hDC;
  RECT rect;
  LONG xLeft, xRight, yTop, yBottom;
  short nRed, nGreen, nBlue;
  HBRUSH hBrush, hOldBrush;
  DWORD dwRetCode;

  srand((unsigned int)hwnd + 1);
  while(!fTerminate) 
  {
    dwRetCode = WaitForSingleObject(hMutex, INFINITE);
    if(dwRetCode == WAIT_OBJECT_0)
    {
      hDC = GetDC(hwnd);
      nRed   = rand() % 255;
      nGreen = rand() % 255;
      nBlue  = rand() % 255;
      GetWindowRect(hwnd, &rect);
      xLeft   = rand() % (rect.left   + 1);
      xRight  = rand() % (rect.right  + 1);
      yTop    = rand() % (rect.top    + 1);
      yBottom = rand() % (rect.bottom + 1);
      hBrush = CreateSolidBrush(RGB(nRed, nGreen, nBlue));
      hOldBrush = SelectObject(hDC, hBrush);
      Rectangle(hDC, min(xLeft, xRight), min(yTop, yBottom),
                   max(xLeft, xRight), max(yTop, yBottom));
      SelectObject(hDC, hOldBrush);
      DeleteObject(hBrush);
      ReleaseDC(hwnd, hDC);
      ReleaseMutex(hMutex);
    }
    else
    {
      break;
    }
    Sleep(100);
  }
}

// -----------------------------------------------------
// Функция задачи PaintText
// -----------------------------------------------------
void PaintText(void *hwnd)
{
  HDC hDC;
  RECT rect;
  LONG xLeft, xRight, yTop, yBottom;
  short nRed, nGreen, nBlue;
  DWORD dwRetCode;

  srand((unsigned int)hwnd + 2);
  while(!fTerminate) 
  {
    dwRetCode = WaitForSingleObject(hMutex, INFINITE);
    if(dwRetCode == WAIT_OBJECT_0)
    {
      hDC = GetDC(hwnd);
      GetWindowRect(hwnd, &rect);
      xLeft   = rand() % (rect.left   + 1);
      xRight  = rand() % (rect.right  + 1);
      yTop    = rand() % (rect.top    + 1);
      yBottom = rand() % (rect.bottom + 1);
      nRed   = rand() % 255;
      nGreen = rand() % 255;
      nBlue  = rand() % 255;
      SetTextColor(hDC, RGB(nRed, nGreen, nBlue));
      nRed   = rand() % 255;
      nGreen = rand() % 255;
      nBlue  = rand() % 255;
      SetBkColor(hDC, RGB(nRed, nGreen, nBlue));
      TextOut(hDC, xRight - xLeft,
        yBottom - yTop,"TEXT", 4);
      ReleaseDC(hwnd, hDC);
      ReleaseMutex(hMutex);
    }
    else
    {
      break;
    }
    Sleep(100);
  }
}

В области глобальных переменных определен массив szMutexName, в котором хранится имя объекта Mutex, используемого нашим приложением. Идентификатор этого объекта после его создания будет записан в глобальную переменную hMutex.

Функция WinMain создает объект Mutex, вызывая для этого функцию CreateMutex, как это показано ниже:


hMutex = CreateMutex(NULL, FALSE, szMutexName);
if(hMutex == NULL)
{
  . . .
  return 0l;
}

В качестве атрибутов защиты передается значение NULL. Так как второй параметр функции CreateMutex XE "CreateMutex" равен FALSE, объект Mutex после создания будет находиться в отмеченном состоянии.

Если при создании объекта функция CreateMutex XE "CreateMutex" не вернула признак ошибки (значение NULL), приложение проверяет, действительно ли был создан новый объект Mutex или же функция вернула идентификатор для уже существующего в системе объекта с таким же именем. Проверка выполняется с помощью функции GetLastError XE "GetLastError" :


if(GetLastError() == ERROR_ALREADY_EXISTS)
{
  MessageBox(NULL, "MutexSDI already started",
      szAppTitle, MB_OK | MB_ICONEXCLAMATION);
  return 0l;
}

Если эта функция вернула значение ERROR_ALREADY_EXISTS, значит была запущена копия этого приложения, которая и создала объект Mutex с именем szMutexName. В этом случае приложение выводит сообщение и завершает свою работу.

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

Главная задача выполняет рисование при обработке сообщения WM_PAINT. Обработкой этого сообщения занимается функция WndProc_OnPaint.

Перед тем как получить контекст отображения и приступить к рисованию, эта функция вызывает функцию WaitForSingleObject XE "WaitForSingleObject" , пытаясь стать владельцем объекта Mutex:


dwRetCode = WaitForSingleObject(hMutex, INFINITE);

Если никакая другая задача в данный момент не претендует на этот объект и, следовательно, не собирается рисовать в окне приложения, функция WaitForSingleObject XE "WaitForSingleObject" немедленно возвращает управление с кодом возврата, равным WAIT_OBJECT_0. После этого функция WndProc_OnPaint получает контекст отображения, выполняет рисование, освободает контекст отображения и затем переводит объект Mutex в неотмеченное состояние, вызывая функцию ReleaseMutex XE "ReleaseMutex" :


ReleaseMutex(hMutex);

После этого другие задачи, выполняющие ожидание для объекта Mutex, могут продолжить свою работу.

В приложении определены три функции задач, выполняющих рисование в окне приложения. Синхронизация всех этих функций между собой и с главной задачей процесса выполняется одинаковым способом. Все эти задачи выполняют в цикле ожидание события для объекта Mutex с идентификатором hMutex:


while(!fTerminate) 
{
  dwRetCode = WaitForSingleObject(hMutex, INFINITE);
  if(dwRetCode == WAIT_OBJECT_0)
  {
    hDC = GetDC(hwnd);
     . . .
    Ellipse(hDC, min(xLeft, xRight), min(yTop, yBottom),
                 max(xLeft, xRight), max(yTop, yBottom));
     . . .
    ReleaseDC(hwnd, hDC);
    ReleaseMutex(hMutex);
  }
  else
    break;
  Sleep(100);
}

Если ни одна задача не владеет объектом Mutex, функция WaitForSingleObject XE "WaitForSingleObject" возвращает значение WAIT_OBJECT_0. При этом задача выполняет рисование в окне приложения. В том случае когда какая-либо другая задача владеет объектом Mutex, данная задача перейдет в состояние ожидания. Если при ожидании произошла ошибка, цикл завершает свою работу.

После рисования задача переводит объект Mutex в неотмеченное состояние с помощью функции ReleaseMutex XE "ReleaseMutex" .

Кратко перечислим другие файлы, имеющие отношение к приложению MutexSDI.

В файле mutexsdi.h (листинг 4.4) находятся прототипы функций, определенных в приложении.

Листинг 4.4. Файл mutexsdi/mutexsdi.h


LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL WndProc_OnCreate(HWND hWnd, 
  LPCREATESTRUCT lpCreateStruct);
void WndProc_OnDestroy(HWND hWnd);
void WndProc_OnPaint(HWND hWnd);
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify);

void PaintEllipse(void *hwnd);
void PaintRect(void *hwnd);
void PaintText(void *hwnd);

Файл resource.h (листинг 4.5) создается автоматически и содержит определения констант для файла ресурсов приложения.

Листинг 4.5. Файл mutexsdi/resource.h


//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by MutexSDI.RC
//
#define IDR_APPMENU                     102
#define IDI_APPICON                     103
#define IDI_APPICONSM                   104
#define ID_FILE_EXIT                    40001
#define ID_HELP_ABOUT                   40003
#define ID_FORMAT_BOLD                  40010
#define ID_FORMAT_ITALIC                40011
#define ID_FORMAT_UNDERLINE             40012
#define ID_FORMAT_PARAGRAPH_LEFT        40014
#define ID_FORMAT_PARAGRAPH_RIGHT       40015
#define ID_FORMAT_PARAGRAPH_CENTER      40016
#define ID_EDIT_DELETE                  40021
#define ID_FILE_SAVEAS                  40024
#define ID_EDIT_SELECTALL               40028
#define ID_SETPROTECTION_PAGENOACCESS   40035
#define ID_SETPROTECTION_PAGEREADONLY   40036
#define ID_SETPROTECTION_PAGEREADWRITE  40037
#define ID_SETPROTECTION_PAGEGUARD      40038
#define ID_MEMORY_READ                  40039
#define ID_MEMORY_WRITE                 40040
#define ID_MEMORY_LOCK                  40041
#define ID_MEMORY_UNLOCK                40042

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        121
#define _APS_NEXT_COMMAND_VALUE         40043
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

И, наконец, файл ресурсов приложения mutexsdi.rc приведен в листинге 4.6.

Листинг 4.6. Файл mutexsdi/mutexsdi.rc


//Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

//////////////////////////////////////////////////////////////
// Menu
//

IDR_APPMENU MENU DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "E&xit",                       ID_FILE_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",                   ID_HELP_ABOUT
    END
END

#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////
// Icon
//

// Icon with lowest ID value placed first to ensure 
// application icon
// remains consistent on all systems.
IDI_APPICON             ICON    DISCARDABLE     "mutexsdi.ico"
IDI_APPICONSM           ICON    DISCARDABLE     "mutexssm.ico"

//////////////////////////////////////////////////////////////
// String Table
//

STRINGTABLE DISCARDABLE 
BEGIN
    ID_FILE_EXIT            "Quits the application"
END

#endif    // English (U.S.) resources
//////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 3 resource.
//
//////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
[Назад] [Содеожание] [Дальше]