Сервер 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. В приведенном выше массиве каждый байт соответствует одной строке растра изображения:
Для того чтобы сделать графический счетчик посещений, в нашем приложении подготовлен массив для каждой цифры от 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"); |