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

Сервер Web своими руками. Язык HTML, приложения CGI и ISAPI, установка серверов Web для Windows

© Александр Фролов, Григорий Фролов
Том 29, М.: Диалог-МИФИ, 1997, 288 стр.

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

Счетчик посещений XBMCNT

Еще один счетчик посещений, который мы рассмотрим в нашей книге, создан на основе приложения ISAPI, которое называется XBMCNT. Это приложение создает графическое изображение счетчика в виде файла формата XBM или X-Bitmap, что одно и тоже.

Внешний вид страницы, на которой расположен наш графический счетчик, показан на рис. 8.8.

Рис. 8.8. Графический счетчик на странице сервера WWW

Исходный текст соответствующего документа HTML приведен в листинге 8.10.

Листинг 8.10. Файл chap8\xbmcnt\xbmcnt.htm


<HTML>
  <BODY BGCOLOR="#FFFFFF">
    <H1>Главная страница фирмы XYZ Inc.</H1>
    <P>Добро пожаловать на нашу главную страницу!
    <HR>
    <P>Вы посетитель номер 
    <IMG ALT="Счетчик доступа" SRC=/scripts/xbmcnt.dll?">с 1 января 1913 года
  </BODY>
</HTML>

Обратите внимение, что ссылка на файл библиотеки приложения ISAPI здесь сделана с помощью оператора <IMG>. Когда навигатор отображает такую страницу, он запускает приложение xbmcnt.dll, а то, в свою очередь, посылает навигатору массив данных изображения.

В каком виде?

До сих пор наши приложения ISAPI возвращали навигатору документы HTML, сформированные динамически. Однако есть и другая возможность - приложение ISAPI может возвратить навигатору данные типа MIME, например, графическое изображение.

Для этого в заголовке HTTP, который отправляется навигатору, необходимо указать правильный тип данных. В нашем случае мы будем работать с графическим изображением в формате XBM, для которого определен тип данных MIME, обозначаемый как image/x-xbitmap. Поэтому заголовок Content-type протокола HTTP должен выглядеть следующим образом:


Content-type: image/x-xbitmap

Вслед за этим заголовком должна идти одна пустая строка и массив данных изображения в формате XBM.

Изображение в формате XBM представляет собой достаточно простую вещь - это просто определение массива данных на языке программирования Си. Приведем пример.

Пусть например, у нас есть файл counter.xbm, в котором хранится черно-белое изображение цифры 0 высотой 8 пикселов и шириной 16 пикселов (формат XBM не позволяет хранить цветные изображения). Этот файл должен иметь следующий вид:


#define counter_width  8
#define counter_height 16
static unsigned char counter_bits[] =
{
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 
    0x42, 0x42, 0x42, 0x3C 
};

Файл определения изображения XBM всегда текстовый.

Первые две строки файла определяют константы, содержащие размеров графического изображения. Имена этих констант образуются из имени файла (без расширения) и строк _width и _height, соответственно, для значений ширины и высоты изображения.

Далее в файле располагается определение статического массива данных, имя которого образуется из имени файла и строки _bits. В приведенном выше массиве каждый байт соответствует одной строке растра изображения:

0x00

               

0x00

               

0x00

               

0x00

               

0x00

               

0x00

               

0x3C

    X X X X    

0x42

  X         X  

0x42

  X         X  

0x42

  X         X  

0x42

  X         X  

0x42

  X         X  

0x42

  X         X  

0x42

  X         X  

0x42

  X         X  

0x3C

    X X X X    

Для того чтобы сделать графический счетчик посещений, в нашем приложении подготовлен массив для каждой цифры от 0 до 9. Отображая значение счетчика посещений, приложение комбинирует одно изображение из указанных массивов.

Теперь, когда мы познакомились с форматом XBM и знаем, какие данные должно вернуть приложение ISAPI навигатору, рассмотрим исходный текст приложения XBMCNT, приведенный в листинге 8.11. Мы сделали это приложение на основе приложения COUNTER из примеров Microsoft Visual C++ версии 4.2, упростив и изменив его таким образом, чтобы не использовать библиотеку классов MFC.

Листинг 8.11. Файл chap8\xbmcnt\xbmcnt.c


// ===============================================
// Расширение ISAPI xbmcnt.c
// Счетчик посещений страниц
//
// (C) Фролов А.В., 1997
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//         или
//         http://www.dials.ccas.ru/frolov
// ===============================================

#include <windows.h>
#include <httpext.h>
#include <stdio.h>
#include <string.h>

// Ширина символа
#define char_width 8

// Высота символа
#define char_height 16

// Массив растровых изображений цифровых символов
static unsigned char char_bits[10][char_height] = 
{
  // Изображение цифры 0
  {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 
    0x42, 0x42, 0x42, 0x3C 
  },
	
  // Изображение цифры 1
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x10, 0x18, 0x14, 0x10,	0x10, 0x10, 
    0x10, 0x10, 0x10, 0x7C 
  },

  // Изображение цифры 2
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x3E, 0x22, 0x20, 0x20, 0x10, 0x08, 
    0x04, 0x02, 0x22, 0x3F 
  },

  // Изображение цифры 3
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x3E, 0x22, 0x40, 0x40, 0x3C, 0x40, 
    0x40, 0x40, 0x22, 0x3E 
  },

  // Изображение цифры 4
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x10, 0x18, 0x14, 0x12, 0x12, 0x3E, 
    0x10, 0x10, 0x10, 0x38 
  },

  // Изображение цифры 5
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x3E, 0x02, 0x02, 0x02, 0x3E, 0x20, 
    0x20, 0x20, 0x22, 0x2C 
  },

  // Изображение цифры 6
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x1C, 0x06, 0x02, 0x02, 0x1E, 0x22, 
    0x22, 0x22, 0x22, 0x1C 
  },

  // Изображение цифры 7
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x3E, 0x22, 0x20, 0x20, 0x10, 0x08, 
    0x08, 0x08, 0x08, 0x08 
  },

  // Изображение цифры 8
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x1C, 0x22, 0x22, 0x22, 0x1C, 0x22, 
    0x22, 0x22, 0x22, 0x1C 
  },

  // Изображение цифры 9
  { 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x1C, 0x22, 0x22, 0x22, 0x3C, 0x20, 
    0x20, 0x20, 0x20, 0x1C 
  },
};

void PrintCounter(LPSTR szCounter, LPSTR szBuffer);

// =============================================================
// Функция GetExtensionVersion
// Запись версии интерфейса ISAPI и
// строки описания расширения
// =============================================================
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
  // Записываем версию интерфейса ISAPI
  pVer->dwExtensionVersion = 
    MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR );

  // Записываем строку описания расширения
  lstrcpyn(pVer->lpszExtensionDesc,
    "ISAPI Counter", HSE_MAX_EXT_DLL_NAME_LEN);

  return TRUE;
}

// =============================================================
// Функция HttpExtensionProc
// =============================================================
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *lpECB)
{
  // Рабочий буфер
  CHAR  szBuff[4096];

  // Идентификатор файла счетчика
  HANDLE hCounterFile;

  // Количество прочитанных байт
  DWORD dwBytesRead;

  // Количество записанных байт
  DWORD dwBytesWritten;

  // Результат выполнения операции
  BOOL bResult;

  // Временный буфер для работы со счетчиком
  CHAR  szBuf[20];

  // Текущее значение счетчика
  INT   nCounter;

  // Нулевой код состояния - признак успешного выполнения
  lpECB->dwHttpStatusCode = 0;

  // -----------------------------------------------
  // Увеличиваем значение счетчика в файле
  // -----------------------------------------------

  // Открываем файл счетчика для чтения
  hCounterFile = CreateFile("CNTDAT.DAT", 
    GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
    FILE_FLAG_SEQUENTIAL_SCAN, NULL);

  // Читаем из файла строку значения счетчика
  bResult = ReadFile(hCounterFile, szBuf, 7, 
    &dwBytesRead, NULL);

  // Закрываем файл счетчика
  CloseHandle(hCounterFile);
  
  // Преобразуем значение счетчика из текстовой
  // строки в численную величину
  sscanf(szBuf, "%d", &nCounter);
  
  // Увеличиваем значение счетчика
  nCounter++;
  
  // Записываем в буфер szBuf пять цифр нового
  // значения счетчика
  sprintf(szBuf, "%05.5ld", nCounter);

  // Сохраняем новое значение счетчика в файле
  hCounterFile = CreateFile("CNTDAT.DAT", 
    GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
    FILE_FLAG_SEQUENTIAL_SCAN, NULL);

  WriteFile(hCounterFile, szBuf, strlen(szBuf),
    &dwBytesWritten, NULL);

  CloseHandle(hCounterFile);
  
  // Записываем в буфер заголовок HTTP
  wsprintf(szBuff, "Content-type: image/x-xbitmap\r\n\r\n");

  // Выводим биты графического изображения в формате XBM
  PrintCounter(szBuf, szBuff);

  // Посылаем содержимое буфера удаленному пользователю
  if(!lpECB->ServerSupportFunction(lpECB->ConnID,
    HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, 
    (LPDWORD)szBuff))
  {
    // Если послать данные не удалось, 
    // завершаем работу нашего расширения ISAPI 
    // с кодом ошибки
    return HSE_STATUS_ERROR;
  }

  // Записываем код успешного завершения
  lpECB->dwHttpStatusCode = 200;
  
  // Возвращаем принак успешного завершения  
  return HSE_STATUS_SUCCESS;
}

// =============================================================
// Функция PrintCounter
// =============================================================
void PrintCounter(LPSTR szCounter, LPSTR szBuff)
{
  // Временный буфер
  CHAR  szTempBuf[4096];

  // Полная ширина изображения счетчика
  int nFinalWidth;

  // Полная высота изображения счетчика
  int nFinalHeight;

  // Номер текущей строки растра изображения
  int nLine;

  // Текущий символ
  unsigned int nChar;

  // Смещение в массиве изображений цифр
  int nDigitOffset;

  // Полная ширина изображения вычисляется исходя из
  // количества цифр отображаемой строки
  nFinalWidth  = char_width * strlen(szCounter);

  // Полная высота изображения равна высоте 
  // изображения цифр
  nFinalHeight = char_height;

  // Отменяем кэширование
  wsprintf(szTempBuf, "Expires: Thu, 03 Dec 1996 10:00:00 GMT\r\n");
  strcat(szBuff, szTempBuf);

  wsprintf(szTempBuf, "Pragma: no-cache\r\n");
  strcat(szBuff, szTempBuf);

  // Выводим заголовок изображения XBM
  wsprintf(szTempBuf, "#define counter_width %d\r\n",
    nFinalWidth);
  strcat(szBuff, szTempBuf);

  wsprintf(szTempBuf, "#define counter_height %d\r\n", 
    nFinalHeight);
  strcat(szBuff, szTempBuf);

  wsprintf(szTempBuf, "static unsigned char counter_bits[] = {\r\n");
  strcat(szBuff, szTempBuf);

  // Выводим в цикле биты изображения счетчика
  for(nLine=0; nLine < nFinalHeight; nLine++)
  {
    for(nChar=0; nChar < strlen(szCounter); nChar++)
    {
      nDigitOffset = szCounter[nChar] - '0';

      wsprintf(szTempBuf, "0x%02X, ", 
        char_bits[nDigitOffset][nLine]);

      strcat(szBuff, szTempBuf);
    }
  }
 
  // Выводим строку, закрывающую структуру в файле XBM
  strcat(szBuff, "};\r\n");
}

Файл определения модуля нашего расширения ISAPI представлен в листинге 8.12.

Листинг 8.12. Файл chap8\xbmcnt\xbmcnt.def


LIBRARY	     xbmcnt

DESCRIPTION  'ISAPI Counter'

EXPORTS
    GetExtensionVersion
    HttpExtensionProc

Константы char_width и char_height задают, соответственно, ширину и высоту изображений цифр, из которых формируется графическое изображение счетчика.

В массиве char_bits определено десять растровых изображений - по одному для каждой цифры от 0 до 9.

Функция GetExtensionVersion записывает версию интерфейса ISAPI и строку описания, как это было сделано и в других наших приложениях.

Функция HttpExtensionProc выполняет всю полезную работу.

Прежде всего, она записывает код успешного завершаения в поле dwHttpStatusCode структуры EXTENSION_CONTROL_BLOCK. Если произойдет ошибка, этот код следует изменить.

Далее функция HttpExtensionProc увеличивает значение счетчика посещений в файле. Заметим, что так как библиотека ISAPI работает в мультизадачном режиме, вы должны использовать критическую секцию для предотвращения конфликтов при попытке одновременной работы с файлом счетчика. Когда вы будете создавать свой счетчик на базе нашего примера, не забудьте об этом. О критических секциях мы рассказывали в 26 томе “Библиотеки системного программиста”, который называется “Программирование для Windows NT. Часть 1”. Там же вы найдете соответствующие примеры программ.

Приложение XBMCNT открывает файл счетчика с именем CNTDAT.DAT для чтения в текущем каталоге, пользуясь для этого функцией CreateFile. Далее из этого файла функцией ReadFile приложение считывает семь байт данных в буфер szBuf и закрывает файл функцией CloseHandle.

При помощи функции sscanf прочитанное текстовое значение счетчика преобразуется в двоичную форму и записывается в переменную nCounter.

Далее значение счетчика в переменной nCounter увеличивается на единицу и сохраняется в файле счетчика в текстовом виде.

Все использованные нами функции, предназначенные для работы с файлами, были подробно описаны в 26 и 27 томах “Библиотеки системного программиста”.

После того как содержимое файла счетчика будет обновлено, функция HttpExtensionProc возвращает навигатору заголовок HTTP и биты графического изображения.

Заголовок записывается в буфер szBuff при помощи функции wsprintf:


wsprintf(szBuff, "Content-type: image/x-xbitmap\r\n\r\n");

Далее к этому буферу функция PrintCounter, определенная в нашем приложении, дописывает биты изображения, после чего функция ServerSupportFunction с параметром HSE_REQ_SEND_RESPONSE_HEADER отправляет данные навигатору.

Теперь о функции PrintCounter.

Эта функция получает два параметра - szCounter и szBuff. Первый из них содержит указатель на текстовую строку значения счетчика, второй - указатель на буфер, в который нужно дописывать биты графического изображения.

Вначале функция PrintCounter вычисляет размеры изображения счетчика, умножая количество цифр в строке szCounter на ширину изображения одной цифры.

Далее к буферу дописываются два дополнительных заголовка HTTP, отменяющих кэширование страницы:


wsprintf(szTempBuf, "Expires: Thu, 03 Dec 1996 10:00:00 GMT\r\n");
strcat(szBuff, szTempBuf);
wsprintf(szTempBuf, "Pragma: no-cache\r\n");
strcat(szBuff, szTempBuf);

На следующем этапе к буферу szBuff дописываются три начальные строки файла в формате XBM:


wsprintf(szTempBuf, "#define counter_width %d\r\n", nFinalWidth);
strcat(szBuff, szTempBuf);
wsprintf(szTempBuf, "#define counter_height %d\r\n", nFinalHeight);
strcat(szBuff, szTempBuf);
wsprintf(szTempBuf, "static unsigned char counter_bits[] = {\r\n");
strcat(szBuff, szTempBuf);

Далее функция PrintCounter выводит в цикле шестнадцатиричные значения графического изображения счетчика по строкам растра, пользуясь значениями из массива char_bits.

Определение массива завершается дописыванием закрывающей фигурной скобки и символа точка с запятой:


strcat(szBuff, "};\r\n");
[Назад] [Содеожание] [Дальше]