#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);
  }
}

