#define  STRICT
#include <windows.h>
#include <mem.h>
#include <time.h>

#include "hlpmore.h"

BOOL RegisterEWclass(HMODULE hModule);
extern "C" LRESULT CALLBACK _export
EWWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
char const szClassName[]   = "EWHlpMoreClass";

// ========================================================
// Функция LibMain
// Получает управление только один раз при
// загрузке DLL-библиотеки в память
// ========================================================
#pragma argsused
int FAR PASCAL
LibMain(HINSTANCE hModule, WORD wDataSegment,
  WORD wHeapSize, LPSTR lpszCmdLine)
{
  // После инициализации локальной области данных
  // функция LibEntry фиксирует сегмент данных.
  // Его необходимо расфиксировать.
  if(wHeapSize != 0)
    // Расфиксируем сегмент данных
    UnlockData(0);

  // Регистрируем класс для встроенного окна
  if(!RegisterEWclass(hModule))
    return FALSE;
  else
    // Возвращаем TRUE. Это означает, что инициализация
    // DLL-библиотеки выполнена успешно
    return TRUE;
}

// ========================================================
// Функция WEP
// Получает управление только один раз при
// удалении DLL-библиотеки из памяти
// ========================================================
#pragma argsused
int FAR PASCAL WEP(int bSystemExit)
{
  return 1;
}

// ========================================================
// Функция MsgBox
// Выводит на экран диалоговую панель с сообщением
// ========================================================
extern "C" void FAR PASCAL _export
MsgBox(HWND hwnd, LPSTR szMsg)
{
  MessageBox(hwnd, szMsg, "Message from DLL", MB_OK);
}

// ========================================================
// Функция RegisterEWclass
// Регистрация класса для встроенного окна
// ========================================================
BOOL RegisterEWclass(HMODULE hModule)
{
  ATOM aWndClass;
  WNDCLASS wc;

  memset(&wc, 0, sizeof(wc));

  // Класс окна доступен для всех приложений
  wc.style         = CS_GLOBALCLASS;

  wc.lpszMenuName  = NULL;
  wc.lpfnWndProc   = (WNDPROC) EWWndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hModule;
  wc.hIcon         = NULL;
  wc.hCursor       = LoadCursor(hModule, "AppCursor");
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszClassName = (LPSTR)szClassName;

  aWndClass = RegisterClass(&wc);
  return (aWndClass != 0);
}

// ========================================================
// Функция EWWndProc
// Функция встроенного окна
// ========================================================
extern "C" LRESULT CALLBACK _export
EWWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  RECT rc;
  TEXTMETRIC tm;
  int cxChar, cyChar;
  QCI  qci;
  QRI  qri;
  static BYTE szCustomData[80];
  static BYTE szBuf[80];

  #define CLOCK_TIMER 1

  switch (msg)
  {
    // Создание встроенного окна
    case WM_CREATE:
    {
      // Копируем строку параметров
      qci = (QCI)((CREATESTRUCT FAR *)lParam)->lpCreateParams;
      if(lstrlen(qci->szAuthorData) < 80)
        lstrcpy(szCustomData, qci->szAuthorData);

      // Добавляем к встроенному окну рамку 
      SetWindowLong(hwnd, GWL_STYLE,
        GetWindowLong(hwnd,GWL_STYLE) | WS_BORDER);

      // Создаем таймер для часов
      SetTimer(hwnd, CLOCK_TIMER, 1000, NULL);
      return 0;
    }

    // Сообщение от таймера
    case WM_TIMER:
    {
      // Перерисовываем встроенное окно
      InvalidateRect(hwnd, NULL, FALSE);
      return 0;
    }

    // Обработчик сообщения WM_PAINT
    case WM_PAINT:
    {
      int    nBufSize;
      time_t t;
      struct tm *ltime;
      RECT    rc;

      hdc = BeginPaint(hwnd, &ps);

      // Определяем время и его отдельные компоненты
      time(&t);
      ltime = localtime(&t);

      // Подготавливаем буфер, заполняя его
      // строкой с текущим временем
      nBufSize = wsprintf(szBuf, "%02d:%02d:%02d",
        ltime->tm_hour,	ltime->tm_min,	ltime->tm_sec);

      // Выбираем шрифт с фиксированной шириной букв
      SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

      // Получаем координаты и размер окна
      GetClientRect(hwnd, &rc);

      // Выбираем цвет текста для часов
      SetTextColor(ps.hdc, RGB(0,100,0));

      // Выводим время в центре окна
      DrawText(hdc, (LPSTR)szBuf, nBufSize, &rc,
        DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE);

      EndPaint(hwnd, &ps);
      return 0L;
    }

    // Обработчик этого сообщения должен определить
    // размеры встроенного окна
    case EWM_QUERYSIZE:
    {
      // Определяем метрики фиксированного шрифта
      hdc = GetDC(hwnd);
      SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
      GetTextMetrics(hdc, &tm);
      cxChar = tm.tmMaxCharWidth;
      cyChar = tm.tmHeight + tm.tmExternalLeading;
      ReleaseDC(hwnd, hdc);

      // Сохраняем размеры встроенного окна
      ((LPPOINT)lParam)->x = 10 * cxChar;
      ((LPPOINT)lParam)->y = 2  * cyChar;

      return 1;
    }

    // В ответ на это сообщение функция встроенного окна
    // должна вернуть представление содержимого окна
    // в виде текста или битового изображения
    case EWM_RENDER:
    {
      long lPtr = 0l;

      switch(wParam)
      {
        // Представление содержимого в виде текста
        case CF_TEXT:
        {
          // Заказываем глобальный блок памяти
          HGLOBAL hglb = GlobalAlloc(GMEM_MOVEABLE, 50);
          if(hglb)
          {
            // Фиксируем блок памяти
            LPSTR lpszTempBuf = (LPSTR)GlobalLock(hglb);

            // Копируем строку, содержащую текущее время
            lstrcpy(lpszTempBuf,"\r\n");
            lstrcat(lpszTempBuf,szBuf);
            lstrcat(lpszTempBuf,"\r\n");

            // Расфиксируем блок памяти
            GlobalUnlock(hglb);

            // Возвращаем идентификатор блока памяти,
            // содержащего текстовое представление
            lPtr = (long)hglb;
          }
          break;
        }

        // Представление содержимого
        // в виде битового изображения 
        case CF_BITMAP:
        {
          POINT pt;
          RECT rc;
          HBITMAP hbmp, hbmpOld;
          HBRUSH hbrush;

          // Сохраняем указатель на структуру RENDERINFO
          qri = (QRI)lParam;

          // Создаем контекст отображения, совместимый
          // с экраном монитора
          hdc = CreateCompatibleDC(NULL);

          // Определяем размеры встроенного окна
          SendMessage(hwnd, EWM_QUERYSIZE,
            (WPARAM)hdc, (long)(LPPOINT)&pt);

          // Создаем битовое изображение, имеющее размеры,
          // равные размерам встроенного окна 
          rc.left = rc.top = 0;
          rc.right = pt.x;
          rc.bottom = pt.y;
          hbmp = CreateCompatibleBitmap(qri->hdc, pt.x, pt.y);

          // Выбираем битовое изображение в контекст отображения
          hbmpOld = (HBITMAP)SelectObject(hdc,hbmp);

          // Закрашиваем битовое изображение цветом фона
          hbrush = CreateSolidBrush(GetBkColor(hdc));
          FillRect(hdc, &rc, hbrush);
          DeleteObject(hbrush);

          // Рисуем рамку по периметру битового изображения
          Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);

          // Рисуем время в центре битового изображения
          DrawText(hdc, (LPSTR)szBuf, lstrlen(szBuf), &rc,
	          DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE);

          // Выбираем старое битовое изображение
          hbmp = (HBITMAP)SelectObject(hdc, hbmpOld);
          DeleteDC(hdc);

          // Возвращаем идентификатор созданного
          // битового изображения
          lPtr = (long)hbmp;

          break;
        }
      }

      return lPtr;
    }

    // Обрабатываем сообщение от левой клавиши мыши
    case WM_LBUTTONDOWN:
    {
      if(IDYES == MessageBox(hwnd, "Запустить clock.exe?",
       "Help Sample", MB_YESNO | MB_ICONQUESTION))
        WinExec("clock.exe", SW_SHOW);

      #define IDN_CLOCK 200

      // Во временном окне отображаем раздел справочной
      // системы с номером контекста, равным IDN_CLOCK
      WinHelp(hwnd, "hlpmore.hlp", HELP_CONTEXTPOPUP,
          (DWORD)IDN_CLOCK);

      return 0;
    }

    // При удалении встроенного окна уничтожаем таймер
    case WM_DESTROY:
    {
      KillTimer(hwnd, CLOCK_TIMER);
      return 0;
    }

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