Конечно же вы попадали в такую ситуацию, когда приложение, разработанное вами ранее, могло быть снова использовано в рамках другого проекта. Вначале вы конечно же подумали, что это не создаст никаких проблем. Всего-то необходимо скопировать код из одного каталога в другой! Со временем вы осознали, что проекты могут различаться между собой различными параметрами, пусть даже самыми незначительными. Например, это может быть e-mail адрес на который отсылаются сообщения. В таком случае вам ничего не остается, как открыть множество файлов в редакторе и изменить их содержимое, вставляя нужный e-mail при помощи функции найти/заменить. Эта статья расскажет вам о том, как можно избавить себя от подобной работы, а так же порекомендует ряд дополнительных средств для создания и чтения конфигурационных файлов.
Повторное использование кода
Компьютер был изобретен для того, чтобы избавить человека от лишней работы. Развитие компьютерных технологий привело к тому, что человек стал стремиться все меньше времени проводить за компьютером. Допустим, вы программист. Не будь компьютера, вы бы остались без работы. Но в то же время вы стараетесь с помощью компьютера упростить свою ежедневную работы, с этой целью вы используете, например, функцию автозавершения кода в редакторе. Мы хотим подвести вас к той мысли, что код созданный вами, должен быть организован так, чтобы работы по его модификации были сведены к минимуму. Чаще всего это удается, когда вы создаете код, автоматизирующий рутинные операции, такие как создание и прорисовка формы, а так же отправка e-mail. Однако не стоит забывать, что функции для выполнения рутинных операций никогда не бывают на 100% идентичными в различных приложениях. Один формуляр не похож на другой, а сообщения электронной почты предназначены разным адресатам. Однако логика на уровне приложения остается прежней, функции различаются между собой только некоторыми параметрами. Таким образом, вы должны ясно представлять свою цель – разработать код, параметры которого можно было бы определять извне.
Модульная организация
Для решения этой задачи, планируя структуру приложения, вы должны позаботиться о модульности. То есть вам необходимо поместить часто используемые функции или классы в отдельный файл, который будет подключаться через require_once. В этом случае файлы приложения не будут наполнены избыточным кодом. Допустим, вы часто осуществляете запись в лог-файл. В таком случае было бы неплохо код, выполняющий эту операцию, заключить в рамки класса или функции. Будет еще лучше, если вы воспользуетесь уже готовым классом, взятым из какой-нибудь библиотеки исходных кодов, например PEAR.
Параметры процедурального кода
После того, как вы проанализировали код, выделили повторяющиеся фрагменты, распределили их по классам и функциям, необходимо подумать о выделении необходимых параметров, значения которых будут устанавливаться извне. Если речь идет о процедуральном коде, самым простым решением является использование глобальных переменных, которые необходимо определить в отдельном файле. Это позволит в дальнейшем без проблем изменять их значения.
Листинг 1 демонстрирует функцию, которая занимается отправкой e-mail. В ее теле содержится только одна php-функция - mail(). Таким образом, мы избавляемся от необходимости каждый раз указывать получателя при отправке сообщения. Следующая переменная, которую мы определяем, обозначает префикс, предшествующий теме сообщения. Конфигурационный файл, подключаемый через require_once, мог бы выглядеть следующим образом.
Listing 1
Есть способ лучше
Даже если рассмотренный выше способ и является действенным, однако это не самое лучшее решение. По мере того как код вашего приложения будет усложняться, вырастет и число опций, тогда могут возникнуть следующие проблемы:
Глобальные переменные, которые мы используем, могут породить конфликты в пространстве имен.
В том случае, если конфигурационные файлы редактируются не программистом, а дилетантом, в системе могут возникнуть синтаксические ошибки, например из-за незакрытых кавычек.
Для того, чтобы получить доступ к различным переменным, необходимо обращаться к массиву $_GLOBALS.
Вместо php-модулей существуют другие форматы, которые могут быть легко поняты и изменены дилетантами, а так же php-скриптами. Мы имеем в виду два формата: этого широко используемые операционной системой Windows ini-файлы, а так же формат XML.
PHP уже содержит функцию parse_ini_file(), которая без проблем читает ini-файлы. Такой файл имеет очень простую структуру. Каждой опции может быть присвоено только одно значение, а в качестве оператора присваивания используется знак равенства. Конфигурационный файл из предыдущего примера выглядел бы следующим образом в ini-формате.
После считывания ini-файла, имя которого передается в качестве параметра функции parse_ini_file(), мы получаем ассоциативный массив, имеющий вид:
В листинге 2 находится функция отправки почты, основанная на ini-файлах:
Listing 2
Если вы уже прочитали документацию по функции parse_ini_file(), вы кончено же заметили, что она может принимать и второй параметр. Он необходим, если вы хотите разделить ini-файл на несколько разделов или секций. Предположим, вам необходимо сохранить несколько настроек электронной почты. Тогда ini-файл будет выглядеть следующим образом:
"
Если вы при вызове parse_ini_file() передаете true в качестве второго параметра, в этом случае php будет искать в файле секции, а затем вернет многомерный массив, в котором каждой секции (errors и contact) будет соответствовать определенный набор значений:
Особые значения в ini-файлах
При использовании ini-файлов вы должны иметь в виду, что некоторые особые значения могут быть представлены строками. Допустим, вы определяете значение опции как true или yes (без кавычек), в таком случае они автоматически конвертируются в число 1, а false или no – в пустую строку. К сожалению, при этом не генерируется никакой ошибки. Поэтому не пытайтесь использовать no для сокращенного обозначения Норвегии.
Listing 3
Безопасность
Вы должны понимать то, что если конфигурационный файл используется для хранения важных данных, например паролей, необходимо позаботиться о том, чтобы содержимое такого файла не попало в web-браузер. Простейший выход из положения заключается в том, чтобы хранить конфигурационные файлы вне корневой директории сайта, например здесь: /etc/myApp/config
Если этого сделать нельзя, в таком случае можно изменить расширение файла. Для конфигурационного файла в формате модуля php необходимо всегда выбирать расширение .php. В этом случае сервер проанализирует php-файл, а пользователь увидит пустую страницу. С ini-файлами такое не пройдет, однако сервер Apache предоставляет возможность защитить данные. Просто поместите в каталог, где хранятся ini файл с именем .htaccess В него нужно поместить следующие строки:
Теперь сервер перестанет выдавать файлы с расширением ini, а опции приложения будут скрыты от пользователей.
Другие средства
Кончено же вы не являетесь единственным разработчиком, который сталкивается с проблемой обеспечения гибкости настроек веб-приложения. Поэтому некоторые программисты уже разработали библиотеки классов, которые переводят работу с конфигурационными файлами на абстрактный уровень, а так же упрощают запись и чтение различных форматов конфигурационных файлов.
PEAR::Config
Одним из классов, который может пригодится при чтении и записи конфигурационных файлов является PEAR::Config [3]. Как и все классы PEAR, PEAR::Config инсталлируется при помощи PEAR-Installer по команде
Этот класс является многоформатным, поскольку работает с конфигурационными файлами в форматах XMIL, ini, Apach-Style (гибрид XML и ini), а также php-массивами. Достоинством данного класса является то, что API для взаимодействия со всеми форматами одинаков. Т.е. логика работы с конфигурационными файлами в формате XML ничем не отличается от логики работы с ini-файлами. Вследствие этого необходимо, чтобы все форматы имели одинаковую структуру. Конфигурационные файлы, с которыми работает PEAR::Config, состоят, как и ini-файлы из секций.
Изменим снова наш пример. Сначала мы создаем объект Config, а затем вызываем его метод parseConfig(). Поскольку метод позволяет считывать различные форматы файлов, при вызове его необходимо передавать параметр, уточняющий формат. Для конфигурационных файлов в формате ini в качестве такого параметра используется строка iniFile. После считывания файла, мы не получаем опции в виде массива, вместо этого создается объект-контейнер, который дает доступ ко всем настройкам. Хотя во многих случаях бывает желательно получить опции в форме массива. Для этого используется метод toArray(). Листинг 4 демонстрирует считывание ini-файла:
Listing 4
С первого взгляда это может показаться несколько запутанным. Однако преимущество данного подхода заключается в том, что один и тот же метод используется для чтения всех форматов файлов, поддерживаемых PEAR::Config. Измененные опции могут быть также сохранены в любом формате:
Листинг 5 содержит код, где серия опций помещается в массив, который затем сохраняется в формате XML. Если вы хотите побольше узнать о PEAR::Config необходимую информацию вы сможете найти в документации по PEAR[5] или в DevShed-Tutorial [6].
Listing 5
patConfiguration
Альтернативным классом для работы с конфигурационными файлами является patConfiguration[7], однако он предназначен исключительно для работы с файлами в формате XML. После скачивания архива, его необходимо распаковать. Сам класс находится в директории include. patConfiguration предварительно определяет Tag-Set, который затем наполняется данными. К тому же этот класс предоставляет возможность указать тип опции: целое число, число с плавающей точкой, булевское значение. Типичный конфигурационный файл, созданный patConfiguration, имеет следующую структуру:
После создания объекта класса, может быть вызван метод parseConfigFile(). Доступ к опциям осуществляется через getConfigValue(). В качестве параметра этот метод может принимать путь к нужной опции. Вернемся к нашему примеру. Допустим, мы хотим получить e-mail адрес, на который высылается сообщение об ошибке. В этом случае используется путь errors.email. Если путь не указан, тогда все параметры передаются в массив. Листинг 6 демонстрирует код, который можно использовать для считывания файлов.
patConfiguration 2.0.0
В данный момент многоформатная версия patConfiguration находится в стадии разработки. Возможно, при публикации статьи эта версия уже станет доступной. Впрочем, самую новую версию для разработчиков вы можете скачать с сайта snaps.php-tools.net/downloaden.
В этом примере вы уже заметили, что внутри тега указывается тип значения. Названия типов идентичны тем, что используются в php-функции settype(). Если тип не указан, тогда значение интерпретируется как строка. Для часто используемых опций можно определить отдельный тег.
Наряду с функцией getConfigValue, существует функция setConfigValue(), с помощью которой можно изменить значение опции. Затем конфигурационный файл может быть заново записан с помощью writeConfigFile() (см листинг 7).
patConfiguration предлагает также серию дополнительных возможностей. Например, наряду с тегами, существует возможность определять атрибуты и пространства имен (Namespace), а к тегу можно привязать внешний файл, таким образом, опции будут распределены по нескольким файлам. Кроме этого patConfiguration включает систему кэширования, благодаря которой пропадает необходимость в многократном считывании конфигурационного файла.
Дополнительную информацию вы сможете найти на PHP Application Tools-Homepage и в patConfiguration-Tutorial на DevShed [8].
Listing 7
Заключение
Забота о гибкости настроек приложения может сберечь много времени, особенно если его компоненты предполагается использовать в других проектах. Вы потратите еще меньше времени, если доверите работу с конфигурационными файлами одному из готовых классов. Выбор между PEAR::Config и patConfiguration зависит от задачи. Преимуществом PEAR::Config является поддержка различных форматов конфигурационных файлов, в то время как patConfiguration прекрасно работает с XML, так же предоставляет ряд дополнительных возможностей. Однако с появлением версии 2.0.0 этот пакет будет иметь одинаковый API для считывания ini и wddx файлов. PHP-массив поддерживаются уже в текущей версии.
Всё что вы когда-либо хотели знать про mysql, php и кодировки, но боялись спросить! Почему кириллица на сайте отображается вопросами? Как правильно настроить сервер mysql для работы с кириллицей? Как поменять кодировку в mysql? Как изменить кодировку в скриптах php? Какую выбрать кодировку? Как сконвертировать базу данных из одной кодировки в другую? Эти и многие подобные вопросы с завидным упорством снова и снова поднимаются на различных форумах уже который год. В этом посте я постарался рассказать что нужно делать чтобы такие проблемы не возникали и дать наиболее эффективные советы на тот случай если они все-таки возникнут.
Mysql, php и кодировки. Источник проблем.
Проблемы с кодировками в Mysql обусловлены историей создания этой программы. Так как разрабатывали mysql - европейцы - для них было естественно выбрать в качестве основной кодировки более удобную для себя latin1. Странно, но и по сей день большинство инсталляций Mysql по умолчанию работают с этой кодировкой что и создает для пользователей кириллицы проблемы с добавлением в базу данных строк на русском и украинском языках - в latin1 эти символы просто отсутствуют.
Поэтому первое что нужно сделать при возникновении проблем с кодировками в mysql - нужно проверить какая кодировка является для данной инсталляции mysql основной. Проверить это можно несколькими способами.
Настройка сервера mysql для нужной кодировки.
* Если вы админ сервера или вы самостоятельно настраиваете собственную mysql на рабочей машине.
Откройте файл конфигурации mysql.ini (/etc/mysql.cnf для os linux) и найдите такие строки.
Вместо “название_кодировки” нужно подставить название той кодировки, которую вы будете использовать. Для текстов на русском и украинском языках можно использовать utf8 или cp1251 (обратите внимание - названия кодировок в mysql пишутся без обычного дефиса!!!). Но я советовал бы использовать только utf8 - так вы себе сэкономите в будущем немало нервов.
Если такие строки в файле конфигурации отсутствуют, то это означает что база данных использует по умолчанию ту кодировку, которая была задана при компиляции. Добавьте в конфиг нужные вам настройки кодировок (примеры ниже) и перезапустите mysql.
Если у вас возникли проблемы с кодировкой на хостинге, где вы не имеете прав администратора, то проверить настройки кодировки для mysql вы сможете другим способом: установите соединение с mysql (при помощи консольной команды mysql или phpmyadmin - как вам удобнее) и выполните такой sql-запрос: show variables like ‘char%’. Этот запрос покажет вам значения переменных mysql, которые имеют отношение к кодировкам. Скорее всего, вы увидите что-то вроде такого
Я специально привел выше пример НЕПРАВИЛЬНО НАСТРОЕННОГО СЕРВЕРА!!! Обратите внимание - в нем используются в разных случаях три(!) разные кодировки. Начинающему веб-программисту в такой ситуации будет сложно добиться корректной работы скрипта. Старайтесь чтобы все переменные были настроены на работу с одной и той же кодировкой. Тогда 99% проблем которые обсуждаются на форумах у вас просто не возникнут. Тут даже не столь важно какую именно кодировку вы выберете - главное чтобы она была везде одинаковой. Но все-таки старайтесь указывать в настройках ту кодировку, которую действительно будете использовать для хранения данных.
Итак, удачный вариант - это если команда show variables like ‘char%’ из абзаца выше покажет вам список одинаковых кодировок для каждой из переменных и еще лучше будет если эта кодировка совпадет с той которую используете вы.
Если же кодировка mysql отличается от вашей - не спешите расстраиваться. Изменить любую из этих переменных вы можете либо глобально, для всех правкой конфигов (если вы администратор сервера), либо только для себя - sql-запросом set character_set_database=utf8 (если вы пользователь). Такой запрос должен будет выполняться из вашего php скрипта сразу после установки соединения с сервером mysql. Ниже пример для установки кодировки utf8 из php скрипта.
Что касается character_set_database - постарайтесь сразу создать базу данных в нужной кодировке (как вариант - отправьте такую просьбу в техподдержку хостинга), тогда вы избежите по крайней мере одного лишнего запроса к mysql во время работы скрипта. Если удастся,то строчку с ‘character_set_database’ из приведенного выше кода можно будет удалить.
Примеры настроек сервера mysql для правильной работы с кодировками.
При правильно настроенном сервере делать запросы из скрипта для установки правильной кодировки уже будет не нужно.
Настройки для utf8
Проверка реальной кодировки в которой хранятся базы данных mysql.
В случае если вы все (и сервер, и php скрипт) настроили правильно, по инструкции выше, но русские буквы все равно не отображаются - проверьте действительно ли ваши строки сохранены в той кодировке, которую вы указали в настройках!!!
Простой способ проверки - сделайте дамп базы данных в sql-формате и откройте его в текстовом редакторе. Sql-формат - это обычный текст. Если ваша база данных mysql в кодировке cp1251 - открывайте в Блокноте. Если utf8 - в любом редакторе с поддержкой Юникода. Пролистайте файл и убедитесь что все надписи с кириллицей нормально читаются и что sql-команды create table и create database, которые встречаются в дампе содержат правильные названия кодировки mysql (той кодировки, которая вами была указана в настройках сервера или в запросах из php-скриптов.
Если кодировка не подходит - сделайте бекап базы на всякий случай, перекодируйте sql-дамп в любом текстовом перекодировщике, замените названия кодировок в файле на правильные и заливайте полученный файл на сервер mysql. Теперь с кодировками все должно быть в порядке.
Какие жесткие диски лучше установить на компьютере с windows 2000/XP: ata (иначе ide) или scsi? Спор о сравнительных достоинствах и недостатках дисков ata и scsi – один из самых давних в отрасли. В одной из статей я уже сравнивал технические характеристики различных вариантов этих технологий и рассказывал, как их использовать в системах на базе windows nt.
За последнее время появились новые реализации интерфейсов ata и scsi. Сфера scsi расширилась и теперь охватывает ultra2 scsi, волоконно-оптический канал, ultra160 scsi и новейший стандарт – ultra320 scsi. Максимальная пропускная способность этих устройств составляет 80, 100, 160 и 320 Мбайт/с, соответственно. Однако высокая скорость всегда была достоинством scsi, поэтому более важным событием стало сокращение ценового разрыва между технологией scsi и ее конкурентами.
ATA догоняет
Последние стандарты ata 66 (или ultra dma/66, или udma/66) и ata 100 (или ultra dma/100, или udma/100) обеспечивают быструю передачу данных в пакетном и непрерывном режимах (66 и 100 Мбайт/с, соответственно). Планка производительности ata поднимется еще выше с появлением в 2002 г. стандарта se-rialata (первые устройства будут обеспечивать скорость передачи данных 150 Мбайт/с, а в дальнейшем – до 300 и даже 600 Мбайт/с). Таким образом, ata уже годится не только для пользовательских систем и корпоративных настольных компьютеров начального уровня, но и для машин, к дисковой подсистеме которых предъявляются повышенные требования.
Реально на офисных однодисковых системах обычно не удается достигнуть максимального быстродействия. Системные ограничения (например, возможности микросхем ata, архитектура системной шины, физические ограничения диска) часто снижают скорость пересылки данных. Тем не менее, в основном из-за дороговизны scsi (которая объясняется высокой стоимостью контроллера и диска), ata преобладает везде, кроме настольных рабочих станций самого высокого уровня. Однако, чтобы добиться максимальной производительности дисков ata на компьютерах win-dows 2000, недостаточно просто установить новые накопители и подключить кабели.
Стараясь идти в ногу с технологией ata, разработчики microsoft дополнили windows 2000 новыми возможностями и уделяют ata больше внимания при подготовке различных пакетов исправления и программных заплаток. Чтобы эффективно использовать устройства ata на компьютерах windows 2000, требуется иметь базовые знания об интерфейсе ata, необходимых аппаратных средствах и программном обеспечении (например, пакетах исправления и заплатках windows 2000, встроенных драйверах и драйверах независимых поставщиков).
Аппаратные средства
Во-первых, в системе должен быть установлен контроллер, который поддерживает скоростные режимы ata. Самые распространенные стандарты современных дисков – ata/33 (ultra dma/33 или udma/33), ata/66 и ata/100. Практически все контроллеры ata обратно совместимы с дисками прежних стандартов. Например, контроллер ata/100 обычно совместим с дисками ata/33 и даже старыми стандартами ide и eide.
В большинстве систем контроллер реализован в микросхемах ata на системной плате (львиная доля рынка микросхем ata принадлежит компании intel, но есть и другие поставщики, такие, как viahardware.com). Однако в некоторых случаях контроллер может быть размещен на плате расширения pci, например в raid-контроллере ata.
От набора микросхем (важнейшего компонента системной конфигурации ata) и его драйверов зависят функциональные возможности дисков и других устройств, подключенных к контроллеру. Поэтому в первую очередь необходимо тщательно изучить набор микросхем на системной или вспомогательной плате и определить его возможности. Эту информацию можно получить у поставщика ком-пьютера, с системной платы или платы контроллера.
Если микросхемы ata расположены на системной плате, необходимо убедиться, что bios системы поддерживает нужные режимы ata. По всей вероятности, конкретный режим ata реализован в наборе микросхем, но он может отсутствовать в редакции bios, регулярно обновляемой поставщиками ПК и системных микросхем. В этом случае новую версию bios можно получить на web-сайте изготовителя системной платы или компьютера.
Затем следует убедиться, что аппаратные средства обеспечивают нужный режим ata и настроены на оптимальную производительность. Во-первых, все жесткие диски должны поддерживать необходимые режимы ata (например, ata/66, ata/100). Во-вторых, важно распределить диски по отдельным каналам, так как по умолчанию канал ata работает со скоростью самого медленного диска. Если диски ata/33 и ata/100 установлены в одном канале, то скорость передачи данных будет определяться быстродействием ata/33. Поэтому следует разместить медленные устройства ata (например, устройства cd-rom, cd-r, cd-rw, zip, старые жесткие диски) на одном канале, а скоростные жесткие диски – на другом.
Кроме того, необходимо верно выбрать кабели. В спецификациях ata/33, ata/66 и ata/100 указывается, что устройства следует подключать через специальный 80-жильный ленточный кабель, а не 40-жильные кабели, применявшиеся в прежних дисках ata. Дополнительные жилы кабеля нужны для заземления и увеличивают соотношение сигнал/шум при передаче данных. И наконец, накопители следует подключать к 80-жильному кабелю иначе, чем к прежним 40-жильным кабелям. Главное устройство (drive 0) необходимо разместить на конце 80-жильного кабеля, а вторичный накопитель (drive 1) нужно подключить к среднему разъему. Синий разъем на одном конце предназначен для системной платы или платы контроллера, серый разъем в середине – для вторичного устройства, а черный разъем на другом конце – для главного устройства.
Эта статья является продолжением статьи о создании счетчика просмотров для каждой страницы сайта на php и MySQL (если Вы ее не читали, то обязательно прочтите, иначе ничего не поймете из ниже сказанного). В этой статья я решил продолжить тему и расширить возможности счетчика просмотров страниц.
Для увеличения возможностей и получения статистики просмотров страниц сайта к базовому php скрипту необходимо добавить несколько строк и своих функций. В частности нужно будет создать еще одну таблицу, которая имеет следующую структуру:
Как видно из структуры MySQL таблицы, она состоит всего из двух полей (page_id – хэш сумма md5() от urla страницы и page_url – url страницы) и индекса, установленного на поле page_id – для значительного ускорения поиска значения в таблице. И еще, я не стал изменять изначальную таблицу my_log, которая использовалась для подсчета количества просмотров страниц, а создал другую по одной простой, но очень весомой причине: чем больше данных в таблице – тем медленнее осуществляется поиск по таблице. А скорость работы php скриптов такого уровня не должна ощутимо влиять на работу сайта в целом. Ведь если у вас коммерческий и при этом очень посещаемый сайт, то тратить строго ограниченное процессорное время на второстепенные задачи просто невыгодно, ведь зачастую прибыль зависит от того, сколько человек сможет увидеть ваш сайт.
Теперь перейдем непосредственно к коду php скрипта. Я внес в него незначительные изменения, в основном это новые функции для работы с MySQL таблицей my_log_urls.
В counter.php внес следующие изменения:
добавляем функцию Default_Write_URL
В результате, получаем значительную экономию времени т.к. делаем всего одну запись в таблицу my_log_urls и одну в my_log, и при следующих запросах этой же страницы запрос к таблице my_log_urls выполняться не будет, т.к. запись уже существует в таблице my_log, следовательно и в таблице my_log_urls она то же есть.
Для подсчета рейтинга страниц сайта, предлагаю написать другой php скрипт, который будет по значениям просмотров страницы в таблице my_log брать значения в таблице my_log_urls. А результат представлять в виде таблицы с данными о просмотрах страниц, отсортированными по убыванию (от большего значения к меньшему).
Ниже приведен код php скрипта, который необходимо скопировать в созданный вами файл top.php:
Данный php скрипт выводит 10 самых популярных страниц вашего сайта за последние сутки и за все время. В принципе, можно осуществлять вывод и большего числа страниц, изменив в php функциях MySQLReadAll и MySQLReadToday лимит считываемых из таблицы записей. А так же можно вместо самых популярных страниц увидеть самые непопулярные, изменив способ сортировки в этих же функциях с DESC на ASC.
Скачать данный php скрипт, вместе с модифицированным php скриптом подсчета просмотров страниц, можно по этой ссылке.
bluetooth - это технология, которая призвана заменить соединение сотового телефона, мобильного компьютера и других периферийных устройств между собой с помощью проводов, на более удобное соединение по радио каналу.
Немного истории.
Вообще-то, bluetooth дословно переводится как "Голубой зуб". Так прозвали когда-то короля викингов Харальда, жившего в Дании около тысячи лет назад. Прозвище это король получил за темный передний зуб.
Король Харальд вошел в историю как человек, объединивший Данию и принесший им христианство. Таким образом, именем исторической личности был назван протокол, который, по замыслу его создателей, так же должен творить историю.
Как все начиналось.
Где-то в начале 1998 года сразу несколько гигантов компьютерного и телекоммуникационного рынка, такие как ericsson, nokia, intel, ibm, toshiba, объединились с целью создания технологии беспроводного соединения между мобильными устройствами и периферийной техникой.
20 мая миру была представлена специальная рабочая группа (sig, special interest group), в задачу которой как раз и входило создать и запустить такую систему, получившую название bluetooth.
Очень быстро к Группе присоединились такие компании, как, например, motorola, dell, compaq, xircom и многие, многие другие.
Создаётся форум bluetooth, в который входит более 1300 компаний (Полный список участников группы bluetooth доступен по адресу http://www.bluetooth.com/).
Компания ericsson впервые создаёт аппарат, реально подтверждающий, что bluetooth работает, развивается и будет продолжать свое развитие.
Речь идет об уже упоминавшемся комплекте беспроводной связи с сотовым телефоном, состоящем из наушника и микрофона. Этот комплект способен работать на расстоянии до 10 метров от базы, которой, в данном случае, и является сотовый телефон со встроенной платой bluetooth.
Кстати, в скором времени компания собирается увеличить дальность работы комплекта в несколько раз:
При этом эти два аппарата могут не находиться в прямой видимости друг друга. Чтобы ответить на звонок, вам достаточно нажать на кнопку микрофона, а при наборе номера вам на помощь придет функция голосового вызова. Растущая словно снежный ком популярность bluetooth объясняется так же и его общедоступностью. Во-первых, использование частоты 2,44 ГГц. не требует лицензирования, да и распространение других лицензий на работу с bluetooth будет производиться за символическую плату. Во-вторых, помимо общедоступности, данная технология обещает стать и общепринятой, то есть стандартом де-факто, так как в ближайшее время мировое промышленное сообщество примет технологию как глобальный стандарт. Такое единство вызвано тем, что гораздо проще и дешевле снабжать все устройства одинаковым стандартным чипом, нежели разрабатывать компьютеры под разные интерфейс-карты.
Еще один аргумент за - это дешевизна сетевых адаптеров, которые будут встроены буквально во все - в средства связи, в бытовые приборы, в компьютеры, в другую оргтехнику.
Перспективы.
Они у bluetooth огромны. Эта технология, как ожидается, будет (и есть) совместима с очень многими протоколами и аналогичными системами. gsm, tcp, ip и так далее. Кроме того, есть надежды на относительно низкую стоимость, ведь технология уже изначально задумывалась как общедоступная. Пользователю будет гарантирована высокая степень защиты и отличное качество работы.
Согласно прогнозу компании international data corporation к 2004 в мире будет насчитываться 448.9 млн. устройств, поддерживающих этот стандарт. Одними из первых поддержат стандарт мобильные телефоны. Следом идут принтеры - они начнут поддержку bluetooth в 2001-2002 годах. А к 2004 году 19% всех цифровых камер будут поддерживать bluetooth.
Уже начались поставки инструментального набора от компании ericsson, для разработчиков приложений. И наконец, сегодня (точнее, совсем недавно) компания ericsson, один из главных "родителей" технологии, выпустила телефон, действительно работающий с технологией bluetooth. Это - ericsson t36. Данный сотовый телефон вообще можно назвать образцовым. В нем объединено все лучшее, чего достигла сотовая связь на сегодняшний день. Так, t36 является телефоном трехдиапазонным, у него есть wap-броузер, он поддерживает технологию high speed data, что дает возможность организовать более быстрое соединение, чем стандарт gsm, огромное количество функций, делающих его не просто телефоном, а полноценным помощником в бизнесе, и конечно, он поддерживает технологию bluetooth.
Так же компания выпустила аналогичный по своим возможностям Т36 телефон ericsson r520. Основное отличие - это, безусловно, дизайн - Т36 продолжает линейку таких телефонов, как Т10, Т18, Т28, а r520 можно с уверенностью назвать более совершенным вариантом r320. Трехдиапазонность Т36 позволяет ему работать практически в любой точке земного шара. Но, конечно, диапазон 1900 работает только в Америке.
Но не только ericsson выпускает bluetooth - компоненты. На том же cebit компания toshiba продемонстрировала устройство, использующее bluetooth и видеостандарт mpeg-4 для проведения видеоконференции - изображение с камеры передавалось на компьютер и затем на еще один компьютер.
А, например, компания anoto (http://www.anoto.com/) совместно со все той же компанией ericsson, разработала авторучку, позволяющую передавать сделанные ей записи по мобильной связи.
Конечно, писать придется не на простой бумаге, а на специальной, состоящей из множества точек, которые и различает находящаяся внутри ручки миниатюрная видеокамера.
nec заявила о своем намерении начать в середине этого года выпуск ноутбуков со встроенными чипами bluetooth. Новые модели будут иметь возможность выводить данные на принтеры и другие периферийные устройства, используя беспроводное соединение. Коммуникационный модуль bluetooth, используемый nec, построен на rf-чипе от national semiconductor corp.
ibm сообщила, что сейчас она ведет разработку bluetooth-модема для органайзеров типа palm, в том числе и для своего palm-совместимого карманного компьютера workpad. Как сообщается, эта модемная карта bluetooth pc card будет стоить не дороже 200 дол.
Следует отметить, что осенью этого года компании acer neweb и widcomm собираются выпустить свой bluetooth-модем, который должен составить конкуренцию модему от ibm. Это тоже будет карта стандарта bluetooth pc card, называться она будет bluecard, а стоить - около 100 дол.
На некоторых сайтах часто можно увидеть следующую надпись внизу страницы или под статьями: "Всего просмотров xxx. Сегодня xx". На первый взгляд ничего особенного, но все равно, многим интересно, как это сделано.
В этой статье я попробую рассказать вам о том, как устроена данная статистика просмотров страниц сайта, на самом простом примере, написанном на php. Статистика просмотров страниц будет работать на связке MySQL + PHP. Основным отличием этой статистики от других будет то, что MySQL таблица будет занимать очень мало места, но при этом нельзя будет точно сказать какую именно страницу и сколько раз просмотрели. А все из-за того, что все url будут хешированны с помощью php функции md5(), что гарантирует почти 100% неповторяющихся id для каждой страницы сайта. Делается это только для ускорения работы php скрипта (при условии, что индексом является id страницы) и уменьшения размеров MySQL таблицы (за счет отсутствия длинных url).
MySQL таблица будет иметь следующую структуру:
page_id – уникальный id для каждой страницы сайта сгенерированный php функцией md5().
all – значение всех просмотров данной страницы.
today – просмотров страницы сегодня.
date – дата возвращаемая php функцией time() + 24 часа
Для правильного учета посещений страниц значение поля date будет изменяться, тогда, когда текущая дата будет больше той, что указанна в таблице. В этот же момент будет происходить и обнуление счетчика просмотров страницы за прошедшие сутки.
Почти весь php скрипт статистики просмотров для каждой страницы сайта состоит в основном из функций, которые выполняют строго определенную роль. Все функции снабжены комментариями, поэтому, надеюсь, все поймете сами.
PHP код скрипта статистики просмотров страниц сайта:
Вот в принципе и весь php скрипт статистики просмотров страниц сайта. Для того, что бы он работал, его нужно "подключить" к нужному вам скрипту, например к index.php, добавив в index.php строчку include(' counter.php ');. А в том месте, где должно выводиться сообщение о том, сколько человек просмотрело данную страницу – строчку echo Today_and_all_counter;.
Скачать данный php скрипт статистики просмотров страниц сайта и MySQL файл со структурой таблицы можно здесь
В этом разделе вы создадите два новых приложения OLE. Первое - простая программа-сервер OLE, второе - пример простого контейнера OLE. Эти программы предназначены для демонстрации минимальных затрат программирования, необходимых для создания приложений OLE 2.
В любом случае, для создания оболочки программы следует воспользоваться приложением AppExpert. Сначала необходимо сгенерировать основу приложения в AppExpert, затем модифицировать созданные файлы для создания законченного рабочего примера.
При написании своих версий этих программ необходимо иметь в виду несколько моментов. Во-первых, в этой главе приводятся листинги только исходных, немодифицированных файлов.
Во-вторых, CLSID этих программ будет отличаться от CLSID программ, которые вы сгенерируете с помощью AppExpert. Это нормально и даже необходимо, поскольку с помощью CLSID одни серверные приложения в Windows отличаются от других.
В-третьих, эти примеры содержат минимум необходимых средств для того, чтобы начать программировать с OLE. Вы можете использовать эти примеры в качестве начального кода для создания своего действительно полезного сервера или контейнера. В этой главе просто не хватает места для описания реализации функциональных сервера и контейнера - в этом случае вам понадобился бы грузоподъемник, чтобы положить эту книгу на стол.
Создание сервера OLE
Первое приложение OLE в этой главе - сервер. В этом примере вы построите полный сервер - сервер, который может использоваться и как автономное приложение, и как сервер. Создавая автономный сервер (т.е. в виде исполняемой программы .ЕХЕ, а не в виде динамически подключаемой библиотеки DLL), вы упрощаете процесс регистрации сервера в Windows.
Начальный процесс разработки сервера прост. Сначала из интегрированной среды Borland C++ версии 4.5 запустите AppExpert. Задайте каталог и имя вашего проекта. Я поместил свой проект в каталог \BC45\SOURCE\OLESVR. Проект я назвал OLESVR (я всегда называю проекты и каталоги проектов одним и тем же именем, это облегчает запоминание). Ниже приводится последовательность действий, в результате которых был создан проект OLESVR.
Запустите AppExpert. В первом диалоговом окне следует задать имя и каталог проекта. Как уже отмечалось, я использовал OLESVR для задания обоих.
После выбора ОК в диалоговом окне имени и каталога проекта следующий раздел АррЕхреrt - диалоговое окно Application General Options (основные опции приложения). Это диалоговое окно позволяет задать конфигурацию приложения, генерируемого AppExpert. Вам придется модифицировать несколько опций для проекта OLESVR.
Первая опция, которую необходимо изменить, находится в блоке Application: Summary. Замените параметр по умолчанию Multiple document interface на Single document interface. Это изменение согласуется с призывом Microsoft делать ставку на однодокументные приложения для Windows. На рис. 21.1 демонстрируется модифицированный блок Application: Summary.
Второе изменение, которое необходимо внести, - указать AppExpert, что ваша программа будет сервером OLE. Это изменение вносится в пункт Application: OLE 2 Options, имеющий ряд опций OLE 2, которые можно задавать. Поскольку вы создаете сервер OLE, вы будете оперировать только элементами блока группы OLE 2 Server: (поищите его в правой верхней части диалога). Выберите кнопку ячейки пометки Server EXE. На рис. 21.2 демонстрируются изменения, проведенные в пункте Application:OLE 2 Options.
При желании вы можете заполнить элементы пункта Application: Admin Options блока диалога AppExpert. С его помощью вы можете задать в приложении заметку об авторском праве, имя и информацию о версии. Все элементы в Application: Admin Options необязательны, и вы можете их не задавать.
Подпункты пункта Main Window не нуждаются в модификациях, их следует оставить заданными значениями по умолчанию. Для данного приложения нет необходимости менять что-либо в этих подпунктах. Пункт MDI Child/View неприменим для этого проекта, поэтому нет нужды в нем что-нибудь менять.
После задания всех необходимых модификаций следует выбрать кнопку Generate в нижней части блока диалога AppExpert Application General Options. AppExpert запросит у вас подтверждение, действительно ли вы собираетесь создать проект; после принятия подтверждения AppExpert сгенерирует приложение. На рис. 21.3 приводится конечный проект, загруженный в интегрированную среду Borland C++ версии 4.5.
Теперь, когда программа сгенерирована, в нее следует добавить код, задающий функциональность сервера OLE. Необходимо включить код, рисующий изображение, а также провести другие незначительные изменения.
К счастью, помимо Borland C++ версии 4.5 можно воспользоваться программой ClassExpert, что облегчит внесение большей части изменений. Предположим, вы хотите сперва заняться вопросами отображения. Как и в любой созданной с помощью AppExpert программе, основная часть рисования выполняется классом отображения, производным от класса OWL TOleView. Файл, в котором содержится реализация отображения, имеет имя LSVROLVW.CPP. В листинге 21.1 приводится первоначальный файл OSROLVW.CPP.
Листинг (файл реализации класса отображения OLESVR, OSVROLVW.CPP)
Давайте определимся с номенклатурой. Поле - это ячейка в таблице. Запись - набор из полей. Так вот, существуют разные типы полей - обычные, индексные и ключевые. Обычные поля - просто данные. Индексное поле - поле, по которому данные сортируются. Ключевое - поле, значение которого уникально. В общем-то четкого разделения нет. Ведь программа базы данных может сортировать таблицу и по обычным полям, а индексное поле может быть также уникальным.
В BDE достаточно логично поределена структура построения полей и их типов. Для этого имеются классы TxxxDef (три икса здесь обозначают подстановку, а то мало ли что Вы подумаете ;)), произведенные ото абстрактного базового класса TNamedItem. В компоненте TTable имеются и соответствующие свойства
TIndexDefs
TFieldDefs
Как и следовало предполагать, эти свойства содержат в себе определения индексных и обычных полей. Здесь нет свойства типа TKeyDefs, потому что в таблицах типа Парадокс индексные поля могут быть сами по себе уникальны. За счет этих свойств и задаются параметры таблицы, ее сетка. Создание таблицы довершает метод CreateTable.
Теории было много, теперь практика. Вот пример, честно скажу, взятый их Хелпа и перекомментированный автором (то есть мной).
Это все еще объяснять и объяснять. Но, я надеюсь, общая логика понятна.
В состав библиотеки MFC входит ряд классов, представляющих стандартные диалоговые панели. Эти классы позволяют легко реализовать такие часто используемые операции, как открытие и сохранение файла, выбор цвета, выбор шрифта и т.д. Все эти классы наследуются от CCommonDialog, который в свою очередь является производным по отношению к базовому классу CDialog.
Приведем классы стандартных диалоговых панелей и их назначение:
CColorDialog - Панель для выбора цвета
CFileDialog - Панель выбора файлов для открытия и сохранения на диске
CFindReplaceDialog - Панель для выполнения операции поиска и замены
CFontDialog - Панель для выбора шрифта
CPrintDialog - Панель для вывода документа на печать
CPageSetupDialog - Панель выбора формата документа
COleDialog - Панель для управления технологией OLE
Классы, управляющие стандартными диалоговыми панелями, определены в файле afxdlgs.h. Поэтому при использовании этих классов в приложении необходимо включить этот файл в исходный текст при помощи директивы #include.
Панель выбора цвета (класс CColorDialog)
Чтобы отобразить на экране стандартную диалоговую панель выбора цвета, надо создать объект класса CColorDialog, а затем вызвать метод DoModal. При создании объекта класса СColorDialog используется следующий конструктор:
Все параметры конструктора необязательны, однако в некоторых случаях использование этих параметров может помочь.
Первый параметр clrInit позволяет указать цвет, выбранный по умолчанию сразу после открытия диалоговой панели. Если параметр не будет указан, в качестве цвета, выбранного по умолчанию, будет использоваться черный цвет.
Параметр dwFlags содержит набор флагов, управляющих диалоговой панелью выбора цвета. При помощи него блокировать или разрешать работу некоторых элементов управления диалоговой панели выбора цвета. Если при создании объекта класса CColorDialog не указать параметр dwFlags, тем не менее можно выполнить настройку диалоговой панели, обратившись непосредственно к элементу m_cc данного класса. Параметр dwFlags, указанный в конструкторе, используется для инициализации m_cc. Изменения в элемент m_cc должны быть внесены до того, как панель будет отображаться на экране.
Последний параметр pParentWnd можно использовать, чтобы указать родительское окно диалоговой панели.
Методы класса CСolorDialog
Чтобы вывести диалоговую панель выбора цвета на экран, необходимо использовать метод DoModal. После отображения панели на экране пользователь может выбрать из нее цвет и нажать кнопки OK или Cancel для подтверждения выбора цвета или отказа от него. Когда диалоговая панель закрывается, метод DoModal возвращается значения IDOK и IDCANCEL, в зависимости от того, какую кнопку нажал пользователь:
На экране появится стандартная диалоговая панель выбора цвета Color. В верхней половине диалоговой панели расположены 48 прямоугольников, имеющих различные цвета. Они представляют так называемые основные цвета (Basic colors). Можно выбрать один из этих цветов и нажать кнопку OK. После того, как диалоговая панель закрыта (метод DoModal завершил свою работу), можно воспользоваться методами класса CColorDialog, чтобы узнать цвета, выбранные пользователем.
Для определения цвета, выбранного пользователем, можно обратиться к методу GetColor класса CColorDialog. Данный метод возвращает значение COLORREF, соответствующее выбранному цвету.
Если пользователю недостаточно основных цветов, представленных в диалоговой панели Color, он может выбрать до 16 дополнительных цветов. Для этого он должен нажать кнопку DefineCustom Colors. Диалоговая панель изменит свой внешний вид - появятся дополнительные органы управления, позволяющие выбрать любой из 16 777 216 цветов. Когда цвет выбран, нужно нажать кнопку Add Custom Colors. Выбранный цвет будет добавлен к дополнительным цветам (Custom colors) - один из свободных прямоугольников окрасится соответствующим цветом.
При помощи метода GetSavedCustomColors класса CColorDialog можно определить дополнительные цвета, выбранные пользователем в диалоговой панели Color. Этот метод возвращает указатель на массив из 16 элементов типа COLORREF. Каждый элемент массива описывает один дополнительный цвет.
Когда диалоговая панель Color отображается приложением первый раз, все прямоугольники, отображающие дополнительные цвета, имеют белый цвет. Дополнительные цвета, выбранные пользователем, сохраняются во время работы приложения. После перезапуска приложения дополнительные цвета сбрасываются.
Панель выбора файлов (класс CFileDialog)
Среди стандартных диалоговых панелей, для которых в библиотеке MFC создан специальный класс, есть панели для работы с файловой системой - Open и Save As. Диалоговая панель Open позволяет выбрать один или несколько файлов и открыть их для дальнейшего использования. Диалоговая панель Save As позволяет выбрать имя файла для записи в него документа.
Для управления диалоговыми панелями Open и Save As предназначен один класс CFileDialog. Рассмотрим конструктор класса CFileDialog более подробно:
Объекты класса CFileDialog представляют диалоговые панели Open или Save As в зависимости от параметра bOpenFileDialog. Если параметр bOpenFileDialog содержит значение TRUE, то создается объект, управляющий диалоговой панелью Open, а если FALSE - диалоговой панелью Save As.
Параметр bOpenFileDialog является единственным обязательным параметром, который необходимо указать. Остальные параметры конструктора класса CFileDialog задают различные режимы работы панели и могут не указываться.
Чтобы создать объект класса CFileDialog , представляющий диалоговую панель для открытия файлов (mFileOpen), и объект, представляющий диалоговую панель для сохранения файлов (mFileSaveAs), можно воспользоваться следующими вызовами конструктора класса:
Во многих случаях имена файлов, которые нужно открыть или закрыть, имеют определенное расширение. Параметр lpszDefExt позволяет задать расширение файлов, используемое по умолчанию. То есть, если пользователь при определении имени файла не укажет расширение, имени файла автоматически присваивается расширение, принятое по умолчанию. Если при определении свойств диалоговой панели программист присвоит параметру lpszDefExt значение NULL, то расширение файлов должно задаваться пользователем явно.
В некоторых случаях требуется, чтобы диалоговые панели отображались с уже выбранным именем файла. Чтобы указать имя файла, используемое по умолчанию, применяется параметр lpszFileName. Если параметр lpszFileName имеет значение NULL, данная возможность не реализуется.
С помощью флага dwFlags можно изменить внешний вид и некоторые другие характеристики стандартных диалоговых панелей класса CFileDialog. В него можно записать комбинацию флагов, управляющих различными характеристиками этих панелей. Например, флаг OFN_HIDEREADONLY означает, что из диалоговой панели удаляется переключатель "Read Only", а флаг OFN_OVERWRITEPROMPT (используемый для панели Save As) - что необходимо выводить диалоговую панель с предупреждением, если пользователь выбирает для сохранения имя уже существующего файла.
Диалоговые панели выбора файлов обычно имеют список так называемых фильтров, включающих названия типов файлов и расширения имен файлов данного типа. Выбрав фильтр, пользователь указывает, что он желает работать только с файлами определенного типа, имеющими соответствующее расширение. Файлы с другими расширениями в диалоговых панелях не отображаются.
Список фильтров можно указать через параметр lpszFilter. Одновременно можно указать несколько фильтров. Каждый фильтр задается двумя строками - строкой, содержащей имя фильтра, и строкой, в которой перечислены соответствующие ему расширения имен файлов. Если одному типу соответствует несколько расширений, они разделяются символом ;. Строка, содержащая имя фильтра, отделяется от строки с расширениями файлов символом |. Если используется несколько фильтров, то они также отделяются друг от друга символом |. Например, в качестве строки, задающей фильтры, можно использовать строку вида:
Диалоговые панели, представленные объектами класса CFileDialog, могут иметь или не иметь родительского окна. Чтобы указать родительское окно, нужно передать конструктору CFileDialog указатель на него через параметр pParentWnd.
Методы класса CFileDialog
Создание объекта класса CFileDialog еще не вызывает отображения соответствующей диалоговой панели. Для этого необходимо воспользоваться методом DoModal класса CFileDialog.При вызове метода DoModal для ранее созданного объекта класса CFileDialog на экране открывается соответствующая диалоговая панель. После того, как пользователь завершает работу с диалоговой панелью, метод DoModal вернет значение IDOK или IDCANCEL в случае успешного завершения и нуль - в случае возникновения ошибок:
После того, как пользователь закроет диалоговую панель и метод DoModal вернет управление, можно воспользоваться другими методами класса CFileDialog , чтобы определить имена выбранных файлов:
GetPathName - Определяет полный путь файла
GetFileName - Определяет имя выбранного файла
GetFileExt - Определяет расширение имени выбранного файла
GetFileTitle - Позволяет определить заголовок выбранного файла
GetNextPathName - Если диалоговая панель позволяет выбрать сразу несколько файлов, то этот метод можно использовать для определения полного пути следующего из выбранных файлов
GetReadOnlyPref - Позволяет узнать состояние атрибута "только для чтения" (read-only) выбранного файла
GetStartPosition - Возвращает положение первого элемента из списка имен файлов
Наиболее важный метод - GetPathName. Он получает полный путь файла, выбранного из диалоговых панелей Open или Save As. Если диалоговая панель позволяет выбрать сразу несколько файлов, тогда метод GetPathName возвращает массив строк, состоящий из нескольких строк, заканчивающихся двоичным нулем. Первая из данных строк содержит путь к каталогу, в котором расположены выбранные файлы, остальные строки содержат имена выбранных файлов. Выделение строки, содержащей путь к каталогу, проблем не вызывает, а чтобы получить имена выбранных файлов, необходимо воспользоваться методами GetStartPosition и GetNextPathName.
[pagebreak]
Метод GetStartPosition возвращает значение типа POSITION. Оно предназначено для передачи методу GetNextPathName и получения очередного имени выбранного файла. Если пользователь не выбрал ни одного файла, метод GetStartPosition возвращает значение NULL. Значение, полученное этим методом, следует записать во временную переменную типа POSITION и передать ссылку на нее методу GetNextPathName. Метод GetNextPathName вернет полный путь первого из выбранных в диалоговой панели файлов и изменит значение переменной pos, переданной методу по ссылке. Новое значение pos можно использовать для последующих вызовов метода GetNextPathName и получения путей всех остальных выбранных файлов. Когда метод GetNextPathName вернет имена всех выбранных файлов, в переменную pos записывается значение NULL.
В панелях Open и Save As имеется переключатель "ReadOnly". По умолчанию этот преключатель не отображается. Если есть необходимость воспользоваться этим переключателем, то нужно отказаться от использования флага OFN_HIDEREADONLY.
Метод GetReadOnlyPref позволяет определить положение переключателя "ReadOnly". Если переключатель включен, то метод GetReadOnlyPref возвращает ненулевое значение. В противном случае GetReadOnlyPref возвращает нуль.
Панель выбора шрифта (класс CFontDialog)
Стандартная диалоговая панель Font предназначена для выбора шрифта. Эта панель отображает список шрифтов, установленных в системе, и позволяет выбрать название шрифта, его начертание и другие параметры.
Для управления диалоговой панелью Font в библиотеку классов MFC включен класс CFontDialog. Методы этого класса можно использовать для отображения панели Font и определения характеристик шрифта, выбранного пользователем. Конструктор класса CFontDialog:
Все параметры конструктора являются необязательными. Настройка стандартной панели выбора шрифта, которая выполняется конструктором класса CFontDialog по умолчанию, удовлетворяет большинству пользователей.
Параметр lplfInitial является указателем на структуру LOGFONT, описывающую логический шрифт. Если этот параметр используется, то в диалоговой панели по умолчанию будет выбран шрифт, наиболее соответствующий шрифту, описанному в структуре LOGFONT.
Параметр dwFlags задает набор флагов, управляющий различными режимами работы панели. Например, флаг CF_EFFECTS позволяет пользователю создавать подчеркнутые и перечеркнутые буквы, определять цвет букв, а флаг CF_SCREENFONTS - разрешает выбирать только экранные шрифты.
Через параметр pdcPrinter можно передать конструктору контекст отображения принтера, шрифты которого будут представлены в диалоговой панели Font. Данный параметр используется только в том случае, если в параметре dwFlags указаны флаги CF_PRINTERFONTS или CF_BOTH.
Через параметр pParentWnd можно указать родительское окно для диалоговой панели Font.
Методы класса CFontDialog
Для отображения диалоговой панели Font предназначен виртуальный метод DoModal. Если пользователь выбрал шрифт и нажал кнопку OK, метод DoModal возвращает идентификатор IDOK, если пользователь отменил выбор шрифта, метод DoModal возвращает идентификатор IDCANCEL:
Остальные методы класса предназначены для определения характеристик выбранного пользователем шрифта.
Метод GetCurrentFont позволяет сразу определить все характеристики выбранного шрифта, записав их в структуру LOGFONT.
Остальные методы класса позволяют определить только отдельные характеристики выбранного шрифта:
GetFaceName - Возвращает имя выбранного шрифта
GetStyleName - Возвращает имя стиля выбранного шрифта
GetSize - Возвращает размер выбранного шрифта
GetColor - Возвращает цвет выбранного шрифта
GetWeight - Возвращает плотность выбранного шрифта
IsStrikeOut - Определяет, является ли шрифт выделенным перечеркнутой линией
IsUnderline - Определяет, является ли шрифт выделенным подчеркиванием
IsBold - Определяет, является ли шрифт жирным
IsItalic - Определяет, является ли шрифт наклонным
Панель для вывода документов на печать (класс CPrintDialog)
Класс CPrintDialog можно использовать для создания двух видов диалоговых панелей, предназначенных для печати документов и выбора форматов документов. Кроме класса CPrintDialog можно также использовать класс CPageSetupDialog. Он позволяет создать диалоговую панель для выбора формата документа, имеющую несколько иной вид.
В приложениях, подготовленных с использованием средств MFC AppWizard и построенные по модели документ-облик, по умолчанию встроена возможность вывода редактируемого документа на печать.
В меню File такого приложения находятся три строки (Print, Print Preview и Print Setup), которые управляют процессом печати документов, подготовленных в приложении. Чтобы распечатать документ, достаточно выбрать из меню File строку Print. На экране появится диалоговая панель Print. В ней можно выбрать печатающее устройство для печати документов (группа Name), указать, будет печататься весь документ либо его часть (группа Print range), а также сколько копий документа будет напечатано (группа Copies). Также можно настроить различные характеристики печатающего устройства, если нажать кнопку Properties в группе Printer.
Если требуется определить только печатающее устройство и формат документа, из меню File следует выбрать строку Printer Setup. В группе Printer можно указать печатающее устройство и настроить его соответствующим образом. Группа Paper задает формат бумаги и режим подачи бумаги в печатающее устройство. Группа Orientation включает только один переключатель, определяющий ориентацию бумаги. Он принимает положение Portrait для вертикальной ориентации изображения на бумаге (режим "портрет") или Landscape для горизонтальной ориентации изоборажения на бумаге (режим "ландшафт").
Строка Print Preview меню File выбирается для предварительного просмотра документа перед печатью. При этом главное окно приложения изменит свой внешний вид и можно будет просмотреть, как будет выглядеть документ после печати.
Если не требуется выполнять специфическую обработку документа перед печатью, то вряд ли понадобится самостоятельное добавление программного кода, отвечающего за процесс печати. Просто следует отметить, что процедура создания панелей, связанных с печатью документа, практически ничем не отличается от создания выше описанных стандартных диалоговых панелей.
Панель для выполнения поиска и замены (класс CFindReplaceDialog)
Класс CFindReplaceDialog предназначен для управления диалоговыми окнами Find и Replace. Диалоговая панель Find используется для поиска известных строк в документе приложения, а панель Replace позволяет замену одной строки на другую.
Важным отличием диалоговых панелей Find и Replace от других стандартных диалоговых панелей является то, что они представляют собой немодальные диалоговые панели. Поэтому процесс создания этих панелей значительно отличается от процесса создания стандартных панелей для выбора цвета, шрифта и имен файла.
Когда пишут про сокетное программирование, конечно же, подразумевается TCP/IP. Вот тут мы и отступим от правил, поговорим про IPX/SPX.
А все начинается как всегда, а именно, с инициализации WINSOCK библиотеки, обработка ошибок упускается для упрощения кода:
Ну и собственно сокет, тут я дам только кусок, отличный от нормальных сокетов:
В остальном, работа с SPX идентична работе TCP сокетов, все выше написанное справедливо и для IPX сокетов, только не забудьте, что последние нельзя законнектить. Открываются они следующим образом:
Передача данных происходит следующим образом:
Дальше я дам несколько, на мой взгляд, полезных вещей при работе с данными протоколами.
Приём заголовка пакета данных
В некоторых случаях нам нужен больший контроль над IPX/SPX пакетами, и для того, чтоб наше приложение могло управлять, изменять заголовок IPX/SPX, нужно вызвать следующий код:
А вот вам и структура заголовка SPX пакета, взято из WSIPX.H
В данном режиме Windows Sockets не будут сегментировать пакеты, ограничивая их размер до максимально допустимого протоколом.
Широковещательные пакеты
Широковещательные пакеты могут быть использованы, например, в качестве средства "принюхивания" клиента к серверу, это в случае, когда мы знаем порт нужного нам сервера, но не знаем его сетевого адресса.
Установка, изменение DataStreamType в заголовке SPX пакета
Это может быть использовано в собственных целях, например, для искусственной сегментации своих данных для совместимости разных реализаций протокола. Например, некоторые реализации протокола для DOS поддерживают максимальную длину пакета в 512 байт либо принудительно ограниченную сетевыми модулями, вот они и используют DataStreamType, чтобы указать последнюю порцию данных.
Устанавливается следующим образом:
Причём данную установку надо делать перед каждым send. Работает всё ОК, когда посылаются данные ДОС клиенту, ну а при приеме пакетов WIN клиентом от ДОС клиент DataStreamType не хочет устанавливатся, т.е. мы не получим установленное значение DataStreamType ДОС клиентом. Я обошел данную проблему при помощи следующего куска кода:
Данный метод хорош еще тем, что WIN клиент может принять один пакет вместо нескольких, посланных ДОС клиентом.
Другие специфические расширения для данных протоколов, используемые getsockopt/setsockopt, можно найти в файле wsnwlink.h, но, как упоминалось выше, данные расширения - для NT-платформ и могут не работать для других реализаций данных протоколов.
В этой статье приведу пример реализации древовидного меню на JavaScript с помощью PHP.
В своей практике много раз сталкивался с тем, что такое меню нужно, но человек затрудняется соединить скрипт на PHP с меню скриптом на JavaScript.
Для примера возьмём бесплатный скрипт для отображения древовидного меню Tigra Tree Menu, который поддерживается: MS IE, Netscape на платформах Windows 95/98/ME/2000/XP и Mac OS 8/9/X по адресу http://www.softcomplex.com/products/tigra_tree_menu/
Скачав и распаковав архив, мы увидим 3 яваскриптовых файла:
tree.js (сам скрипт отображения и формирования меню), tree_tpl.js(настройки самого меню и картинок к нему), а также
tree_items.js (сама структура меню), который нам и нужно заменить на PHP скрипт, который возьмёт наши данные из базы.
Создайте таблицу в базе данных:
id - идентификатор пункта меню;
pid - принадлежность к ветке меню (если, например, pid = 12, значит, этот пункт является подуровнем пункта, у которого id = 12);
name - название самого пункта меню.
Ну, и заполним немного данными для демонстрации:
Далее соединимся с базой данных и напишем рекурсивную функцию для формирования такой структуры, как в файле tree_items.js.
Пишем саму функцию:
Теперь в нужном нам месте выводим сформированное меню:
P.S. Для того, чтобы меню работало, не забудьте на странице, сразу после тега < body >, вставить нужные скрипты "Tigra Tree Menu":
Ну вот и всё. Надеюсь, что кому-нибудь это пригодится.
Очень часто при работе с запросами приходится менять SQL этого запроса. Например, при изменении порядка сортировки или при необходимости изменения фильтра, прописанного в where. Сделать это стандартными средствами можно, но довольно муторно, т.к. весь запрос хранится в одном месте (для TQuery и её потомков это свойство Sql). При желании изменить, например, количество или порядок следования полей в order by, нужно программно найти этот order by, написать свой, вставить его вместо старого и т.д. Для меня, честно говоря, загадка, зачем борланд пошла по такому ущербному пути: стандарт ANSI SQL-92, с которым (и только с которым!) работает Bde, подразумевает достаточно жёсткий синтаксис запроса, вполне допускающий обработку на уровне отдельных секций. Сегодня я хотел бы поделиться одним из вариантов реализации потомка TQuery, в котором задачи такого класса будут решаться на лету одной строчкой кода.
Смысл очень простой. Для того, чтобы уйти от ручной обработки текста sql-запроса, надо просто разбить его на стандартные секции. И менять их по отдельности. Ведь любой select-запрос имеет достаточно строгий синтаксис, состоя из определённого количества заранее известных секций (clauses), задаваемых в строго определённой последовательности. Рассмотрим этот синтаксис поподробнее на примере СУБД Interbase:
Как видим, обязательными являются две секции: SELECT и FROM.
Ещё восемь секций опциональны. Наша задача сводится к тому, чтобы значение каждой секции устанавливать отдельно, при необходимости переоткрывая запрос. Можно было бы плясать от стандартного свойства Sql, выделять нужную секцию, менять и вставлять обратно. Но зачем это, если можно сам Sql формировать на основе заданных секций? Конечно, этот подход имеет тот минус, что накрывается прямая установка Sql одной строкой, что может быть неудобно при хранении запроса в реестре, базе и т.д., но и это, при желании, можно побороть.
В общем-то, ничего заумного, реализация до смешного проста, но при использовании в проектах позволяет сэкономить массу времени и значительно увеличить читабельность кода.
Чтобы не писать отдельное свойство на каждую секцию, задавать их будем в виде массива строк. Для работы с этим массивом нам понадобятся индексы, которые тоже лучше определить заранее:
Определим тип нашего индексированного свойства и определим сам класс:
Свойство fClauses будет содержать все секции запроса, на основе которых и будет формироваться сам запрос. Занимается этим процедура UpdateSql. Ну а методы GetClause/SetClause стандартны, и служат для установки/чтения значений отдельных секций. Поглядим на сам код:
Всё достаточно прозрачно, отмечу лишь, что метод UpdateSql добавляет в текст Sql-запроса только те секции, для которых установлено начение, и переоткрывает квери, если она была открыта на момент изменения секции. Здесь есть мелкие недоработки, например, не проверяется выход индекса за пределы допустимых значений, я просто не хотел мусорить исходный код вещами, которые очевидны и принципиально не важны. Можно было бы привести код регистрации компонента в палире дельфи, но это также тривиально. Приведу лучше исходник тестового проекта, в котором используется этот квери. В этом проекте на форме находятся компоненты DbGrid1, подключенные к источнику данных DataSource1, динамически создаётся экземпляр TDynQuery, открывающий таблицу "biolife" из DbDemos, входящую в стандартную поставку Delphi. После этого изменяется по кликанью на заголовке (Title) грида меняется сортировка таблицы:
Все данные о регионе храняться в структуре RGNDATA. Упоминалась также и функция, позволяющая эту структуру получить: GetRegionData. У этой функции есть приятная особенность: если в третий параметр передать nil, то она вернёт размер памяти, необходимый для сохранения региона.
Аналогичным образом можно и прочитать записанный на диск регион:
Вот на этом, пожалуй, можно закончить этот обзор, отнюдь не претендующий на исчерпываемость.
Хочется надеятся, что кого-то сей опус подвигнет на создание чего-нить хорошего, или просто сэкономит несколько часов ползанья по Win32 SDK.
Для программирования расширенных хранимых процедур Microsoft предоставляет ODS (Open Data Service) API набор макросов и функций, используемых для построения серверных приложений позволяющих расширить функциональность MS SQL Server 2000.
Расширенные хранимые процедуры - это обычные функции написанные на С/C++ с применением ODS API и WIN32 API, оформленные в виде библиотеки динамической компоновки (dll) и призванные, как я уже говорил, расширять функциональность SQL сервера. ODS API предоставляет разработчику богатый набор функций позволяющих передавать данные клиенту, полученные от любых внешних источников данных (data source) в виде обычных наборов записей (record set). Так же, extended stored procedure может возвращать значения через переданный ей параметр (OUTPUT parametr).
Как работают расширенные хранимые процедуры.
* Когда клиентское приложение вызывает расширенную хранимую процедуру, запрос передаётся в TDS формате через сетевую библиотеку Net-Libraries и Open Data Service ядру MS SQL SERVER.
* SQL Sever находит dll библиотеку ассоциированную с именем расширенной хранимой процедуры и загружает её в свой контекст, если она не была загружена туда ранее, и вызывает расширенную хранимую процедуру, реализованную как функцию внутри dll.
* Расширенная хранимая процедура выполняет на сервере необходимые ей действия и передаёт набор результатов клиентскому приложению, используя сервис предоставляемый ODS API.
Особенности расширенных хранимых процедур.
* Расширенные хранимые процедуры - это функции выполняющиеся в адресном пространстве MS SQL Server и в контексте безопасности учётной записи под которой запущена служба MS SQL Server;
* После того, как dll библиотека с расширенными хранимыми процедурами была загружена в память, она остаётся там до тех пор, пока SQL Server не будет остановлен, или пока администратор не выгрузит её принудительно, используя команду :
DBCC DLL_name (FREE).
* Расширенная хранимая процедура запускается на выполнение так же, как и обычная хранимая процедура:
EXECUTE xp_extendedProcName @param1, @param2 OUTPUT
@param1 входной параметр
@param2 входной/выходной параметр
Внимание!
Так как расширенные хранимые процедуры выполняются в адресном пространстве процесса службы MS SQL Server, любые критические ошибки, возникающие в их работе, могут вывести из строя ядро сервера, поэтому рекомендуется тщательно протестировать Вашу DLL перед установкой на рабочий сервер.
Создание расширенных хранимых процедур.
Расширенная хранимая процедура эта функция имеющая следующий прототип:
Параметр pSrvProc указатель на SRVPROC структуру, которая является описателем (handle) каждого конкретного клиентского подключения. Поля этой структуры недокументированны и содеражат информацию, которую библиотека ODS использует для управления коммуникацией и данными между серверным приложением (Open Data Services server application) и клиентом. В любом случае, Вам не потребуется обращаться к этой структуре и тем более нельзя модифицоравать её. Этот параметр требуется указывать при вызове любой функции ODS API, поэтому в дальнейшем я небуду останавливаться на его описании.
Использование префикса xp_ необязательно, однако существует соглашение начинать имя расширенной хранимой процедуры именно так, чтобы подчеркнуть отличие от обычной хранимой процедуры, имена которых, как Вы знаете, принято начинать с префикса sp_.
Так же следует помнить, что имена расширенных хранимых процедур чувствительны к регистру. Не забывайте об этом, когда будете вызвать расширенную хранимую процедуру, иначе вместо ожидаемого результата, Вы получите сообщение об ошибке.
Если Вам необходимо написать код инициализации/деинициализации dll, используйте для этого стандартную функцию DllMain(). Если у Вас нет такой необходимости, и вы не хотите писать DLLMain(), то компилятор соберёт свою версию функции DLLMain(), которая ничего не делает, а просто возвращает TRUE. Все функции, вызываемые из dll (т.е. расширенные хранимые процедуры) должны быть объявлены, как экспортируемые. Если Вы пишете на MS Visual C++ используйте директиву __declspec(dllexport). Если Ваш компилятор не поддерживает эту директиву, опишите экспортируемую функцию в секции EXPORTS в DEF файле.
Итак, для создания проекта, нам понадобятся следующие файлы:
* Srv.h заголовочный файл, содержит описание функций и макросов ODS API;
* Opends60.lib файл импорта библиотеки Opends60.dll, которая и реализует весь сервис предоставляемый ODS API.
Microsoft настоятельно рекомендует, чтобы все DLL библиотеки реализующие расширенные хранимые процедуры экспортировали функцию:
Когда MS SQL Server загружает DLL c extended stored procedure, он первым делом вызывает эту функцию, чтобы получить информацию о версии используемой библиотеки.
Для написания своей первой extended stored procedure, Вам понадобится установить на свой компьютер:
- MS SQL Server 2000 любой редакции (у меня стоит Personal Edition). В процесе инсталляции обязательно выберите опцию source sample
- MS Visual C++ (я использовал версию 7.0 ), но точно знаю подойдёт и 6.0
Установка SQL Server -a нужна для тестирования и отладки Вашей DLL. Возможна и отладка по сети, но я этого никогда не делал, и поэтому установил всё на свой локальный диск. В поставку Microsoft Visual C++ 7.0 редакции Interprise Edition входит мастер Extended Stored Procedure DLL Wizard. В принципе, ничего сверх естественного он не делает, а только генерирует заготовку шаблон расширенной хранимой процедуры. Если Вам нравятся мастера, можете использовать его. Я же предпочитаю делать всё ручками, и поэтому не буду рассматривать этот случай.
Теперь к делу:
- Запустите Visual C++ и создайте новый проект - Win32 Dynamic Link Library.
- Включите в проект заголовочный файл - #include <srv.h>;
- Зайдите в меню Tools => Options и добавьте пути поиска include и library файлов. Если , при установке MS SQL Server, Вы ничего не меняли, то задайте:
- C:Program FilesMicrosoft SQL Server80ToolsDevToolsInclude для заголовочных файлов;
- C:Program FilesMicrosoft SQL Server80ToolsDevToolsLib для библиотечных файлов.
- Укажите имя библиотечного файла opends60.lib в опциях линкера.
На этом подготовительный этап закончен, можно приступать к написанию своей первой extended stored procedure.
Постановка задачи.
Прежде чем приступать к программированию, необходимо чётко представлять с чего начать, какой должен быть конечный результат, и каким способом его добиться. Итак, вот нам техническое задание:
Разработать расширенную хранимую процедуру для MS SQL Server 2000, которая получает полный список пользователей зарегистрированных в домене, и возвращает его клиенту в виде стандартного набора записей (record set). В качестве первого входного параметра функция получает имя сервера содержащего базу данных каталога (Active Directory), т.е имя контролера домена. Если этот параметр равен NULL, тогда необходимо передать клиенту список локальных групп. Второй параметр будет использоваться extended stored procedure для возварата значения результата успешной/неуспешной работы (OUTPUT параметр). Если, расширенная хранимая процедура выполнена успешно, тогда необходимо передать количество записей возвращённых в клиентский record set , если в процессе работы не удалось получить требуемую информацию, значение второго параметра необходимо установить в -1, как признак неуспешного завершения.
.
А вот шаблон расширенной хранимой процедуры, который нам предстоит наполнить содержанием:
Работа с входными параметрами
В этой главе я не хочу рассеивать Ваше внимание на посторонних вещах, а хочу сосредоточить его на работе с переданными в расширенную хранимую процедуру параметрами. Поэтуму мы несколько упростим наше техническое задание и разработаем тольку ту его часть, которая работает с входными параметрами. Но сначал не много теории
Первое действие, которое должна выполнить наша exteneded stored procedure , - получить параметры, которые были переданы ей при вызове. Следуя приведённому выше алгоритму нам необходимо выполнить следующие действия:
- Определить кол-во переданных параметров;
- Убедится, что переданные параметры имеют верный тип данных;
- Убедиться, что указанный OUTPUT параметр имеет достаточную длину, для сохранения в нём значения возвращаемого нашей extended stored procedure.
- Получить переданные параметры;
- Установить значения выходного параметра как результат успешного/неуспешного завершения работы extended stored procedure .
Теперь рассмотрим подробно каждый пункт:
Определение количества переданных в расширенную хранимую процедуру параметров
Для получения количества переданных параметров необходимо использовать функцию:
.
При успешном завершении функция возвращает количество переданных в расширенную хранимую процедуру параметров. Если extended stored procedure была вызвана без параметров - srv_rpcparams ввернёт -1. Параметры могут быть переданы по имени или по позиции (unnamed). В любом случае, нельзя смешивать эти два способа. Попытка передачи в функцию входных параметров по имени и по позиции одновременно - приведёт к возникновению ошибки, и srv_rpcparams вернёт 0 .
[pagebreak]
Определение типа данных и длины переданых параметров
Для получения информации о типе и длине переданных параметров Microsoft рекомендует использовать функцию srv_paramifo. Эта универсальная функция заменяет вызовы srv_paramtype, srv_paramlen, srv_parammaxlen, которые теперь считаются устаревшими. Вот её прототип:
.
.
.
.
.
.
.
.
.
.
pByte - указатель на переменную получающую информацию о типе входного параметра;
pbType задаёт порядковый номер параметра. Номер первого параметра начинается с 1.
pcbMaxLen - указатель на переменную, в которую функция заносит максимальное значение длины параметра. Это значение обусловлено конкретным типом данных переданного параметра, его мы и будем использовать, чтобы убедиться втом, что OUTPUT параметр имеет достаточную длину для сохранения передаваемых данных.
pcbActualLen указатель на реальную длину параметра переданного в расширенную хранимую процедуру при вызове. Если передаваемый параметр имеет нулевую длину, а флаг pfNull устанавлен в FALSE то (* pcbActualLen) ==0.
pbData - указатель на буфер, память для которого должна быть выделена перед вызовом srv_paraminfo. В этом буфере функция размещает полученные от extended stored procedure входные параметры. Размер буфера в байтах равен значению pcbMaxLen. Если этот параметр установлен в NULL, данные в буфер не записываются, но функция корректно возвращает значения *pbType, *pcbMaxLen, *pcbActualLen, *pfNull. Поэтому вызывать srv_paraminfo нужно дважды: сначала с pbData=NULL, потом, выделив необходимый размер памяти под буфер равный pcbActualLen, вызвать srv_paraminfo второй раз, передав в pbData указатель на выделенный блок памяти.
pfNull указатель на NULL-флаг. srv_paraminfo устанавливает его в TRUE, если значение входного параметра равно NULL.
Проверка, является ли второй параметр OUTPUT параметром.
Функция srv_paramstatus() предназначена для определения статуса переданного параметра:
.
.
.
.
.
n - номер параметра переданного в расширенную хранимую процедуру при вызове. Напомню: параметры всегда нумеруются с 1.
Для возврата значения, srv_paramstatus использует нулевой бит. Если он установлен в 1 переданный параметр является OUTPUT параметром, если в 0 обычным параметром, переданным по значению. Если, exteneded stored procedure была вызвана без параметров, функция вернёт -1.
Установка значения выходного параметра.
Выходному параметру, переданному в расширеную хранимую можно передать значение используя функцию srv_paramsetoutput. Эта новая функция заменяет вызов функции srv_paramset, которая теперь считается устаревашай, т.к. не поддерживает новые типы данных введённые в ODS API и данные нулевой длины.
.
.
.
.
.
.
.
.
n - порядковый номер параметра, которому будет присвоено новое значение. Это должен быть OUTPUT параметр.
pbData указатель на буфер с данными, которые будут посланы клиенту для установки значения выходного параметра.
cbLen длина буфера посылаемых данных. Если тип данных переданного OUTPUT параметра определяет данные постоянной длины и не разрешает хранение значения NULL (например SRVBIT или SRVINT1), то функция игнорирует параметр cbLen. Значение cbLen=0 указывает на данные нулевой длины, при этом парметр fNull должен быть установлен в FALSE.
fNull установите этот его в TRUE, если возвращаемому параметру необходимо присвоить значение NULL, при этом значение cbLen должно быть равно 0, иначе функция завершится с ошибкой. Во всех остальных случаях fNull=FALSE.
В случае успешного завершения функция возвращает SUCCEED. Если возвращаемое значение равно FAIL, значит вызов был неудачным. Всё просто и понятно
Теперь мы достаточно знаем, для того чтобы написать свою первую расширенную хранимую процедуру, которая будет возвращать значение через переданный ей параметр.Пусть, по сложившейся традиции, это будет строка Hello world! Отладочну версию примера можно скачать здесь.
. Не рассмотренными остались функции srv_sendmsg и srv_senddone. Функция srv_sendmsg используется для посылки сообщений клиенту. Вот её прототип:
msgtype определяет тип посылаемого клиенту сообщения. Константа SRV_MSG_INFO обозначает информационное сообщение, а SRV_MSG_ERROR сообщение об ошибке;
msgnum номер сообщения;
class - степень тяжести возникшей ошибки. Информационные сообщения имеют значение степени тяжести меньшее или равное 10;
state номер состояния ошибки для текущего сообщения. Этот параметр предоставляет информацию о контексте возникшей ошибки. Допустимые значения лежат в диапазоне от 0 до 127;
rpcname в настоящее время не используется;
rpcnamelen - в настоящее время не используется;
linenum здесь можно указать номер строки исходного кода. По этому значению, в последствие будет легко установить в каком месте возникла ошибка. Если Вы не хотите использовать эту возможность, тогда установите linenum в 0;
message указатель на строку посылаемую клиенту;
msglen определяет длину в байтах строки сообщения. Если это строка заканчивается нулевым символом, то значение этого параметра можно установить равным SRV_NULLTERM.
Возвращаемыме значения:
- в случае успеха SUCCEED
- при неудаче FAIL.
В процессе работы расширенная хранимая процедура должна регулярно сообщать клиентскому приложению свой статус, т.е. посылать сообщения о выполненных действиях. Для этого и предназначена функция srv_senddone:
status - статус флаг. Значение этого параметра можно задавать использую логические операторы AND и OR для комбинирования констант приведённых в таблице:
Status flag Описание
SRV_DONE_FINAL Текущий набор результатов является окончательным;
SRV_DONE_MORE Текущий набор результатов не является окончательным следует ожидать очердную порцию данных;
SRV_DONE_COUNT Параметр count содержит верное значение
SRV_DONE_ERROR Используется для уведомления о возникновении ошибок и немедленном завершении.
into зарезервирован, необходимо установить в 0.
count количество результирующих наборов данных посылаемых клиенту. Если флаг status установлен в SRV_DONE_COUNT, то count должен содержать правильное количество посылаемый клиенту наборв записей.
Возвращаемыме значения:
- в случае успеха SUCCEED
- при неудаче FAIL.
Установка расширенных хранимых процедур на MS SQL Server 2000
1.Скопируйте dll библиотеку с расширенной хранимой процедурой в каталог binn на машине с установленным MS SQL Server. У меня этот путь следующий: C:Program FilesMicrosoft SQL ServerMSSQLBinn;
2.Зарегистрирйте расширенную хранимую процедуру на серверt выполнив следующий скрипт:
Заключение
На этом первая часть моей статьи закончена. Теперь я уверен Вы готовы справиться с нашим техническим заданием на все 100%. В следующей статье Вы узнаете:
- Типы данных определённые в ODS API;
- Особенности отладки расширенных хранимых процдур;
- Как формировать recordset-ы и передавать их клиентскому приложению;
- Чстично мы рассмотрим функции Active Directory Network Manegment API необходимые для получения списка доменных пользователей;
- Создадим готовый проект (реализуем наше техническое задание)
Надеюсь - до скорой встречи!
Можно сказать, что современная корпорация буквально "пропитана" данными. Они повсюду и, более того, очень часто одни и те же данные могут находиться в нескольких местах. Корпорация должна иметь возможность идентифицировать источник, происхождение, семантику и пути доступа к данным. Метаданные или, как их обычно называют, "данные о данных", являются ключом для получения этой информации. Но, как это ни удивительно, у большинства корпораций нет отчетливой стратегии относительно метаданных. Различные подразделения организации используют разные наборы инструментов для поддержки своих данных.
Каждому такому набору соответствуют определенные метаданные. Поэтому картина, типичная для многих корпораций, - это так называемые "острова метаданных", т.е. некоторые объемы информации, которые невозможно связать друг с другом. Для решения этой проблемы некоторые организации начинают крупные проекты по интеграции метаданных, тратя на это значительные средства и время. Но, к сожалению, в большинстве проектов отсутствует структурный подход, поэтому временные и финансовые затраты не окупаются.
В предлагаемой статье обсуждаются подходы к управлению метаданными, в том числе то, какие метаданные необходимо собирать, как их можно моделировать, как создать требуемое архитектурное решение и как обеспечить простоту поддержки метаданных в долгосрочной перспективе. Большинство этих подходов уже существуют в той или иной форме в различных организациях. В данной статье сделана попытка собрать и обобщить имеющийся опыт.
Классификация метаданных
На самом высоком уровне метаданные могут быть разделены на две категории:
Элементы общих метаданных должны иметь совместные (непротиворечивые) определения и семантику в масштабах всей корпорации. Например, определение понятия "клиент" должно быть единым для всей компании.
Метаданные могут быть классифицированы и по другим параметрам:
Метаданные бизнеса включают определения объектов, относящихся к корпоративным пользователям, логическим картам данных и словарям Хранилищ данных. Технические метаданные включают данные о физических объектах: названия таблиц и столбцов, ограничения и правила физического преобразования между различными зонами. В метаданных процессов отражается статистическая информация о различных процессах: статистика загруженности, информация о календарном планировании и обработка исключений.
Создание решения для управления метаданными
Для создания успешного решения по управлению корпоративными метаданными автор рекомендует следовать определенной последовательности шагов:
1. собрать все требования, предъявляемые к метаданным;
2. выбрать соответствующую модель метаданных;
3. определить общие подходы к архитектуре;
4. внедрить выбранное решение и осуществлять его поддержку.
Сбор требований, предъявляемых к метаданным
Определение требований, предъявляемых к метаданным, может оказаться непростой задачей. Ключевые стороны, которым могут быть нужны метаданные, разнообразны и пространственно разобщены. Это могут быть как конечные пользователи или аналитики, так и приложения или наборы инструментов. Процесс сбора стандартных требований не должен слишком расплываться. Автор предлагает следующий подход, учитывающий специфическую природу метаданных:
* определение ключевых сторон для каждого элемента метаданных;
* отнесение каждого элемента метаданных к определенной категории: метаданным бизнеса, техническим или метаданным процессов;
* отнесение каждого элемента метаданных к категории общих или уникальных на основе их использования в тех или иных процессах.
Следующий шаг - идентификация источника элемента метаданных. Обычно они называются "официальными метаданными" или "метаданными записи"1. Метаданные записи указывают на официальную версию определенного элемента для какого-либо события, в котором может быть несколько источников одних и тех же данных. Для того чтобы назвать определенный элемент метаданных официальным, важно понимать различные процессы, которые могут привести к созданию этого элемента. Эта информация помогает определить официальный источник метаданных. Например, компания розничной торговли создает корпоративное Хранилище данных, при этом элементы, содержащие информацию о клиентах, появляются в нескольких местах, таких как Хранилище данных о потребителях, система управления отношениями с клиентами (Customer Relationship Management, сокр. CRM) и система сбыта. При этом важно проводить анализ надежности и полноты каждого источника и оценивать, какие именно определения могут использоваться в качестве официальной версии. В данном случае уже может существовать Хранилище данных о потребителях, определяющее соответствующее измерение, поэтому можно будет считать словарь данных этого Хранилища официальными метаданными записей. После того как этот процесс будет закончен для всех элементов метаданных, можно будет сказать, что организация требований к метаданным завершена.
Выбор метамодели
Следующий шаг после формализации требований к метаданным - создание модели. Моделирование метаданных важно, поскольку оно может стать элементом, который используется во всей корпорации. Существует несколько способов выбора модели метаданных:
* создание специальной модели данных для работы с метаданными;
* использование имеющихся стандартных моделей;
* оснащение доступного репозитория метаданных инструментами, позволяющими использовать его как источник интеграции.
Для создания специальной модели метаданных важно иметь корректные определения элементов, их атрибутов и связей с другими элементами. Такая модель может быть объектно-ориентированной или моделью типа объект-отношение. Что касается стандартных моделей, то тут существует два варианта: модель открытой информации (Open Information Model, сокр. OIM) и общая метамодель Хранилища данных (Common Warehouse Meta-Model, сокр. CWM). CWM описывает обмен метаданными между Хранилищами данных, средствами Business Intelligence и управления знаниями и портальными технологиями. Согласно компании Meta Data Coalition, OIM - это набор спецификаций метаданных для облегчения их совместного и многократного использования в области разработки приложений и Хранилищ данных. OIM описывается с помощью универсального языка моделирования (Unified Modeling Language, сокр. UML) и организуется по предметным областям, которые могут быть легко использованы и при необходимости расширены. Эта модель данных основана на отраслевых стандартах, таких как UML, XML и SQL.
Выбор подходящей метамодели является непростой задачей. Хотя специальные модели бывают гораздо более гибкими, создание надежной модели на корпоративном уровне и ее долгосрочная поддержка могут оказаться довольно обременительными. Для решения такой задачи нужен хорошо продуманный план. С другой стороны, стандартные модели довольно широкие: они охватывают большинство требований, предъявляемых на корпоративном уровне. Но настройка таких моделей под специфические нужды корпорации может оказаться проблематичной. Для тех корпораций, где существуют наборы инструментов и связанные с ними метаданные, хорошим решением будет использование метамоделей от любого поставщика. При этом, безусловно, понадобятся существенные интеграционные усилия. С другой стороны, если корпорация только начинает работать с метаданными и у нее нет несовместимых наборов инструментов, то хорошим решением может быть создание собственной специальной метамодели.
После завершения моделирования метаданных важно определить репозиторий для хранения данных. Это может быть реляционное или объектно-ориентированное Хранилище.
[pagebreak]
Определение архитектуры высокого уровня
Для внедрения решений по работе с метаданными существует целый ряд архитектурных возможностей. Одно из решений - централизованный репозиторий, где хранятся все метаданные.
Основные элементы метаданных, которые будут храниться в таком центральном репозитории, - это метаданные приложений, систем управления базами данных, бизнеса и метаданные, связанные с различными процессами. Создание и модификация элементов метаданных должны осуществляться с помощью общего интерфейса. Для такого решения можно разработать специальную метамодель или использовать одну из стандартных. Данная архитектура имеет несколько преимуществ:
* сравнительно простая поддержка метаданных;
* упрощенные процедуры взаимодействия между компонентами;
* простые процедуры подготовки отчетности.
Некоторые корпорации пытаются создавать очень небольшие решения для работы с метаданными. Это означает, что каждое подразделение организации конструирует свое собственное решение.
Для облегчения обмена метаданными в качестве основы для их передачи используется XML. Каждое приложение, система управления базами данных или инструмент вступает в контакт с репозиторием с помощью XML. Парсер репозитория преобразует формат XML в формат метамодели и обновляет содержимое репозитория.
Наконец, третье архитектурное решение известно под названием распределенной архитектуры. Это тот случай, когда корпорация уже потратила значительное количество ресурсов на создание локального решения для работы с метаданными, а интеграция в масштабах всей корпорации оказывается слишком дорогостоящей. В результате локальное решение продолжает существовать, а в тех случаях, когда это оправдано и выгодно, происходит совместное пользование метаданными из нескольких источников.
Внедрение и поддержка решения для работы с метаданными
После завершения разработки архитектуры и выбора метамоделей можно приступать к внедрению решения. При этом надо иметь в виду следующее:
1. природу репозитория метаданных (реляционная база данных, система файлов, объектно-ориентированная база данных или репозиторий XML);
2. вопросы безопасности репозитория метаданных (кто управляет репозиторием; кто имеет право читать информацию репозитория или обновлять ее);
3. механизмы создания, чтения и добавления компонентов метаданных;
4. инфраструктуру отчетности для метаданных.
После разработки плана и обеспечения соответствующих инструментальных средств можно приступать к внедрению решения для работы с метаданными.
Но собственно внедрение еще не обеспечивает решения всех проблем. Важно обеспечить достаточно продолжительное функционирование созданной системы и ее соответствующее обслуживание. Одно из основных требований при этом - правильное распределение ролей и ответственности в корпорации.
После распределения ролей и ответственности необходимо создать процесс, определяющий жизненный цикл метаданных. Этот цикл задает следующие параметры: кто создает метаданные, кто использует их компоненты и кто отвечает за поддержку этих компонентов. Один из главных критериев долгосрочного успеха решения для работы с метаданными - это его расширяемость. Архитектура должна позволять легко добавлять новые требования к метаданным. Для этого необходим специальный процесс, обеспечивающий добавление новой информации о метаданных. При этом необходимо получить ответы на следующие важные вопросы:
* нужно ли хранить новые метаданные в общем репозитории (если таковой имеется);
* каковы методы доступа к элементам этих метаданных (только чтение или чтение и запись);
* являются ли эти метаданные уникальными или будут использоваться несколькими приложениями.
На основе ответов на эти вопросы принимаются соответствующие решения о хранении компонентов новых метаданных.
Пример решения для работы с метаданными
В качестве примера автор приводит розничную компанию, имеющую несколько Хранилищ данных для обеспечения различных видов бизнес-отчетности. Компания имеет Хранилище для составления отчетов по каналам поставок, Хранилище для CRM, Хранилище для данных о продажах и отдельное Хранилище для финансовой информации. Компания хочет создать единое корпоративное Хранилище данных с помощью консолидации информации в масштабах всей организации. Это хранилище будет центральным репозиторием для всех корпоративных данных, а отдельные подразделения будут создавать себе витрины данных на его основе. В процессе реализации этого проекта пришло понимание того, что также необходимо выработать стратегию консолидации метаданных.
Для этого можно использовать подход, описанный выше, который включает четыре основных действия. Первое действие - определение требований к метаданным. Этот процесс включает идентификацию заинтересованных сторон и классификацию метаданных. Поскольку это проект консолидации Хранилища данных, то типы метаданных будут достаточно простыми. Основные элементы - это некоторые корпоративные измерения, которые должны быть определены, и корпоративные факты. Оба этих элемента связаны с одними и теми же метаданными бизнеса. Следующий набор метаданных - это список таблиц и граф, использующих данные измерения и факты, т.е. это технические метаданные. Наконец, для документирования процессов ETL (extraction, transformation, loading - извлечение, преобразование и загрузка) и создания витрин данных необходима информация о тех шагах, из которых они состоят, т.е. это метаданные о процессах.
Для этих метаданных заинтересованными сторонами являются те, кто занимаются моделированием данных, а также разработчики ETL, витрин данных и отчетов. Помимо этого, такие метаданные нужны для работы с инструментами ETL и отчетности. Для консолидации метаданных требуются все элементы метаданных, их классификация, а также информация о том, кто и какие именно данные использует.
Следующий шаг - моделирование решения для работы с метаданными. В организации было принято решение создать свою метамодель, которая бы учитывала требования к модели данных, процессу ETL, витринам данных и инструментам отчетности.
После создания метамодели необходимо определить общую архитектуру. Было решено создать единый репозиторий для метаданных и определить процесс, который обеспечит его наполнение из всех систем. Например, после определения измерений и фактов метаданные экспортируются из инструментов моделирования данных и сохраняются в репозитории. Информация о процессах ETL создается вручную и также сохраняется в репозитории. Репозиторий инструментов отчетности наполняется с помощью заранее определенной технологии. Для выполнения требований отчетности, предъявляемых к метаданным, была создана система отчетности на основе интернета, которая создает запросы к репозиторию для получения информации.
После создания такого решения консолидация метаданных может считаться практически законченной. Следующая проблема - обеспечение долговременной работы данного решения. Например, как должен обрабатываться новый элемент или измерение, созданные в модели данных? Как вносится информация о новом процессе ETL или новом отчете? Все это определяется процессом поддержки метаданных. Для моделей данных периодически используется процесс синхронизации репозиториев инструментов и метаданных. Для ETL и отчетности существуют аналогичные процессы.
Заключение
Важность метаданных для корпораций уже общепризнанна. При работе с метаданными очень важно предварительно выработать соответствующую стратегию. Также важно понимать, что метаданные не являются универсальным средством для управления данными. Это мощное средство, которое может существенно улучшить качество анализа данных в корпорации, тем самым способствуя росту эффективности ее работы. При этом важно не распыляться в поисках абсолютно совершенного решения, а создавать решение, наиболее оптимальное для конкретного бизнеса.