Электронная библиотека книг Александра Фролова и Григория Фролова.
 
Библиотека
Братьев
Фроловых
Электронная библиотека книг Александра Фролова и Григория Фролова.
Библиотека системного программиста
Программирование на JAVA
ПК. Шаг за шагом
Другие книги
Восстановление данных
Антивирусная защита
Статьи для
программистов
Пользователю компьютера

Сервер 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");
[Назад] [Содеожание] [Дальше]


Создание интернет-магазинов: http://www.shop2you.ru/ © Александр Фролов, Григорий Фролов, 1991-2016