ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
ЮЖНО-УРАЛЬСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
УДК 681.3.06 (07) К721 В. В. Костерин
ИСПОЛЬЗОВАНИЕ КОМПОНЕНТОВ VCL В BORLAND BUILDER C++ ДЛЯ ПРИЛОЖЕНИЙ WINDOWS Учебное пособие к лабораторным работам
Челябинск 2006
Министерство образования и науки Российской Федерации Федеральное агентство по образованию Южно-Уральский государственный университет Кафедра «Информационные системы»
УДК 681.3.06 (07) К721 В. В. Костерин
ИСПОЛЬЗОВАНИЕ КОМПОНЕНТОВ VCL В BORLAND BUILDER C++ ДЛЯ ПРИЛОЖЕНИЙ WINDOWS
Учебное пособие к лабораторным работам
Челябинск Издательство ЮУрГУ 2006 3
УДК 681.3.06:519.6 (075.8) К721 Одобрено учебно-методической комиссией факультета «Экономика и предпринимательство» Рецензеты: В.Д. Гунченко, Сафронова И.В.
Костерин, В.В. К721 Использование компонентов VCL в Borland Builder C++ для приложений Windows: Учебное пособие к лабораторным работам/ Костерин В.В. – Челябинск: Изд-во ЮУрГУ, 2006 – 73 с
Учебное пособие «Использование компонентов VCL в Borland Builder C++ для приложений Windows» содержит руководство для выполнения пяти комплексов задач по практической отработке приёмов работы с компонентами библиотеки VCL. Необходимо отметить качественный подбор методического материала задач, который позволяет студентам практически освоить основные приемы решения типовых задач, связанных с традиционными вопросами информационных технологий, например, применение функций API для анализа ресурсов вычислительной системы, приёмы работы с базами данных различных форматов с использованием библиотеки BDE и ActivX Data Object (ADO). Особое внимание уделено методологии построения «дружественного» пользовательского интерфейса современного программного обеспечения. Для иллюстрации способов решения задач в пособии приведены возможные варианты исходного программного кода, которые предполагают не только слепое копирование, но позволяют студентам расширить исходную постановку задачи и проявить свои творческие способности. Более того, приведенный программный код может многократно использоваться в реальных приложениях для решения практических задач.
УДК 681.3.05(075.8) + 681.3.068(075.8) © В.В. Костерин, 2006 © Издательство ЮУрГУ, 2006 4
ОГЛАВЛЕНИЕ Лабораторная работа №1. «Однокнопочный бандит» – наше первое приложение .......................................................................................................... 5 Упражнение 1.1. Запустим наш автомат .......................................................... 5 Упражнение 1.2. Работаем с картинками и добавим «вишенки» и «яблочки»............................................................................................................11 Упражнение 1.3. Определим условия выигрыша ..........................................17 Отчет о лабораторной работе ..........................................................................18 Лабораторная работа №2. Делаем заставки................................................... 20 Упражнение 2.1. Создание простейшей заставки .........................................20 Упражнение 2.2. Вывод на заставке информации о приложении ...............23 Упражнение 2.3. Видеофрагмент на заставке ................................................27 Упражнение 2.4. Видеофрагмент на полном экране .....................................30 Отчет о лабораторной работе ..........................................................................31 Лабораторная работа №3. Внешние устройства и операционная система 32 Упражнение 3.1. Наличие дискеты или компакт-диска в дисководе ..........32 Упражнение 3.2. Параметры текущего видеорежима ...................................34 Упражнение 3.3. Сведения о частоте развертки монитора ..........................36 Упражнение 3.4. Инициирование перезагрузки Windows ...........................37 Упражнение 3.5. Отключение хранителя экрана...........................................39 Упражнение 3.6. Получение сведений об операционной системе ..............42 Отчет о лабораторной работе ..........................................................................45 Лабораторная работа №4. Базы данных, Borland Database Engine (BDE). Настройка параметров доступа. Библиотека Borland Database Engine (BDE). Утилита настройки BDE ................................................................................... 46 Особенности настройки BDE для работы с некоторыми источниками данных .................................................................................................................56 dBase ................................................................................................................... 56 Paradox ................................................................................................................ 57 ODBC-источники .............................................................................................. 58 Oracle................................................................................................................... 62 InterBase .............................................................................................................. 63 Упражнение 4.1. Формулировка задачи и создание базы данных для «Однокнопочного бандита» ..............................................................................64 Упражнение 4.2. Создание отчетов.................................................................65 Упражнение 4.3. Изменение цвета строки в TDBGrid ..................................65 Упражнение 4.4. Замена данных в столбце компонента TDBGrid ..............69 Упражнение 4.5. Графическое изображение в TDBGrid ..............................71 Отчет о лабораторной работе ..........................................................................73 5
Лабораторная работа №5. MIDAS и настольные приложения .................... 74 Упражнение 5.1. Приложение с базой данных без использования BDE ....75 Упражнение 5.2. Экономим место на форме при отображении таблиц .....79 Упражнение 5.3. Сортировка данных в компоненте TClientDataSet...........79 Упражнение 5.4. Универсальный инструмент для сохранения содержимого таблиц в локальных файлах ......................................................80 Отчет о лабораторной работе ..........................................................................84
ЛАБОРАТОРАЯ РАБОТА №1. «ОДНОКНОПОЧНЫЙ БАНДИТ» – НАШЕ ПЕРВОЕ ПРИЛОЖЕНИЕ Итак, Вы знаете немного теории объектно-ориентированного программирования и уже сможете отличить класс от структуры. Пришла пора применить эти знания на практике и написать первое приложение на Builder C++. Кстати, предоставляется замечательный повод сравнить инструментарий для программирования на «Вижуал-Васике» и инструментарий настоящих программистов, уважающих себя и потенциального пользователя. Один из студентов второго курса факультета «Экономики и предпринимательства» признался, что практически всю свою стипендию он оставляет воротилам игорного бизнеса в игровых автоматах. Думается, что не он один стал рабом азарта. Так вот предлагаю Вам сделать такой автомат у себя на домашнем компьютере. Азарт тот же, а сохраненная стипендия будет вашим выигрышем. По теории вероятности – это гораздо больше, чем Вы сможете выиграть, тем более – все в Ваших руках и правила игры Вы сможете установить в свою пользу. Лабораторная работа состоит из трех упражнений. 1. Запустим наш автомат, где в 4–5 полях случайным образом появляются цифры в диапазоне от 0 до 9. 2. Работаем с картинками и добавим «вишенки» и «яблочки». 3. Определим условия выигрыша и, самое главное, цифры выигрыша в подходящей для Вас валюте.
Упражнение 1.1. Запустим наш автомат В этом упражнении мы сделаем игровой автомат (рис. 1), который в 5 полях случайным образом быстро меняет цифры в диапазоне от 0 до 9. Этот процесс запускает и останавливает игрок, пытаясь добиться совпадения цифр в нескольких полях. Чем больше совпадений – тем больше выигрыш. Правила игры мы обсудим позже, в третьем упражнении, а сейчас начнем проектирование. После того как Вы загрузили Builder и увидели заготовку главной формы приложения, обратите своё внимание на инспектор объектов и задайте свойства главной формы. Запишите название «Однокнопочный бандит» – свойство Caption; для свойства BorderStyle выберите значение bsSingle; для Position – poScreenCenter; значение BorderIcons – Maximize переведите в false. В проекте произошли изменения – сразу же сохраните их. Во время сохранения определите свои имена файлов (здесь они останутся такими, какими их предложил Builder C++, т.е. Project1.bpr – для проекта и Unit1.cpp – для главной формы).
Рис. 1. Главная форма приложения на этапе проектирования Сейчас Вам потребуется минимум выразительных средств диалога – компонентов библиотеки VCL (Visual Component Library) Builder C++, расположенных на палитре. С закладки «Standart» палитры компонентов заберите и перенесите на форму: 1) кнопку – TButton и 2) панель – TPanel, а с закладки «System» – 3) таймер (TTimer). Четвертый элемент, который Вы видите на рисунке – статусная строка TStatusBar с закладки «Win32» палитры, потребуется нам в дальнейшем, а сейчас просто перенесите её на форму и на некоторое время о ней забудьте. Задача таймера заключается в том, что бы во время работы приложения создавать непрерывный поток отсчетов – прерываний с нужной частотой. Ваша задача в том, что бы написать функцию обработки этих прерываний, которая должна некоторым, типа, случайным образом генерировать 5 чисел. Кнопка предназначена для Включения/Выключения таймера, т.е. для запуска и остановки процесса создания случайных чисел. Панели, в дальнейшем их станет пять, предназначены для отображения значений случайных чисел. Вот такая основная идея нашего игрового автомата. Таймер компонент невидимый, поэтому его расположение на форме во время визуального проектирования не имеет никакого значения, а вот о кнопке и панели надо побеспокоиться. Прежде всего, измените
первоначальный размер панели с помощью манипулятора «мышь». Это делается точно так же как с любым окном Windows. Для точной подгонки размеров и положения компонента на форме можно использовать комбинации клавиш <Ctrl> или <Shift> в сочетании с клавишами «стрелок» дополнительной клавиатуры. В инспекторе объектов подберите для панели значения группы свойств Font. В конечном итоге, допейтесь того, что бы Ваша панель стала лучше, чем панель на рис. 1. Теперь копируем панель в буфер обмена (File–>Edit–>Copy) и вставляем несколько экземпляров на форму (File–>Edit–>Paste), равномерно их размещая. С кнопкой всё гораздо проще. В инспекторе объектов для неё задаются свойства Font и Caption. Для размещения кнопки в центре окна по горизонтали наведите указатель манипулятора «мышь» на кнопку, нажмите правую кнопку манипулятора и в меню следуйте подпунктами Position–>Align–>Center in Window. Визуальное проектирование закончено – пора приступить к коду, который Вы сможете увидеть ниже. Все это конечно можно переписать в свой проект и сохранить, но при компиляции у Вас, скорее всего, будут проблемы, поэтому будем действовать последовательно – шаг за шагом. Внимание, Builder требует некоторой аккуратности. Теперь, обсудим текст кода. Попытайтесь понять, как и почему все это работает. Пока ничего не редактируйте, а только смотрите. Переключитесь на закладку “Unit1.hpp” редактора кода и прочитайте текст, который был создан автоматически. //--------------------------------------------------------------------------#ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------#include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include <ExtCtrls.hpp> #include <ComCtrls.hpp> //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components TPanel *Panel1; TPanel *Panel2;
TPanel *Panel3; TPanel *Panel4; TPanel *Panel5; TButton *Button1; TTimer *Timer1; TStatusBar *StatusBar1; private: // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------#endif С операторами препроцессора, которые начинаются с символа # и выделены зелёным цветом, думаю всё понятно. Ключевые, зарезервированные слова, языка С++ выделены жирным черным цветом, а всё остальное Builder написал самостоятельно в соответствии с вашими дизайнерскими изысками во время визуального проектирования – перетаскивания компонентов с палитры на форму. Ваша форма описана как класс с именем TForm1 и является наследницей класса TForm из библиотеки VCL. В исходном коде к ней можно обращаться по имени Form1, что определяется строкой «extern PACKAGE TForm1 *Form1;». В секции __published: описания класса TForm1 Вы видите указатели на перенесенные вами компоненты: Panel1, Panel2, Panel3, Panel4, Panel5, Button1, Timer1 и StatusBar1. Так как описание находится в этой секции, то к экземплярам соответствующих классов Вы имеете доступ из инспектора объектов, в котором можете изменять свойства, а также назначать функции, определяющие реакцию приложения на события, созданные этими компонентами. В секции public описана всего одна функция «__fastcall TForm1(TComponent* Owner);» – конструктор класса TForm1. Файл Unit1.cpp содержит исполняемые операторы функций класса TForm1. Именно здесь Вы будете записывать свои операторы, а сейчас там только текст заготовки конструктора и описание интерфейса библиотеки VCL. //--------------------------------------------------------------------------#include <vcl.h> #pragma hdrstop
#include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } Механизм создания случайных чисел Вам уже известен – в приложении один единственный раз необходимо вызвать инициализирующую функцию random, а затем для каждого числа функцию rand. Но здесь воспользуйтесь современными функциями библиотеки VCL Randomize() и RandomRange(), о которых рекомендую прочитать в справочнике Builder. Для формы Form1 в инспекторе объектов переключитесь на закладку «Events» (События) и напротив строки OnCreate (Создание формы) дважды щелкните левой кнопкой «мышки». В окне редактора появится заготовка кода функции реакции на событие «Создание формы». void __fastcall TForm1::FormCreate(TObject *Sender) { } Теперь между фигурными скобками, ограничивающими тело функции, запишите следующие ниже строки. Randomize(); Panel1->Caption = AnsiString(RandomRange(0,10)); Panel2->Caption = AnsiString(RandomRange(0,10)); Panel3->Caption = AnsiString(RandomRange(0,10)); Panel4->Caption = AnsiString(RandomRange(0,10)); Panel5->Caption = AnsiString(RandomRange(0,10)); Обратите внимание на то, что в файле Unit1.hpp в классе TForm1 появилось описание функции FormCreate. Сейчас можно нажать на клавишу <F9> и посмотреть, что же Вы натворили. Если все правильно, то приложение будет выполнено, а на панелях формы Вы увидите набор случайных цифр (рис. 2). Здесь, наверное, все понятно. Первая строчка инициализирует генератор псевдослучайных чисел, а в пяти последующих вызывается функция RandomRange(0,10) для создания случайного числа в диапазоне от 0 до 9 включительно. Результат работы этой функции выступает в
качестве аргумента конструктора класса AnsiString, создающего экземпляр класса AnsiString, который присваивается свойству Captinon экземпляра класса TPanel. Заставим цифры изменяться. Для этого активизируем при проектировании компонент Timer1, переключимся на закладку Events инспектора объектов и дважды щелкнем левой кнопкой «мышки» на событии OnTimer. Далее в тело заготовки функции реакции на прерывание таймера скопируем четыре последних строчки предыдущей функции. void __fastcall TForm1::Timer1Timer(TObject *Sender) { Panel1->Caption = AnsiString(RandomRange(0,10)); Panel2->Caption = AnsiString(RandomRange(0,10)); Panel3->Caption = AnsiString(RandomRange(0,10)); Panel4->Caption = AnsiString(RandomRange(0,10)); Panel5->Caption = AnsiString(RandomRange(0,10)); } Во время работы приложения функция Timer1Timer будет выполняться многократно при появлении прерывания от таймера, а функция FormCreate всего один раз при создании формы и размещении её в памяти. Таким образом, инициализация датчика случайных чисел производится всего один раз, а получение этих чисел с помощью функции RandomRange много раз. Проверьте результат. Если все правильно, то цифры начнут изменяться. Подрегулируйте скорость смены цифр. Для этого на закладке Properties инспектора объектов подберите значение свойства Interval для компонента Timer1 (рекомендую 100). Значение по умолчанию 1000 соответствует 1 секунде. Научимся включать и выключать таймер. Для этого достаточно переключать его свойство Enabled при каждом нажатии кнопки Button1. Запишем после создания заготовки функции обработки события OnClick кнопки всего один оператор. Timer1->Enabled = !Timer1->Enabled; Семь, всего семь, оригинальных строчек кода, которые пришлось написать руками, а четыре строчки скопировать, а какой результат – автомат заработал! На этом цель первого упражнения будем считать достигнутой и перейдем к следующим заданиям.
Рис. 2. Приложение во время выполнения
Упражнение 1.2. Работаем с картинками и добавим «вишенки» и «яблочки» Смотреть на то, как перед глазами мелькаю цифры конечно можно, но утомительно и неинтересно. Поэтому попробуем «оживить» наше приложения картинками, здесь я их назвал «вишенками» и «яблочками», по аналогии с первыми игральными автоматами. Сейчас мы добились управляемого пользователем появления случайных чисел, которые во внешнем представлении отображаются названиями компонента TPanel. Естественно, что для отображения графических образов нам потребуется другой компонент, и он есть в Builder – это TImage с закладки Addition палитры компонентов. Для того, что бы воспользоваться плодами предыдущей работы создадим новую папку и скопируем все файлы первой лабораторной в него. Загрузим этот проект и сохраним его с новым именем. Раз мы решили использовать новый компонент для отображения случайных чисел смело выделяем пять панелек на форме и нажимаем клавишу <Delete> для их удаления. После чего перенесём на форму компонент TImage и определим его свойства, для начала – это размеры. После этого в инспекторе объектов переключим свойство Strecth в true. И тогда загруженный в него графический файл точно поместится в заданные размеры. Но помните, что при этом могут нарушится пропорции исходного изображения. Поэтому очень не маловажный момент – подбор картинок и синхронизация пропорций с TImage. Найдите в инспекторе
объектов свойство Picture и загрузите изображение из файла следуя инструкциям мастера. Кстати, этот компонент наиболее удачно работаем с файлами расширения .bmp. Скопируйте компонент в буфер обмена и поместите на форму ещё четыре его копии. Должно получится что-то подобное рис. 3. Теперь же, необходимо придумать механизм отображения с помощью картинок. Проще всего это сделать, связав имя графического файла с цифрами. Диапазон наших случайных чисел [0;9]. Давайте договоримся, что имена графических файлов для нашего приложения будут представлять собой цифры из вышеназванного диапазона и соответствующее расширение. Например, 0.bmp, 1.bmp и так далее до 9.bmp. Всего таких файлов нам потребуется 10. Доступными Вам способами найдите файлы графических изображений, конвертируйте их в формат bmp, например, программой irfanview и задайте им имена. По правилам хорошего тона, желательно, эти файлы разместить в отдельной папке, например, с именем IMAGE. Естественно, во время предварительной обработки графических файлов побеспокойтесь о том, что бы размеры всех изображений были одинаковыми, расширения были одинаковыми и пробными загрузками в компоненты TImage на Вашей форме сделайте её более-менее эстетичной. Брать пример с рис. 3 не надо – это не лучший пример для подражания, можно придумать что-то более интересное.
Рис. 3. Добавим в приложение «яблочки» и «вишенки» После этой подготовительной работы можно приступать к написанию кода. В первой лабораторной работе фрагмент кода, связанный с получением псевдослучайных чисел мы записали в двух функциях, а именно, в FormCreate(TObject *Sender) и в Timer1Timer(TObject *Sender). Код был простой и поэтому дублирование не вызывает особых возражений, но сейчас, все что связано с отображением картинок гораздо сложнее. Поэтому целесообразно создать отдельную функцию, которая будет этим заниматься. Кроме того, для нормальной работы приложения требуется дополнительная информация, а именно, имя папки, которая содержит графические файлы.
Исходя из этих соображений, начнём править имеющийся у нас код. Во-первых, удалим все строчки, где встречается слово Panel. Панелек у нас уже нет и что-либо делать с ними, программно, мы не имеем никакого права. Кстати, обратите внимание, что после удаление панелей с формы в заголовочном файле описания класса TForm1 пропало описание указателей на эти панели, а после переноса компонентов TImage появились описания: TImage *Image1; TImage *Image2; TImage *Image3; TImage *Image4; TImage *Image5;. В описании класса TForm1 отразим наши соображения, а именно, сделаем в секции Private описание функции отображения картинок, а также переменных для хранения имени папки изображений и имени графического файла. void __fastcall ImageShow(void); AnsiString FileDir, FileName;. Теперь описание нового класса выглядит следующим образом. class TForm1 : public TForm { __published: // IDE-managed Components TImage *Image1; TImage *Image2; TImage *Image3; TImage *Image4; TImage *Image5; TButton *Button1; TTimer *Timer1; TStatusBar *StatusBar1; private: // User declarations void __fastcall ImageShow(void); AnsiString ImageDir, FileName;. public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------------------------#endif Переходим на закладку редактирования кода функций класса и запишем новые FormCreate(TObject *Sender) и в Timer1Timer(TObject *Sender). void __fastcall TForm1::FormCreate(TObject *Sender) { Randomize(); ImageDir = ExtractFilePath(Application->ExeName) + “\\IMAGE”; ImageShow(); } void __fastcall TForm1::Timer1Timer(TObject *Sender) { ImageShow(); } Самое главное для нас придумать алгоритм функции ImageShow(). Он приведен ниже, что-то для Вас знакомо, а что-то требуется обсудить. Внимательно читайте код и приступим. void __fastcall TForm1::ImageShow(void) { int i; for( i=0; i<ComponentCount; i++ ) { TImage *img = dynamic_cast<TImage*>(Components[i]); if( img ) { FileName = ImageDir; FileName = FileName+AnsiString( RandomRange(0,10) ) + ".bmp"; img->Picture->LoadFromFile( FileName ); } } } Самый главный здесь цикл for с параметром i и условием завершения i<ComponentCount. ComponentCount – это свойство экземпляра класса TFom1, которое равно количеству компонентов помещенных на форму, при этом адреса этих компонентов хранятся в массиве Components[], т.е.
ComponentCount – это всего лишь размер массива Components[]. Далее следует непонятная строчка TImage *img = dynamic_cast<TImage*>(Components[i]);. Она означает, что внутри тела цикла описывается указатель img класса TImage и тут же инициализируется с помощью оператора dynamic_cast, который извлекает из массива Components указатель и пытается преобразовать его к указателю класса TImage. Если это действительно так, то в переменную img записывается адрес этого компонента, в противном случае NULL. Совместно с конструкцией if( img ){ …} мы получаем доступ для изменения свойств только компонентов класса TImage, расположенных на форме. Далее все, наверное, понятно. В переменную FileName сначала записывается имя папки, в которой хранятся графические файлы, а потом добавляется имя файла, состоящее из случайного числа и расширения bmp. Файл с этим именем загружается в выбранный компонент TImage и, естественно, отображается на форме. Ну вот и всё. Если Вы всё правильно сделали, то приложение должно работать и на экране Вы увидите рис. 4 – это мои картинки, а в Вашем приложении должны быть Ваши картинки.
Рис. 4. Приложение с «картинками» во время исполнения
Упражнение 1.3. Определим условия выигрыша Подошло время поговорить о правилах и добиться того, что бы создатель «Однорукого бандита» всегда был в выигрыше. Обратите внимание на статусную строку рис. 2, где Вы видите какие-то непонятные цифры. Эти цифры и есть ключик к определению выигрыша. Собственно говоря, эта информация служебная и в готовом приложении её быть не должно, но на этапе отладки она бесценна. Правила игры Вы можете придумать самостоятельно, и они могут отличаться от тех, которые Вам здесь предлагаются. То, что мы сейчас будем обсуждать это только иллюстрация возможного подхода при назначении правил игры и определении суммы выигрыша. Своим автоматом мы по существу проверяем реакцию игрока – как быстро он может управлять потоком смены изображений в пяти окнах. Поэтому назначать выигрыш будем по количеству одинаковых изображений на форме. Подсчитать количество одинаковых изображений мы должны во время остановки, т.е. в текст функции Button1Click необходимо добавить код, выполняющий эту работу. В момент остановки неизвестно какие случайные числа были созданы функцией Timer1Timer, поэтому необходимо завести переменную AnsiString Value, где эти значения будут сохраняться. Проще всего описать её как экземпляр класса AnsiString в заголовочном файле и немного исправить функцию ImageShow(). void __fastcall TForm1::ImageShow(void) { int i; Value = “”; for( i=0; i<ComponentCount; i++ ) { TImage *img = dynamic_cast<TImage*>(Components[i]); if( img ) { FileName = ImageDir; AnsiString tmp = AnsiString( RandomRange(0,10) ); Value = Value + tmp; FileName = FileName + tmp + ".bmp"; img->Picture->LoadFromFile( FileName ); } } }
Теперь после каждого прерывания таймера в переменной Value будут сохраняться пять последних случайных цифр, которые можно проанализировать в функции Button1Click. Идея анализа проста. Опишем в заголовочном файле вне описания класса TForm1 константу const AnsiString Numbers = “0123456789”; и массив для хранения значения счётчиков каждой цифры int NumbersCount[10];. В функции Button1Click используем конструкцию if-then-else для определения состояния приложения – генерируются числа или нет. В соответствующем месте этой конструкции организуем цикл for, где с помощью функции Pos класса AnsiString в переменной Value ищется один из символов константы Numbers и если он присутствует, то в соответствующий счетчик NumbersCount добавляется 1, а сам символ удаляется из Value. В результате Вы увидите значение строки StatusBar1 такими, как на рис. 2. Последнее значение Value = «01734», цифра «0» встречается 1 раз – «0 – 1»; цифра «1» встречается 1 раз – «1 – 1»; цифра «2» не наблюдается – «2 – 0» и т.д. Сам ход анализа в коде может выглядеть приблизительно так, хотя возможны варианты. for( int i=0; i<Numbers.Lenght();NumbersCount[i++]=0 ); // Обнулим счетчики for( int i=0; i<Numbers.Lenght(); i++ ) { // а это анализ int pos = Value.Pos(Numbers.SubString(i,1)); if( pos ) { NumbersCount[i]++; Value.Delete(pos,1); } } Всё остальное необходимо сделать самостоятельно и получить готовое приложение с придуманными Вами правилами назначения выигрыша. Удачи…
Отчет о лабораторной работе Надеюсь, Вы успешно справились с заданием, и пришла пора подумать об отчёте и вспомнить преподавателя. Итак, в качестве отчетных материалов необходимо предоставить нижеследующее. 1. Красиво оформленные исходные тексты всех Ваших программ, выполненные в едином стиле, например, как в этом пособии. Не забывайте, что каждый файл должен обязательно содержать «шапку»:
/* Project Name: Лабораторная работа №1. Упражнение 1.1 File Name: UOneHandBand.cpp Create Date: 10.10.06 Modify Date: 15.10.06 Version: 1.0.0 Autor: Сидоров С.Д. Group: ЭиП-251 Files for link: UMain Comment: Игровой автомат */ Не забывайте комментировать программу. Все файлы каждого упражнения должны храниться в отдельных папках. Не забывайте, что преподаватель не будет смотреть объектный код – файлы с расширением obj и вспомогательный файл проекта – файл с расширением tds. Своевременно удаляете их. Кроме того, результаты последнего упражнения служат основой следующей лабораторной работы. Выполняйте его наиболее тщательно; 2. Загружаемые программы, собранные без подключения пакетов и без run-time библиотек. Всего таких файлов 3. Помните, что программа может находиться только в двух состояниях: 1) «работает» или 2) «не работает». Понятие «работает чуть-чуть» не существует, как не существует сыра второго сорта. Вы создавали, в некотором роде, художественные произведения. Поэтому побеспокойтесь об эстетическом восприятии Ваших шедевров: цветах, расположении на экране выразительных элементов, комментариях, подборе графических файлов и прочее, прочее. Не забывайте о минимизации операций пользователя Ваших программ и не заставляйте его лишний раз нажимать на кнопочки клавиатуры и «мышки». Отклонения от учебного пособия в сторону расширения функциональности последнего, наиболее полного приложения, внесение элементов настройки приложения, тематический подбор картинок всяческим образом приветствуется. 3. По результатам работы проводится зачетный тест.
ЛАБОРАТОРНАЯ РАБОТА №2. ДЕЛАЕМ ЗАСТАВКИ «Встречают по одежке, а провожают по уму» – известная поговорка, но встречают все же по одежке. При запуске Windows-приложений нередко появляется красивая заставка (в англоязычной литературе она называется Splash Screen), содержащая информацию о запускаемой программе, торговую марку, занимательный видеоклип и, возможно, иные сведения (серийный номер продукта, версию и пр.). Эта Лабораторная работа посвящена созданию именно таких заставок. Вся работа состоит из четырёх упражнений. 1. Создание простейшей заставки, которая появляется перед работой главной формы приложения и исчезает либо при нажатии клавиши, либо автоматически по истечении заданного интервала времени; 2. Вывод информации о приложении на заставке. Здесь на заставку выводится информация о приложении, которая хранится после его установки в реестре Windows; 3. Заставка с видеофрагментом; 4. Заставка с видеофрагментом, отображаемым на полном экране. Все упражнения надо выполнять последовательно, так как каждое следующее является естественным улучшением предыдущего, и Вы сможете экономить на написании кода отдельных проектов каждого упражнения. Основой наших упражнений станет последнее приложение из первой лабораторной работы. Скопируйте все необходимые файлы в новую папку и далее следуйте инструкциям.
Упражнение 2.1. Создание простейшей заставки Добавим в скопированный проект форму Form2 (Рис. 5). Свойство BorderStyle в инспекторе объектов установим равным bsNone, а свойство Position в poScreenCenter. Поместим на форму метки Label1 и Label2 для красивого заголовка с тенью, а также компонент TTimer. Свойство Interval компонента TTimer установим равным 1000, умноженное на число секунд, в течение которых заставка должна находиться на экране (значение 6000 соответствует 6 секундам).
Рис. 5. Заставка на этапе проектирования Создадим обработчик события OnTimer компонента TTimer. void __fastcall TForm2::Timer1Timer(TObject *Sender) { Close(); } Для того, что бы заставку можно было убрать нажатием любой клавиши, установим свойство KeyPrewiev формы в значение true и создадим обработчик события OnKeyPress формы. void __fastcall TForm2::FormKeyPress(TObject *Sender, char &Key) { Close(); } В функции создания формы позаботимся о позиционировании меток в центре формы, а для создания эффекта тени сместим одну надпись относительно другой на 2 пиксела вниз и вправо. void __fastcall TForm2::FormCreate(TObject *Sender) { ClientWidth = 798;
ClientHeight = 514; Label2->Left = ClientWidth/2 - Label2->Width/2; Label1->Left = Label2->Left + 2; Label2->Top = 30; Label1->Top = Label2->Top + 2; } В опциях настройки проекта перенесем вновь созданную форму в раздел Available Forms. Теперь создадим обработчик события OnCreate главной формы приложения. void __fastcall TForm1::FormCreate(TObject *Sender) { Form2= new TForm2(this); Form2->ShowModal(); Form2->Free(); } Смотрите, теперь перед запуском на экране будет появляться заставка (рис. 6), а при нажатии любой клавиши или по истечении 6 с она пропадёт, и появится главная форма вашего предыдущего проекта.
Рис. 6. Заставка на этапе выполнении
Упражнение 2.2. Вывод на заставке информации о приложении Если запустить какое–либо из приложений MS Office, на появляющейся при загрузке приложения заставке можно увидеть не только сведения о самом приложении, но также и регистрационный номер и фамилию пользователя. Сейчас подобные сведения принято хранить в реестре Windows. Если создать дистрибутив приложения с помощью наиболее часто используемого для этой цели средства InstallShield (или с помощью InstallShield Express, входящего в состав старших версий Delphi и C++Builder), в реестре Windows появится раздел, похожий на показанный на рис. 7. Пока Вы ещё не создали дистрибутива для своей модели главного приложения, воспользуйтесь программой regedit из системной папки Windows и найдите подходящие сведения для появления их в Вашей заставке. Для примера (рис. 7) воспользуемся значениями LicenseNumber и UserName приложение ACD Systems. Это приложение установлено на моём компьютере, а Вы может выбрать самое дорогое Вашему сердцу со своего. Если инсталляционное приложение создано не с помощью InstallShield, а с помощью иных средств (например, написано на Delphi или C++Builder), наименование раздела и ключей реестра могут отличаться от приведенных выше, но в любом случае они должны быть Вам известны заранее. Модифицируем нашу заставку так, чтобы на ней отображались имя пользователя и серийный номер из реестра. С этой целью добавим на созданную ранее форму две метки Label3 и Label4 (рис. 8).
Рис. 7. Раздел реестра, создаваемый программой установки
Рис. 8. Форма заставки после внесения изменений Создадим обработчик события, связанный с созданием формы, модифицировав ранее написанный код. void __fastcall TForm2::FormCreate(TObject *Sender) { TRegistry *Reg; try { Reg = new TRegistry; Reg->RootKey = HKEY_LOCAL_MACHINE; AnsiString key = "SOFTWARE\\ACD Systems\\22"; Reg->OpenKey( key, false ); AnsiString usr = Reg->ReadString("UserName"); AnsiString ser = Reg->ReadString("LicenseNumber"); Label3->Caption = "Пользователь: "+usr; Label4->Caption = "Серийный номер: "+ser; Reg->CloseKey(); Reg->Free();
} catch (...) { Label3->Caption = "Незарегистрированный пользователь"; Label4->Caption = "Пожалуйста регистрируйтесь…"; } Label3->Repaint(); Label4->Repaint(); ClientWidth = 798; ClientHeight = 514; Label2->Left = ClientWidth/2 - Label2->Width/2; Label1->Left = Label2->Left + 2; Label2->Top = 30; Label1->Top = Label2->Top + 2; } Естественно, в начале текста модуля следует вставить строку: #include <registry.hpp> В результате наша заставка на этапе выполнения в случае наличия в реестре соответствующего раздела приобретет следующий вид (Рис. 9).
Рис. 9. Заставка на этапе выполнения
Обратите внимание на то, что в коде появился непонятный для Вас блок try-catch – это, так называемый, механизм исключений. Если во время выполнения операторов блока try произойдет ошибка, в нашем случае ключ может отсутствовать в реестре, то системой генерируется исключение и выполняются операторы блока catch. Механизм исключений – наиболее эффективный способ отработки ошибок в Builder C++. Таким образом, если в реестре нет такого раздела, вместо имени пользователя и серийного номера будет выводиться сообщение о том, что пользователь не зарегистрирован. Отметим, что при желании можно добавить в этот обработчик события код, анализирующий серийный номер и в случае, если он не удовлетворяет каким-либо заранее заданным требованиям, прекратить выполнение приложения (или запустить его демонстрационную версию). Именно в этом заключается идея простейшей защиты своих авторских прав.
Упражнение 2.3. Видеофрагмент на заставке Теперь создадим заставку, в которой будет отображаться видеофрагмент. Такие заставки нередко используются разработчиками игр и мультимедийных энциклопедий. С этой целью удалим с формы таймер (он уже не нужен, так как время существования заставки теперь определяется длительностью видеофрагмента), и уничтожим связанный с ним обработчик события OnTimer. Удалим также компонент TImage. Затем добавим компонент TPanel, он будет использоваться нами в качестве "экрана" для воспроизведения видеофрагмента, и компонент TMediaPlayer (рис. 10). Свойство Visible этого компонента установим равным false, свойство AutoOpen – равным true, а в качестве значения свойства FileName выберем имя файла, содержащего видеофрагмент – Clock.avi, из системной папки Windows. Изменим обработчик события, связанного с созданием формы. Теперь мы должны инициировать воспроизведение фрагмента, используя в качестве экрана компонент TPanel. При этом компонент TPanel должен располагаться в центре формы и иметь размеры, соответствующие размерам кадров видеофрагмента (их можно узнать, обратившись к свойству DisplayRect компонента TMediaPlayer).
Рис. 10. Проектная форма заставки с видеофрагментом В случае C++Builder соответствующий код имеет вид. void __fastcall TForm2::FormCreate(TObject *Sender) { TRegistry *Reg; try { Reg = new TRegistry; Reg->RootKey = HKEY_LOCAL_MACHINE; AnsiString key = "SOFTWARE\\ACD Systems\\22"; Reg->OpenKey( key, false ); AnsiString usr = Reg->ReadString("UserName"); AnsiString ser = Reg->ReadString("LicenseNumber"); Label3->Caption = "Пользователь: "+usr; Label4->Caption = "Серийный номер: "+ser; Reg->CloseKey(); Reg->Free(); }
catch (...) { Label3->Caption = "Unregistered user "; Label4->Caption = "Please register "; } Label3->Repaint(); Label4->Repaint(); ClientWidth = 798; ClientHeight = 514; Label2->Left = ClientWidth/2 - Label2->Width/2; Label1->Left = Label2->Left + 2; Label2->Top = 5; Label1->Top = Label2->Top + 2; MediaPlayer1->Open(); MediaPlayer1->DisplayRect = Rect(0,0,0,0); Panel1->Width = MediaPlayer1->DisplayRect.Right; Panel1->Height = MediaPlayer1->DisplayRect.Bottom; Panel1->Left = (ClientWidth - Panel1->Width) / 2; MediaPlayer1->Play(); } Так как мы ликвидировали обработчик события, связанный с компонентом TTimer, наша форма теперь не закроется автоматически. Очевидно, что закрытие формы должно быть связано с моментом окончания воспроизведения видеофрагмента. С этой целью воспользуемся событием OnNotify компонента TMediaPlayer, в котором проанализируем свойство Mode – в момент окончания воспроизведения оно изменится. Код для C++Builder имеет при этом следующий нижеследующий вид. void __fastcall TForm2::MediaPlayer1Notify(TObject *Sender) { MediaPlayer1->Notify= true; if (MediaPlayer1->Mode==mpStopped) { Form2->Close(); } } На этапе выполнения мы получим заставку примерно такую, которая показана на рис. 11.
Рис. 11. Заставка, воспроизводящая видеофрагмент
Упражнение 2.4. Видеофрагмент на полном экране И, наконец, создадим заставку, воспроизводящую видеофрагмент на всем экране. В этом случае надписи на форме и данные из реестра уже не нужны, но при этом нужно растянуть на весь экран и форму, и компонент TPanel. Поэтому обработчик события, связанного с созданием формы, несколько упрощается. void __fastcall TForm2::FormCreate(TObject *Sender) { Left = 0; Top = 0; Width = Screen->Width; Height = Screen->Height; Panel1->Align = alClient; MediaPlayer1->Open(); MediaPlayer1->DisplayRect=Rect(0,0,Screen->Width,Screen->Height); MediaPlayer1->Play(); } Теперь форма, панель и сам видеофрагмент занимают весь экран, закрывая собой все окна, включая панель задач Windows. Тем не менее
рекомендуется для полностью корректного воспроизведения подобного рода приложений установить свойство FormStyle данной формы равным fsStayOnTop.
Рис. 12. Заставка, воспроизводящая видеофрагмент на полном экране
Отчет о лабораторной работе Надеюсь, Вы успешно справились с заданием и пришла пора подумать об отчёте. Итак, в качестве отчетных материалов по этой лабораторной работе Вам необходимо предоставить нижеследующее. 1. Красиво оформленные исходные тексты всех Ваших программ, выполненные в едином стиле. Не забывайте, что каждый файл должен обязательно содержать «шапку» (примет в лабораторной работе № 1). В «шапке» указываются: имя проекта, имя файла, номер версии, автор и его
учебная группа, комментарии с кратким описание модуля, все файлы для совместной сборки программы, даты создания и последней модификации кода. Не забывай комментировать текст программы. Все файлы каждого упражнения должны храниться в отдельных папках. Не забывайте, что преподаватель не будет смотреть объектный код – файлы с расширением obj и вспомогательный файл проекта с расширением tds. Своевременно удаляете эти файлы. Кроме того, результаты последнего упражнения служат основой следующей лабораторной работы. Выполняйте его наиболее тщательно; 2. Загружаемые программы, собранные без подключения пакетов и без run-time библиотек. Всего таких файлов 4. Отклонения от учебного пособия в сторону расширения функциональности, внесение элементов настройки приложения, тематический подбор картинок всяческим образом приветствуется. 3. По результатам работы проводится зачетный тест.
ЛАБОРАТОРНАЯ РАБОТА №3. ВНЕШНИЕ УСТРОЙСТВА И ОПЕРАЦИОННАЯ СИСТЕМА Приложение неспособное определять характеристики «железа», на котором оно угнездилось, неспособное адаптироваться во внешнем для себя мире операционной системы не есть современное и серьезное. И по сему, если Вы хотите создавать действительно профессиональные приложения для Windows, Вам придется познакомиться с функциями Windows API и освоить типовые операции работы с периферийными устройствами вычислительной системы. В этой лабораторной работе Вам необходимо выполнить шесть упражнений. 1. Определение наличия дискеты или компакт–диска в дисководе; 2. Определение параметров текущего видеорежима; 3. Получение сведений о частоте развертки монитора; 4. Инициирования перезагрузки Windows; 5. Отключения хранителя экрана и 6. Получения сведений об операционной системе и переменных окружения. Каждое упражнение – это отдельное приложение, которое Вам необходимо написать и предъявить на защите.
Упражнение 3.1. Наличие дискеты или компакт-диска в дисководе Прежде чем произвести запись на дискету или считать данные с компакт-диска во избежание возникновения сообщений об ошибке при отсутствии диска или дискеты, либо об отсутствии свободного места на диске следует определить, готово ли устройство, и каков объем свободного пространства на нем. Информацию о диске можно получить, используя функцию Windows API GetVolumeInformation. Эта функция возвращает информацию об имени и метке тома, а также характеристиках его файловой системы. Для получения информации о наличии свободного места на диске используем функцию GetDiskFreeSpace. Эта функция возвращает информацию о числе свободных кластеров и секторов и размерах сектора и кластера для данного диска. Число свободных байт равно числу свободных кластеров, умноженному на число секторов в кластере и на число байт в секторе. Создадим простейший пример, определяющий тип устройства, его готовность и количество свободных байт. На форму приложения поместим компонент TDriveComboBox со страницы Win31 и четыре метки.
Рис. 13. Форма для получения информации о дисках Создадим обработчик события OnChange компонента TDriveComboBox. В случае C++ Builder он выглядит следующим образом. void __fastcall TForm1::DriveComboBox1Change(TObject *Sender) { DWORD VolSN,MaxCompLen,FSFlags, FC,SPC,BPS,NC; char VolName[255],FSName[100]; AnsiString s=UpperCase(DriveComboBox1->Drive)+":\\"; Label1->Caption=s; if(GetVolumeInformation(s.c_str(),VolName,255,&VolSN,&MaxComp Len, &FSFlags,FSName,100)) { Label2->Caption="Имя тома: "+AnsiString(VolName); Label3->Caption="Файловая система: "+AnsiString(FSName); if (GetDiskFreeSpace(s.c_str(), &SPC,&FC,&BPS,&NC)) { Label4->Caption="Свободных байт: "+IntToStr(SPC*FC*BPS); } else { Label4->Caption=""; } } else { Label2->Caption="Диск не готов"; Label3->Caption=""; Label4->Caption=""; } }
Запустив приложение, мы можем выбирать устройство из списка доступных устройств и получать информацию о готовности устройства, метке тома и числе свободных байт.
Рис. 14. Приложение на этапе выполнения
Упражнение 3.2. Параметры текущего видеорежима Приложения, активно использующие работу с графикой или видеофрагментами, нередко нуждаются в информации о текущем цветовом разрешении видеоадаптера. Такие сведения можно получить, применив функцию Windows API GetDeviceCaps, использующую в качестве входного параметра контекст графического устройства (каковым является экран). В данном примере мы воспользуемся обычной формой VCL для доступа к этому контексту, используя ее свойство Canvas. В качестве второго параметра этой функции используется целая константа, в зависимости от значения которой возвращается одно из значений, связанных с параметрами графического устройства. В данном случае нас интересуют цветовое разрешение в битах и число поддерживаемых системой цветов. Последнее зависит от числа оттенков цвета. Число оттенков равно 2 в степени, равной цветовому разрешению в битах. Число же поддерживаемых системой цветов равно числу оттенков, возведенному в степень, равную числу цветовых плоскостей. Следовательно, для вычисления числа поддерживаемых цветов функцию GetDeviceCaps следует вызвать дважды: один раз для получения информации о цветовом разрешении, другой раз – о числе цветовых плоскостей. Создадим простейшее приложение для определения этих параметров. С этой целью поместим на форму кнопку и несколько меток.
Рис. 15. Форма приложения для определения цветового разрешения экрана
Создадим обработчик события, связанный с нажатием на кнопку. В C++Builder он выглядит следующим образом. void __fastcall TForm1::Button1Click(TObject *Sender) { long int TC,BPP,CP,VR; BPP=GetDeviceCaps(Form1->Canvas->Handle,BITSPIXEL); CP=GetDeviceCaps(Form1->Canvas->Handle,PLANES); TC=pow(pow(2,BPP),CP); Label1->Caption="Бит на пиксел: "+IntToStr(BPP); if (TC<3) { Label2->Caption="Монохромный дисплей "; } else { Label2->Caption="Число цветов: "+IntToStr(TC); } } Запущенное приложение выглядит примерно так, как на рис. 16.
Рис. 16. Приложение для определения цветового разрешения экрана
Помимо цветового разрешения и числа поддерживаемых цветов c помощью функции GetDeviceCaps можно получить много другой информации о графических устройствах, например, о разрешении в пикселах на дюйм, числе поддерживаемых шрифтов и др. При работе в Windows NT можно даже определить частоту развертки монитора.
Упражнение 3.3. Сведения о частоте развертки монитора Модифицируем ранее созданный пример, добавив определение типа операционной системы, и, если это Windows NT (2000, ХР) – определение частоты развертки. С этой целью необходимо в первую очередь определить тип операционной системы. Как определить тип операционной системы? Для этой цели воспользуемся функцией Windows API GetVersionEx. Но, прежде чем ее вызвать, следует создать структуру типа OSVERSIONINFO, содержащую сведения об имении версии операционной системы (подробное описание этой структуры можно найти в справке по функциям Windows API, поставляемой с средствами разработки), и корректно определить ее размер с помощью функции sizeof. Поместим еще одну метку на форму нашего примера и изменим обработчик события, связанного с нажатием на кнопку. В случае C++Builder он теперь имеет нижеследующий вид. void __fastcall TForm1::Button1Click(TObject *Sender) { long int TC,BPP,CP,VR; OSVERSIONINFO OV; BPP=GetDeviceCaps(Form1->Canvas->Handle,BITSPIXEL); CP=GetDeviceCaps(Form1->Canvas->Handle,PLANES); TC=pow(pow(2,BPP),CP); Label1->Caption="Бит на пиксел: "+IntToStr(BPP); if (TC<3) { Label2->Caption="Монохромный дисплей "; } else { Label2->Caption="Число цветов: "+IntToStr(TC); }; OV.dwOSVersionInfoSize=sizeof(OV); GetVersionEx(&OV); if (OV.dwPlatformId==VER_PLATFORM_WIN32_NT) { VR=GetDeviceCaps(Form1->Canvas->Handle,VREFRESH);
Label3->Caption="Частота развертки: "+IntToStr(VR)+" Гц"; } else { Label3->Caption="Частота развертки не определется в данной ОС"; } } Если запустить это приложение в операционной системе Windows NT, оно отобразит сведения о частоте развертки (рис. 17). Рис. 17. Приложение для определения частоты развертки под управлением Windows NT
Упражнение 3.4. Инициирование перезагрузки Windows Иногда в процессе работы приложения следует произвести перезагрузку операционной системы. Это бывает необходимо, если в процессе работы приложения были произведены изменения в переменных окружения, изменено сетевое имя компьютера, и др. Для перезагрузки операционной системы можно использовать функцию Windows API ExitWindowsEx, первый из параметров которой определяет способ завершения работы Windows 95/98. Создадим простейший пример, использующий эту функцию. Для этого на форму приложения поместим три кнопки (рис. 18).
Рис. 18. Форма приложения для инициирования перезагрузки Windows
Создадим обработчики событий, связанных с нажатием на кнопки. void __fastcall TForm1::Button1Click(TObject *Sender) { ExitWindowsEx(EWX_LOGOFF,0); } //--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender) { ExitWindowsEx(EWX_SHUTDOWN,0); } //--------------------------------------------------------------------------void __fastcall TForm1::Button3Click(TObject *Sender) { ExitWindowsEx(EWX_REBOOT,0); } Запустив приложение, мы можем выбрать способ завершения работы операционной системы или сеанса пользователя Windows. Отметим, что завершение работы или сеанса пользователя вWindows 95/98 действительно может быть осуществлено всеми тремя способами. В Windows NT же в общем случае работоспособен только первый обработчик события. Дело в том, что в соответствии с соображениями безопасности не всякое приложение имеет право произвести завершение работы Windows NT (например, хотя бы потому, что под управлением этой операционной системы могут выполняться различные сетевые и иные сервисы, жизненно важные для функционирования других компьютеров сети). Если же, тем не менее, возникнет необходимость произвести завершение работы Windows NT, а не просто завершение сеанса пользователя, следует воспользоваться функцией Windows API AdjustTokenPrivileges для предоставления данному приложению права остановки или перезагрузки операционной системы.
Упражнение 3.5. Отключение хранителя экрана Нередко разработчики приложений, выполняющих какие–либо длительные действия (например, обработку большого количества данных или пересылку данных на удаленный компьютер с помощью
низкоскоростной линии связи), сталкиваются с проблемами, создаваемыми так называемыми хранителями экрана (screen savers). Хранители экрана представляют собой, по существу, такие же Windows–приложения, нередко потребляющие немалое количество самых разнообразных ресурсов, за исключением собственно люминофора, которым покрыта внутренняя поверхность экрана монитора. Поэтому при запущенном хранителе экрана в общем случае производительность любого другого приложения, выполняющего какие–либо длительные действия, может существенно снизиться. Чтобы понять, как предотвратить запуск хранителя экрана, следует вспомнить, что операционные системы с графическим пользовательским интерфейсом, такими как Windows, основаны на посылке и обработке сообщений, а также на обработке событий. Windows постоянно обрабатывает разнообразные события, в том числе события, связанные с перемещением «мыши» или нажатием клавиш. Хранитель экрана запускается операционной системой, если по истечении заранее заданного, т.е. установленного пользователем операционной системы, промежутка времени не происходило двух указанных выше типов событий. Для этого операционная система посылает активному приложению сообщение WM_SYSCOMMAND с параметром WPARAM, равным SC_SCREENSAVE. Именно это сообщение и следует перехватить в нашем приложении. Перехват сообщения можно осуществить создав обработчик события OnMessage объекта TApplication, наступающего при получении приложением очередного сообщения Windows. Соответствующая функция имеет два параметра: имя сообщения и параметр handled, указывающий, обрабатывается ли такое сообщение данным приложением. Если он установлен равным false, сообщение обрабатывается операционной системой. В противном случае оно обрабатывается приложением. Создадим простейший пример, иллюстрирующий перехват такого сообщения. Для этого поместим на форму радиогруппу (компонент TRadioGroup) из двух элементов (рис. 19). Рис. 19. Форма приложения для отключения хранителя экрана
Теперь создадим функцию-компонент класса TForm1 (для этого есть специальный эксперт, встроенный в Code Explorer, однако, полезнее создать определение функции вручную, что Вы уже делали в первых лабораторных работах). Создадим текст этой функции, а также обработчик события, связанный с созданием формы приложения и указывающий, какая функция должна вызываться при получении сообщения данным приложением. void __fastcall TForm1::ProcessMess(TMsg & msg, bool &handled) { //TODO: Add your source code here if (msg.message==WM_SYSCOMMAND && msg.wParam==SC_SCREENSAVE && RadioGroup1->ItemIndex==1) handled=true; else handled=false; } //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Application->OnMessage = ProcessMess; } Если функция создана вручную, следует не забыть создать ее определение в h-файле. class TForm1 : public TForm { __published: // IDE-managed Components TRadioGroup *RadioGroup1; void __fastcall FormCreate(TObject *Sender); private: void __fastcall ProcessMess(TMsg & msg, bool &handled); public: // User declarations __fastcall TForm1(TComponent* Owner); };
Рис. 20. Эксперт для создания функции–компонента Если созданное таким образом приложение активно, хранитель экрана будет запускаться (или не запускаться) в зависимости от того, какая опция выбрана в радиогруппе.
Упражнение 3.6. Получение сведений об операционной системе Нередко при разработке приложений требуется определить, какова версия операционной системы, где расположены системные каталоги, каковы значения переменных окружения. Для этой цели используются функции Windows API, такие как GetVersionEx (определение типа операционной системы), GetComputerName(определение сетевого имени компьютера), GetUserName (определение имени пользователя), GetSystemDirectory (определение местоположения системного каталога), GetWindowsDirectory (определение каталога Windows),
ExpandEnvironmentStrings (определение значений переменных окружения), а также структуры Windows (например, OSVERSIONINFO). Подробные сведения об этих функциях и структурах содержатся в справке по Windows API, входящей в комплект поставки как C++Builder, так и Delphi. Приведем простейший пример использования некоторых из этих функций, являющийся в значительной степени вариацией на тему стандартных примеров использования Windows API. Поместим на главную форму вновь созданного приложения несколько меток и кнопку и создадим обработчик события, связанный с нажатием на кнопку и отображающий системную информацию в метках: void __fastcall TForm1::Button1Click(TObject *Sender) { LPTSTR lpszSystemInfo; DWORD cchBuff = 256; TCHAR tchBuffer2[1000]; DWORD dwResult; TCHAR tchBuffer[1000]; int nSize; lpszSystemInfo = tchBuffer2; GetComputerName(lpszSystemInfo, &cchBuff); nSize = sprintf(tchBuffer, "Компьютер: %s", lpszSystemInfo); Label1->Caption=AnsiString(tchBuffer); GetUserName(lpszSystemInfo, &cchBuff); nSize = sprintf(tchBuffer, "Пользователь: %s", lpszSystemInfo); Label2->Caption=AnsiString(tchBuffer); nSize = GetSystemDirectory(lpszSystemInfo, MAX_PATH); nSize = sprintf(tchBuffer, "System directory: %s", lpszSystemInfo); Label3->Caption=AnsiString(tchBuffer); nSize = GetWindowsDirectory(lpszSystemInfo, MAX_PATH); nSize = sprintf(tchBuffer, "Windows directory: %s", lpszSystemInfo); Label4->Caption=AnsiString(tchBuffer); Label5->Caption="Переменные окружения :"; dwResult = ExpandEnvironmentStrings("OS=%OS% ", lpszSystemInfo, 500); Label6->Caption=AnsiString(lpszSystemInfo);
dwResult= ExpandEnvironmentStrings("INCLUDE=%INCLUDE%",lpszSystemInfo,500); Label7->Caption=AnsiString(lpszSystemInfo); dwResult= ExpandEnvironmentStrings("CLASSPATH=%CLASSPATH%",lpszSystemInfo,5 00); Label8->Caption=AnsiString(lpszSystemInfo); dwResult= ExpandEnvironmentStrings("ComSpec=%ComSpec%",lpszSystemInfo,500); Label9->Caption=AnsiString(lpszSystemInfo); OSVERSIONINFO osvi; char szVersion [80]; memset(&osvi, 0, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); GetVersionEx (&osvi); if (osvi.dwPlatformId == VER_PLATFORM_WIN32s) wsprintf (szVersion, "Microsoft Win32s %d.%d (Build %d)", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); else if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) wsprintf (szVersion, "Microsoft Windows 95 %d.%d (Build %d)", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); else if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) wsprintf (szVersion, "Microsoft Windows NT %d.%d (Build %d)", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); Label10->Caption="Операционная система: "+AnsiString(szVersion); } Если запустить это приложение, оно отобразит сведения о системе и переменных окружения (рис. 21).
Отметим, что в настоящее время имеется немалое количество свободно распространяемых, условно-бесплатных и коммерческих компонентов для Delphi и C++Builder, предназначенных для получения системной информации. Все они так или иначе используют те же самые функции Windows API для установки или считывания значений своих свойств или выполнения методов, и основное их преимущество заключается главным образом в том, что они избавляют программиста от необходимости создания кода, подобного приведенному выше. Соответственно при разработке приложений можно использовать как непосредственно функции Windows API, как в приведенных выше примерах, так и готовые компоненты.
Рис. 21. Приложение для вывода сведений о системе
Отчет о лабораторной работе Надеюсь, Вы успешно справились с заданием и пришла пора подумать об отчёте. Итак, в качестве отчетных материалов по лабораторной работе № 3 Вам необходимо предоставить нижеследующие материалы. 1. Красиво оформленные исходные тексты всех Ваших программ, выполненные в едином стиле. Не забывайте, что каждый файл должен обязательно содержать «шапку» (примет в лабораторной работе № 1), в которой указываются имя проекта, имя файла, номер версии, автор и его учебная группа, комментарии с кратким описание модуля, все файлы для совместной сборки программы, даты создания и последней модификации кода. Не забывай комментировать текст программы. Все файлы каждого
упражнения должны храниться в отдельных папках. Не забывайте, что преподаватель не будет смотреть объектный код – файлы с расширением obj и вспомогательный файл проекта с расширением tds. Своевременно удаляете эти файлы. Кроме того, результаты последнего упражнения служат основой следующей лабораторной работы. Выполняйте его наиболее тщательно; 2. Загружаемые программы, собранные без подключения пакетов и без run-time библиотек. Всего таких файлов 6. Отклонения от учебного пособия в сторону расширения функциональности, внесение элементов настройки приложений, тематический подбор картинок всяческим образом приветствуется. Более того, Вам предлагается объединить все эти приложения в одно и на главной форме поместить компонент TMainMenu, с помощь которого организуется вызов всех, написанных Вами функций. 3. По результатам работы проводится зачетный тест.
ЛАБОРАТОРНАЯ РАБОТА №4. БАЗЫ ДАННЫХ, BORLAND DATABASE ENGINE (BDE). НАСТРОЙКА ПАРАМЕТРОВ ДОСТУПА. БИБЛИОТЕКА BORLAND DATABASE ENGINE (BDE). УТИЛИТА НАСТРОЙКИ BDE Если созданное с помощью С++ Builder приложение в процессе работы обращается к базам данных, оно, как правило, использует для этой цели библиотеку BDE (Borland Database Engine), основанную на технологии IDAPI (Integrated Database Application Program Interface). Архитектуру этой технологии Вы можете видите на рис. 22. Эта библиотека устанавливается автоматически при установке С++ Builder. По умолчанию она устанавливается в каталог C:\Program Files\Borland\Common Files\BDE. Следует отметить, что файлы, входящие в состав библиотеки BDE, предназначены для использования не только приложениями, созданными с помощью С++ Builder, но и многими другими продуктами фирмы Borland (Visual dBase, Paradox, Delphi, Borland C++, IntraBuilder), созданными на их основе приложениями, а также офисными приложениями Corel (например, электронной таблицей Quattro Pro), генератором отчетов Crystal Reports (Seagate Software) . Поэтому при наличии нескольких использующих BDE приложений все 32-разрядные приложения (в том числе C++ Builder) используют установленную последней 32-разрядную версию BDE. В этом случае по умолчанию программа установки C++ Builder предложит поставить BDE в каталог, где установлена уже используемая версия BDE. BDE обеспечивает для созданных приложений: o непосредственный доступ к локальным базам данных (dBase, Paradox, текстовые файлы); o доступ к SQL-серверам (Oracle, Sybase, MS SQL Server, InterBase, Informix, DB2) с помощью драйверов Borland SQL Links; o доступ к любым источникам данных, имеющим драйвер ODBC (Open DataBase Connectivity), например, к файлам электронных таблиц (Excel, Lotus 1–2–3), серверам баз данных, не имеющим драйверов SQL Links (например, Gupta/Centura); o создание приложений клиент–сервер, использующих разнородные данные; o высокую производительность при работе с плоскими таблицами; o использование SQL (Structured Query Language – язык запросов к серверным СУБД), в том числе при работе с локальными данными; o изоляцию приложения от средств языковой поддержки; o изоляцию приложения от конфигурации системы и сети.
При возникновении необходимости доступа к данным в большинстве случаев для их источника (как правило, это какая-либо конкретная база данных) создается псевдоним (alias), имя которого используется приложением. Параметры этого и других псевдонимов, а также параметры настройки драйверов баз данных содержатся в файле idapi32.cfg, расположенном в том же каталоге, что и файлы BDE. Для изменения содержания этого файла, а также значений соответствующих этой библиотеке ключей реестра Windows 95 и Windows NT используется утилита конфигурации BDE – BDECFG32.EXE. Утилита конфигурации состоит из одной формы, содержащей блокнот из шести страниц. 1. Страница Drivers содержит параметры доступа к различным типам данных. 2. Страница Aliases содержит сведения о псевдонимах источников данных, к которым посредством BDE обращаются приложения, установленные на данном компьютере. 3. Страница System содержит параметры настройки работы самой библиотеки BDE, связанные с использованием памяти, сетевого окружения и других ресурсов, доступных использующим BDE приложениям. Эти параметры содержатся в реестре Windows 95 (или Windows NT). 4. Три последних страницы – Date, Time, Number, содержат правила отображения в приложениях календарных дат, времени и числовых данных в соответствии с правилами, принятыми в той или иной стране.
Рис. 22. Архитектура BDE Для настройки драйверов баз данных используется страница Drivers утилиты настройки BDE. В левой части этой страницы имеется список доступных для BDE драйверов, куда входят драйверы для dBase и Paradox, установленные на данном компьютере драйверы SQL Links для доступа к серверным СУБД, а также имена ODBC-источников данных, созданные с помощью 32–разрядного администратора ODBC панели управления Windows (рис. 23). При нажатии на кнопку New ODBC Driver можно добавить в список новый ODBC-источник данных (перед этим, естественно, следует установить соответствующий ODBC-драйвер и описать источник данных с помощью ODBC–администратора в панели управления Windows). Можно также удалить ODBC-драйвер из файла конфигурации BDE (Delete ODBC Driver). В правой части страницы Drivers указаны параметры выбранного в списке драйвера. При настройке драйверов следует заменить параметры, указанные по умолчанию, на значения, специфические для выбранного источника данных (например, языковый драйвер, правила обработки запросов, имя сервера и базы данных, тип сетевого протокола для доступа
к серверу, номер версии). Эти наборы параметров различны для различных драйверов баз данных. Для доступа приложений к данным средства разработки Borland используют механизм псевдонимов, описывающих доступ к конкретным источникам данных. Создать описание нового источника данных можно, нажав кнопку New Alias в левой части окна (рис. 24 и 25).
Рис. 23. Настройка драйверов баз данных
Рис. 24. Выбор псевдонима БД и установка параметров После этого в появившейся диалоговой панели следует ввести имяпсевдоним для этого источника и выбрать нужный драйвер из предложенного списка (того, что определен на странице Drivers). Имя STANDARD в этом случае соответствует таблицам dBase, Paradox и текстовым файлам (в формате CSV – Comma Separated Value). В правой части страницы Aliases содержатся параметры конкретных источников данных, которые можно модифицировать, заменяя значения по умолчанию (в том числе унаследованные с предыдущей страницы), например, имена каталогов, серверов, имя пользователя по умолчанию, языковый драйвер и др.
Рис. 25. Создание нового псевдонима БД Страница системных настроек позволяет указать параметры системы и сети, используемые BDE и хранящиеся в реестре Windows (рис. 26, табл.1).
Рис. 26. Страница System утилиты конфигурации BDE
Таблица 1 Параметры системы и сети, используемые BDE
Параметр
Описание
VERSION
версия BDE Если этот параметр равен true, можно использовать одни и те же данные приложениями, использующими BDE, и приложениями, не использующими BDE Минимальный размер буфера оперативной памяти для кэширования данных из БД. Возможные значения – от 32 до 65535 Кб, но не более, чем объем доступной Windows оперативной памяти Максимальный размер буфера оперативной памяти для кэширования данных из БД. Должен быть выше, чем MINBUFSIZE, но не более, чем объем доступной Windows оперативной памяти. Должен быть кратен 128. Языковый драйвер, соответствующий национальной версии операционной системы Максимальное число файлов, открываемых BDE. Может принимать целые значения от 5 до 256 Внутренний параметр BDE Максимально допустимый объем основной памяти, используемый BDE
LOCAL SHARE
MINBUFSIZE
MAXBUFSIZE
LANGDRIVER
MAXFILEHANDLES SYSFLAGS LOW MEMORY USAGE LIMIT
Значение по умолчанию
FALSE
2048
48
32
AUTO ODBC
DEFAULT DRIVER
SQLQRYMODE
SHAREDMEMSIZE
SHAREDMEMLOCATION
если этот параметр принимает значение true, используются все ODBCисточники из файла ODBC.INI драйвер, используемый первым, если тип БД – FILE, и имя таблицы не имеет расширения. Метод исполнения запросов к серверам. Может принимать значения NULL, SERVER, LOCAL Максимальный размер для разделяемой оперативной памяти Предпочтительный адрес для размещения разделяемой области памяти.
FALSE
NULL
2048 E000 (Windows 95) 7000(Windows NT)
Страница Date позволяет указать параметры отображения дат (табл. 2).
Таблица 2 Параметры настройки отображения дат
Параметр
SEPARATOR
MODE
FOURDIGITYEAR
YEARBIASED
LEADINGZEROM
LEADINGZEROD
Описание
Символ-разделитель числа, месяца и года Параметр, определяющий порядок следования числа. месяца и года. Может принимать значения: 0(MDY), 1( DMY), 2(YMD). Параметр, определяющий отображаемое число цифр года. Может принимать значения: TRUE (4 цифры), FALSE (2 цифры) Параметр, определяющий, прибавлять или нет 1900 к значению года, если предыдущий параметр равен FALSE Параметр, определяющий, указывать ли лидирующие нули перед значением месяца, если оно является однозначным числом. Параметр, определяющий, указывать ли лидирующие нули перед значением числа, если оно является однозначным числом.
Значение по умолчанию Значение, содержащееся в настройках панели управления Windows 95/NT Значение, содержащееся в настройках панели управления Windows 95/NT
TRUE
TRUE
FALSE
FALSE
Страница Time позволяет указать параметры отображения времени. Параметры настройки на этой странице смотри в табл. 3.
Таблица 3 Параметры настройки отображения времени
Параметр TWELVEHOU R AMSTRING
PMSTRING SECONDS MILSECONDS
Описание Параметр, определяющий интервал отображения часов: 0–12 (TRUE) или 0– 24 (FALSE) Символьная строка для указания первой половины дня, если TWELVEHOUR=TRUE Символьная строка для указания второй половины дня, если TWELVEHOUR=TRUE Параметр, определяющий, указывать ли секунды в значении времени Параметр, определяющий, указывать ли миллисекунды в значении времени .
Значение по умолчанию TRUE
AM
PM TRUE FALSE
Страница Number позволяет указать параметры отображения числовых данных (табл. 4). Средства языковой настройки BDE представляют собой набор так называемых языковых драйверов, устанавливающих набор символов, используемых в данных, и порядок алфавитной сортировки для используемого языка. Языковые драйверы Borland существуют для большого количества языков, в том числе и для русского. При описании параметров драйвера баз данных (страница Drivers) следует указывать языковый драйвер, предлагаемый для баз данных этого типа по умолчанию. При создании нового псевдонима (страница Aliases) следует указывать, какой языковый драйвер используется в базе данных, иначе будет использоваться языковый драйвер, установленный по умолчанию для данного драйвера баз данных. Отметим, что в общем случае языковая настройка баз данных и использующих их приложений включает в себя не только настройку параметра LANGDRIVER на страницах Drivers, Aliases и System. Некоторые СУБД имеют свои средства языковой настройки, причем эти средства могут быть многоуровневыми. Например, в случае использования какой-либо серверной СУБД может потребоваться, помимо настройки BDE, настройка языковых параметров сервера, настройка аналогичных
параметров клиентской части или даже выбор соответствующей языковой Таблица 4 Параметры настройки отображения чисел
Параметр
DECIMALSEPARATOR
THOUSANDSEPARATO R
DECIMALDIGITS
LEADINGZERON
Значение по умолчанию Значение, содержащееся в Символ, отделяющий настройках дробную часть числа панели управления Windows 95/NT Значение, содержащееся в Символ, отделяющий друг настройках от друга "тройки" разрядов в панели многозначных числах управления Windows 95/NT Максимальное число десятичных разрядов числа, полученного при 2 преобразовании символьной строки Параметр, определяющий, указывать ли нуль перед TRUE дробной частью числа, чье абсолютное значение меньше единицы Описание
версии сервера. Подробнее эти проблемы будут рассмотрены чуть позже. Помимо этого, существует проблема, связанная с различиями DOS- и Windows-кодировок для русского языка. Языковые драйверы BDE существуют для обеих кодировок. Какой из них выбрать, зависит от многих факторов: наличия и объема унаследованных данных, наличия других приложений, использующих эти же данные и др. Например, разумно, используя таблицы формата dBase III совместно с приложениями для DOS, созданными на Clipper, применить DOS-кодировку для этих таблиц.
Особенности настройки BDE для работы с некоторыми источниками данных
dBase При работе с таблицами dBase настройка соответствующего драйвера Таблица 5 Настройки dBase
Параметр VERSION TYPE LANGDRIVER LEVEL MDX BLOCK SIZE MEMO FILE BLOCK SIZE
Описание Внутренний параметр BDE Тип сервера (SQL–сервер или файловый сервер) Языковый драйвер, определяющий набор символов и порядок алфавитной сортировки Версия формата dBase при создании таблиц Размер блока, отводимого под индексные файлы *.mdx. Может быть целым числом, кратным 512 K Размер блока, отводимого под файлы memo-полей *.dbt. Может быть целым числом, кратным 512 K
Значение по умолчанию 1.0 FILE dBASE ENU cp437 5 1024
1024
сводится к настройке следующих параметров (табл. 5). При установке параметров псевдонима нужно указать значение параметра PATH – пути к каталогу, где расположены таблицы базы данных (это может быть и сетевой диск). С языковой настройкой таблиц dBase обычно не возникает проблем. Для DOS-кодировки обычно используется языковый драйвер dBASE RUS cp866, а для Windows – 'ascii' ANSI. Следует отметить, что к таблицам dBase можно обращаться, используя ODBC. Особое внимание следует обратить на то, что расширение *.dbf имеют не только таблицы формата dBase, но и таблицы Clipper, FoxBase, FoxPro. Однако эти СУБД могут иметь другой формат индексных файлов и мемополей, которые в явном виде могут не поддерживаться библиотекой BDE (например, индексы *.cdx). В этом случае рекомендуется либо доступ через ODBC (что не всегда эффективно с точки зрения производительности), либо использование библиотек третьих фирм, обеспечивающих интерфейс с такими СУБД (например, Apollo компании SuccessWare).
Paradox При работе с таблицами Paradox настройка соответствующего драйвера Таблица 6 Настройки Paradox
Параметр
Описание
VERSION
Внутренний параметр BDE Тип сервера (SQL–сервер или файловый сервер) Местоположение файла PDOXUSRS.NET, управляющего совместным использованием таблиц Paradox в сети. Языковый драйвер, определяющий набор символов и порядок алфавитной сортировки Версия формата Paradox при создании таблиц (3,4,5 или 7) Размер блока, отводимого для хранения записей таблиц Paradox. Может быть целым числом, кратным 1024 K. Возможные значения зависят от значения параметра LEVEL. Максимальный процент заполнения блока для индексных файлов. Параметр, определяющий, могут ли таблицы Paradox модифицироваться приложениями, не поддерживающими ссылочную целостность.
TYPE
NET DIR
LANGDRIVER LEVEL
BLOCK SIZE
FILL FACTOR STRICTINTEGRT Y
Значение по умолчанию 1.0 FILE
FILE
'ascii'ANSI 5
2048
95
TRUE
сводится к настройке следующих параметров (табл. 6). Как и в случае dBase, русские языковые драйверы для Paradox существуют для обеих кодировок – и DOS, и Windows. Однако указание на используемый язык содержится еще и внутри самих таблиц. Поэтому при создании таблицы Paradox (например, с помощью Database Desktop) следует обязательно указать используемый языковый драйвер при описании свойств таблицы. Как показывает опыт, при неверном определении языка таблицы могут возникнуть проблемы при последующем вводе в нее данных, содержащих русские буквы – некоторые
из них после выхода из редактируемого поля могут превратиться в латинские.
ODBC-источники При работе с ODBC-источниками требуется настройка следующих параметров (табл. 7). Таблица 7 Настройки ODBC-источников
Параметр
Описание
Значение по умолчанию 1.0 FILE
Внутренний параметр BDE Идентификатор ODBC-источника Имя 16-разрядной динамической DLL IDODBC16.DLL библиотеки, содержащей драйвер Имя 32-разрядной динамической DLL32 IDODBC32.DLL библиотеки, содержащей драйвер ODBC-драйвер для соединения с ODBC DRIVER сервером DRIVER FLAGS Внутренний параметр BDE Имя пользователя в диалоге ввода USER NAME пароля Имя источника данных, описанного в ODBS DSN администраторе ODBC Параметр, определяющий, в каком OPEN MODE режиме открываются таблицы – READ/WRITE READ/WRITE или READ ONLY Языковый драйвер, определяющий LANGDRIVER набор символов и порядок алфавитной 'ascii'ANSI сортировки Число таблиц, чья структура SCHEMA кэшируется. Возможные значения – от 8 CASHE SIZE 0 до 32 Метод выполнения запросов. Возможные значения: LOCAL – запрос обрабатывается только клиентским приложением, SERVER – запрос SQLQRYMODE NULL выполняется только сервером, NULL (пустая строка) – запрос передается клиенту, если сервер не может его обработать. VERSION TYPE
Определяет режим совместного использования одного и того же псевдонима направляемыми на сервер и локальными запросами: NOT SHARED – совместное использование запрещено, SHARED SQLPASSTHRU AUTOCOMMIT – совместное SHARED MODE использованием разрешено с AUTOCOMMIT автоматическим завершением транзакций, SHARED NOAUTOCOMMIT – совместное использованием разрешено с завершением транзакций по правилам сервера. Численное значение, определяющее TRACE MODE уровень вывода отладочной информации. Время нахождения информации о структуре таблиц в кэше в секундах от SCHEMA -1 1 до 2147483647. Другие значения: -1 – CACHE TIME до закрытия БД, 0 – информация не кэшируется Число записей, Число записей, помещаемых в пакет умещающихся в BATCH COUNT до завершения транзакции 32 К. Максимальное число записей, которые драйвер может доставить на рабочую -1 (нет MAX ROWS станцию при выполнении одиночного ограничений) SQL-запроса Число записей, доставляемых в одном ROWSET SIZE блоке данных (поддерживается не 20 всеми ODBC- драйверами). При создании псевдонимов баз данных на страницу Aliases по умолчанию заносятся параметры со страницы Drivers для соответствующего ODBC-драйвера. При необходимости многие из них можно переопределить. Ряд ODBC-источников требует указания параметра PATH – пути к каталогу, где находится база данных. Следует обратить внимание на то, что перед описанием ODBCисточника в файле конфигурации BDE обязательно нужно установить соответствующий ODBC-драйвер и описать соответствующий источник данных в панели управления Windows 95/NT, используя соответствующий
ODBC-администратор. При этом следует обратить внимание на некоторую терминологическую неувязку. Дело в том, что ODBC-драйвер, создаваемый при нажатии кнопки New ODBC Driver на странице Drivers утилиты конфигурации BDE, с точки зрения BDE на самом деле представляет собой указание не на реальный ODBC-драйвер, установленный в панели управления Windows, а на конкретный источник данных. Доступ к этому источнику осуществляется с помощью реального ODBC-драйвера (с точки зрения панели управления). При этом следует еще создать и соответствующий псевдоним базы данных, что окончательно сбивает с толку некоторых начинающих пользователей. Таким образом, последовательность действий при осуществлении доступа к ODBC-источникам следующая: 1) установить нужный ODBC-драйвер (и, возможно, соответствующий ODBC-администратор для панели управления Windows); 2) описать с помощью ODBC-администратора необходимый источник данных в панели управления; 3) запустить утилиту конфигурации BDE и нажать кнопку New ODBC Driver на странице Drivers; 4) придумать и ввести имя так называемого ODBC-драйвера с точки зрения BDE; 5) выбрать "настоящий" ODBC-драйвер из установленных в операционной системе; 6) выбрать имя источника данных; 7) нажать OK. В списке драйверов появится новый, так называемый, ODBC-драйвер (с точки зрения BDE); 8) перейти на страницу Aliases и создать псевдоним, связанный со вновь созданным драйвером с точки зрения BDE.
Рис. 27. Описание нового ODBC-драйвера "с точки зрения BDE
Отметим, что в новых версиях BDE вся эта терминологическая путаница ликвидирована, а все описанные в реестре Windows источники данных добавляются в список псевдонимов, и тем самым ликвидируется необходимость выполнения описанной выше инструкции. Для корректного отображения русских букв и установки правильного порядка алфавитной сортировки можно попытаться использовать какойлибо из русскоязычных драйверов dBase или Paradox. Однако следует помнить, что некоторые ODBC-драйверы имеют свои процедуры настройки, которые могут, в частности, включать опцию перекодировки OEM–>ANSI (т.е. DOS–>Windows). Кроме того, если доступ через ODBC осуществляется к какой-либо серверной СУБД, следует обратить внимание на возможности языковой настройки сервера и клиентской части.
Oracle Для настройки доступа к серверам Oracle следует настроить примерно тот же набор параметров, что и в случае ODBC-источников. Помимо этого, для драйвера ORACLE существует дополнительный набор параметров (табл. 8). Таблица 8 Настройки доступа к серверам Oracle Значение по Параметр Описание умолчанию VENDOR Имя библиотеки для соединения клиента с ORANT.DLL INIT сервером (ORANT.DLL, ORA72.DLL и др.) Имя псевдонима (alias) БД, указанного в SERVER файле TNSNAMES.ORA. Если сервер NAME локальный, то SERVER NAME=@2: TNS - если используется SQL*Net версии NET 2.0 или выше, или имя сетевого протокола PROTOCOL для доступа к серверу, если используется более ранняя версия SQL*Net. ENABLE Разрешается ли кэширование на рабочей SCHEMA станции структуры таблиц, содержащихся FALSE CASHE на сервере. SCHEMA Каталог для кэширования структуры таблиц. 1.0 CASHE DIR Разрешен ли перевод числовых и денежных ENABLE величин в формат BCD (binary coded FALSE BCD decimals) во избежание ошибок округления. Разрешено ли преобразование числовых ENABLE величин с фиксированной запятой в целый FALSE INTEGERS формат.
Разрешены ли синонимы (альтернативные имена таблиц и представлений) в структурах LIST таблиц: NONE – нет, PRIVATE – разрешены NONE SYNONYMS личные синонимы, ALL – разрешены личные и общие синонимы. Проблемы установки соединения с Oracle из приложений Borland связаны главным образом с правильной настройкой сетевого программного обеспечения Oracle SQL*Net. Последние версии серверов Oracle (7.2, 7.3) содержат в своем составе SQL*Net версии 2.0 или выше. Для конфигурации SQL*Net 2.0 и выше следует настроить псевдонимы баз данных Oracle с помощью утилиты SQL*Net Easy Configuration, описав сетевое имя или адрес сервера, имя базы данных и тип сетевого протокола. После этого рекомендуется проверить наличие соединения с сервером с помощью утилиты Oracle SQL Plus. Только после проверки соединения можно конфигурировать псевдоним BDE. При конфигурации псевдонима BDE в качестве сетевого протокола можно указать TNS (Transparent Network Substrate – высокоуровневая надстройка Oracle над сетевыми протоколами). В качестве имени сервера следует указать имя псевдонима базы данных Oracle, указав перед этим именем символ '@' (об этом в документации не сказано). Для локального сервера (или в случае, когда клиентское приложение функционирует на одном компьютере с сервером баз данных, например, Oracle Workgroup Server for Windows NT) в качестве имени сервера можно использовать строку '@2:' . Для корректного отображения русских букв и установки правильного порядка алфавитной сортировки можно использовать драйверы dBase RUS CP866 или Paradox ANSI Cyrillic в зависимости от того, какая кодировка – DOS или Windows – будет использоваться. Однако корректная работа с русским языком будет обеспечена только в том случае, если удачно подобрано сочетание трех параметров языковой настройки: языковой настройки сервера (она указывается при его установке), языковой настройки клиентской части (значение HKEY_LOCAL_MACHINE//SOFTWARE)//ORACLE//NLS_LANG реестра Windows 95) и собственно языкового драйвера BDE. Подробности языковой настройки клиентской части и сервера можно найти в документации к серверу Oracle.
InterBase Для настройки доступа к серверам InterBase следует настроить примерно тот же набор параметров, что и в случае ODBC-источников. Помимо этого, для InterBase существует дополнительный набор параметров (табл. 9).
Отметим, что при соединении с локальным сервером InterBase следует указывать путь к файлу базы данных. При возникновении проблем связи C++ Builder с InterBase следует проверить наличие связи с помощью утилиты Interactive SQL. При наличии связи в Interactive SQL следует проверить параметры конфигурации драйвера и псевдонима BDE, а при отсутствии – попробовать найти ошибки в сетевом протоколе, проверив соединение с помощью утилит PING и TELNET. Таблица 9 Дополнительный набор параметров для настройки доступа к серверам InterBase Значение по Параметр Описание умолчанию BLOBS Определяет, сколько BLOB-полей кэшируется TO на рабочей станции. Возможные значения: 64 – 64 CACHE 65536 Определяет размер буфера для BLOB-полей, BLOB передаваемых в результате запроса. Возможные 32 К SIZE значения – 32–100 К. Параметр применим только в случае нередактируемых данных
Упражнение 4.1. Формулировка задачи и создание базы данных для «Однокнопочного бандита» Такая объёмная вступительная часть к этой лабораторной работе предполагает то, что значительную часть работы Вы ДОЛЖНЫ ВЫПОЛНИТЬ САМОСТОЯТЕЛЬНО. Как Вы уже поняли из название, заготовкой для этого упражнения будет приложение из лабораторной работы № 2, упражнение 2.1, все файлы которого необходимо скопировать в отдельную папку. В этой же папке создайте папку для хранения базы данных нового приложения, например, с именем DATABASE. После этого с помощью инструментальных средств BDE Admistrator и DataBase Desktop создайте Alias, связанный с вашей папкой DATABASE и две связанные таблицы формата Paradox: 1) User – таблица игроков и 2) Protocol – таблица результатов игры. При этом таблица User – это главная таблица (Master Table) и в ней содержиться информация об игроках и паролях для подключения к игре, а таблица Protocol – таблица детализации (Detail Table), в которой содержится информация о дате и результатах каждой игры игроков. Расширьте имеющееся приложение следующими функциями:
1) на заставке должен появляться запрос пароля для подключения или регистрации нового игрока; 2) при регистрации должна вызываться новая форма для редактирования таблицы игроков в режиме дополнения новой записи; 3) во время регистрации необходимо проводить контроль уникальности идентификатора игрока и уникальности пароля; 4) при каждой игре в таблицу Protocol необходимо добавлять новую запись с результатами; 5) главную форму приложения дополните управляющими элементами, например, главное меню или кнопки, которые позволят просматривать результаты всех игр игрока и 10 лучших результатов из таблицы Protocol.
Упражнение 4.2. Создание отчетов Вы так же ДОЛЖНЫ ВЫПОЛНИТЬ САМОСТОЯТЕЛЬНО. Для этого, используя справочную систему Builder C++ познакомьтесь с компонентом TQuickReport. Скопируйте все файлы упражнения 4.1 в отдельную папку и расширьте приложение в части создания отчетов. Для этого главную форму приложения дополните управляющими элементами, например, главное меню или кнопки, которые позволят создавать следующие отчеты: 1) отчет о 10 лучших результатах и игрока, которые их добились; 2) отчет о рейтинге всех игроков и их лучших результатах; 3) отчет о количестве игр каждого игрока и общем количестве игр; 4) отчеты 1, 2, 3 за задаваемый период.
Упражнение 4.3. Изменение цвета строки в TDBGrid Предположим, нам требуется изменить атрибуты текста и фона строки в компоненте TDBGrid, если значение какого-либо поля удовлетворяет заранее заданному условию. Для этой цели принято использовать обработчик события OnDrawColumnCell этого компонента. Отметим, что возможности, предоставляемые при его использовании, весьма разнообразны. Рассмотрим простейшее приложение с TDBGrid, содержащее один компонент TTable, один компонент TDataSource и один компонент TDBGrid. Установим значения их свойств в соответствии с ниже приведенной табл. 10.
Рис. 28. Тестовое приложение на этапе проектирования Обычно для перерисовки изображения в ячейках используется метод OnDrawColumnCell. Его параметр Rect – структура, описывающая занимаемый ячейкой прямоугольник, параметр Column – колонка DBGrid, в которой следует изменить способ рисования изображения. Для вывода текста используется метод TextOut свойства Canvas компонента TDBGrid.
Предположим, нам нужно изменить цвет текста и фона строки в зависимости от значения какого-либо поля (например, VenueNo). Создадим обработчик события OnDrawColumnCell компонента DBGrid1. void __fastcall TForm1::DBGrid1DrawColumnCell(TObject *Sender, const TRect &Rect, int DataCol, TColumn *Column, TGridDrawState State) { if (Table1->FieldByName("VenueNo")->Value==1) { DBGrid1->Canvas->Brush->Color=clGreen; DBGrid1->Canvas->Font->Color=clWhite; DBGrid1->Canvas->FillRect(Rect); DBGrid1->Canvas->TextOut( Rect.Left+2,Rect.Top+2,Column->Field->Text); } } В результате на этапе выполнения при отображении строк, в которых значение поля VenueNo равно 1, фон ячеек будет окрашен в зеленый цвет, а текст будет выведен белым цветом.
Рис. 29. Изменение цвета фона и шрифта на этапе выполнения При выводе выделенных строк все данные в ячейках оказались выровненными по левому краю. Если мы хотим более корректно отобразить выравнивание текста в колонке, следует слегка модифицировать наш код, учтя значение свойства Alignment текущей (то есть рисуемой в данный момент) колонки. void __fastcall TForm1::DBGrid1DrawColumnCell(TObject *Sender,
const TRect &Rect, int DataCol, TColumn *Column, TGridDrawState State) { if (Table1->FieldByName("VenueNo")->Value==1) { DBGrid1->Canvas->Brush->Color=clGreen; DBGrid1->Canvas->Font->Color=clWhite; DBGrid1->Canvas->FillRect(Rect); if (Column->Alignment==taRightJustify) { DBGrid1->Canvas->TextOut(Rect.Right-2DBGrid1->Canvas->TextWidth(Column->Field->Text), Rect.Top+2,Column->Field->Text); } else { DBGrid1->Canvas->TextOut( Rect.Left+2,Rect.Top+2,Column->Field->Text); } } } Отметим, что величина смещения (в данном случае 2 пиксела), вообще говоря, зависит от гарнитуры и размера шрифта, используемого в данной колонке, и должна подбираться индивидуально.
Рис. 30. Изменение цвета с учетом выравнивания текста в колонках Если необходимо отобразить нестандартным образом не всю строку, а только некоторые ячейки, следует проанализировать имя поля,
отображаемого в данной колонке, как в приведенном ниже обработчике событий. void __fastcall TForm1::DBGrid1DrawColumnCell(TObject *Sender, const TRect &Rect, int DataCol, TColumn *Column, TGridDrawState State) { if ((Table1->FieldByName("VenueNo")->Value==1) & (Column->FieldName=="VenueNo") ) { DBGrid1->Canvas->Brush->Color=clGreen; DBGrid1->Canvas->Font->Color=clWhite; DBGrid1->Canvas->FillRect(Rect); DBGrid1->Canvas->TextOut(Rect.Right-2DBGrid1->Canvas->TextWidth(Column->Field->Text), Rect.Top+2,Column->Field->Text); } } В результате выделенными оказываются только ячейки, для которых выполняются выбранные нами условия.
Рис. 31. Выделение цветом данных в одной колонке
Упражнение 4.4. Замена данных в столбце компонента TDBGrid Нередко в колонке DBGrid нужно вывести не реальное значение, хранящееся в поле соответствующей таблицы, а другие данные, соответствующие имеющимся (например, символьную строку вместо ее числового кода). В этом случае также используется метод TextOut свойства Canvas компонента TDBGrid.
void __fastcall TForm1::DBGrid1DrawColumnCell(TObject *Sender, const TRect &Rect, int DataCol, TColumn *Column, TGridDrawState State) { if(Column->FieldName=="VenueNo") { DBGrid1->Canvas->Brush->Color=clWhite; DBGrid1->Canvas->FillRect(Rect); if (Table1->FieldByName("VenueNo")->Value==1) { DBGrid1->Canvas->Font->Color=clRed; DBGrid1->Canvas->TextOut(Rect.Right-2DBGrid1->Canvas->TextWidth("our venue"), Rect.Top+2,"our venue"); } else { DBGrid1->Canvas->TextOut(Rect.Right-2DBGrid1->Canvas->TextWidth("other venue"), Rect.Top+2,"other venue"); } } }
Рис. 32. Замена данных в колонке другими значениями Еще один пример – использование значков из шрифтов Windings или Webdings в качестве подставляемой строки. void __fastcall TForm1::DBGrid1DrawColumnCell(TObject *Sender,
const TRect &Rect, int DataCol, TColumn *Column, TGridDrawState State) { if(Column->FieldName=="VenueNo") { DBGrid1->Canvas->Brush->Color=clWhite; DBGrid1->Canvas->FillRect(Rect); DBGrid1->Canvas->Font->Name="Wingdings"; DBGrid1->Canvas->Font->Size=-14; if (Table1->FieldByName("VenueNo")->Value==1) { DBGrid1->Canvas->Font->Color=clRed; DBGrid1->Canvas->TextOut(Rect.Right-2DBGrid1->Canvas->TextWidth("J"), Rect.Top+1,"J"); } else { DBGrid1->Canvas->Font->Color=clBlack; DBGrid1->Canvas->TextOut(Rect.Right-2DBGrid1->Canvas->TextWidth("F"), Rect.Top+1,"F") ; } } }
Рис. 33. Использование символов для выделения значений
Упражнение 4.5. Графическое изображение в TDBGrid Использование свойства Canvas компонента TDBGrid в методе OnDrawColumnCell позволяет не только выводить в ячейке текст методом TextOut, но и размещать в ячейках графические изображения. В этом случае используется метод Draw свойства Canvas. Модифицируем наш пример, добавив на форму компонент TImageList и поместив в него несколько изображений.
Рис. 34. Компонент TImageLis с изображениями Модифицируем код нашего приложения. void __fastcall TForm1::DBGrid1DrawColumnCell(TObject *Sender, const TRect &Rect, int DataCol, TColumn *Column, TGridDrawState State) { Graphics::TBitmap *Im1; Im1= new Graphics::TBitmap; if(Column->FieldName=="VenueNo") { DBGrid1->Canvas->Brush->Color=clWhite; DBGrid1->Canvas->FillRect(Rect); if (Table1->FieldByName("VenueNo")->Value==1) { ImageList1->GetBitmap(0,Im1); } else {
ImageList1->GetBitmap(2,Im1); } DBGrid1->Canvas->Draw( (Rect.Left+Rect.Right-Im1->Width)/2,Rect.Top,Im1); } } Теперь в TDBGrid в колонке VenueNo находятся графические изображения.
Рис. 35. Вывод графических изображений в колонке
Отчет о лабораторной работе Надеюсь, Вы успешно справились с заданием и пришла пора подумать об отчёте. Итак, в качестве отчетных материалов по лабораторной работе № 4 Вам необходимо предоставить нижеследующие материалы. 1. Красиво оформленные исходные тексты всех Ваших программ, выполненные в едином стиле. Все файлы каждого упражнения должны храниться в отдельных папках.; 2. Загружаемые программы, собранные без подключения пакетов и без run-time библиотек. Всего таких файлов 5. Отклонения от учебного пособия в сторону расширения функциональности всяческим образом приветствуется; 3. Для упражнения 4.1 необходимо написать пояснительную записку в соответствии с ГОСТ 19.402-78 Описание программы; 4. По результатам работы проводится зачетный тест.
ЛАБОРАТОРНАЯ РАБОТА №5. MIDAS И НАСТОЛЬНЫЕ ПРИЛОЖЕНИЯ Практически любое современное приложение для своей нормальной работы содержит массу вспомогательных файлов, которые в совокупности являются небольшой специализированной базой данных. И желание при программировании использовать библиотеку BDE вполне естественно. Однако, при этом разработчики испытывают некоторые неудобства при поставке своих приложений. Это связано с необходимостью установки библиотеки BDE на компьютер пользователя. И, если приложение использует базы данных, необходимо, помимо собственно приложения, установить на компьютер пользователя библиотеку Borland Database Engine. Установка этой библиотеки заключается в копировании файлов библиотеки на компьютер пользователя, внесении сведений о ней в реестр, установке пиктограммы для утилиты конфигурации BDE, а также настройке псевдонимов и языковых драйверов для данного приложения. В процессе установки BDE, как правило, возникают проблемы. Вопервых, на компьютере могут быть уже установлены другие приложения, использующие BDE. Если просто скопировать на компьютер файл конфигурации BDE поверх имеющегося описания используемых псевдонимов, т.е. уже установленное приложение станет неработоспособным. Еще одна проблема может быть связана с несовпадением версий BDE. Если с момента выхода используемой версии BDE прошло какое-то время, есть риск заменить старой версией BDE более новую и тем самым нарушить работоспособность использующих ее приложений. Отметим также, что корректное написание дистрибутива приложения далеко не всегда является гарантией дальнейшей корректной его работы. Например, не все пользователи грамотно деинсталлируют приложения. Часто бывает, что ставший ненужным каталог просто стирается (в том числе и каталог с BDE). При этом, скорее всего (исключения бывают очень редко) соответствующая ветвь реестра сохранится, и инсталляционное приложение будет сообщать пользователю, что BDE на данном компьютере уже есть. Иными словами, установка BDE влечет за собой создание дистрибутива и риск быть вовлеченным в коллизии, связанные с заменой этой библиотеки более старой версией или замены файлов конфигурации BDE. В сложных многопользовательских системах с серверными базами данных и с большим количеством таблиц такой риск может быть сведен к минимуму, так как в силу высокой стоимости таких систем на подобных предприятиях обычно устанавливаются жесткие корпоративные правила
для пользователей, в том числе запрещающие несанкционированную установку программного обеспечения без разрешения системного администратора. Но если приложение невелико и использует две-три таблицы, оно не должно требовать наличия жестких корпоративных правил его использования. Отметим также, что в этом случае сама библиотека BDE может существенно превышать по объему и приложение, и поставляемые с ним данные. Все эти прелести Вы уже ощутили на себе при общении с преподавателем во время сдачи лабораторной работы № 4. По этим причинам разработчики нередко изобретают свои форматы данных, дабы не использовать BDE вообще, а просто считывать файл с диска. Как ни парадоксально, разработчики, занимающиеся созданием подобных приложений, даже имея клиент-серверные версии Delphi или C++Builder, просто не обращают внимания на страницу MIDAS палитры компонентов этих средств разработки, считая, что эти компоненты не для их задач. А ведь именно там и содержится компонент TClientDataSet, позволяющий создать вспомогательный файл и использовать его. Обычно компонент TCLientDataSet используется в "тонких" клиентах серверов доступа к данным. Подобные приложения широко используют кэширование данных в оперативной памяти рабочей станции и нередко учитывают возможность разрыва связи с сервером доступа к данным. Соответственно компонент TCLientDataSet позволяет сохранить содержимое своего кэша в файле и загрузить его оттуда. После этого можно просто забыть о сервере доступа к данным и работать только с этим файлом (его можно даже редактировать). Библиотека BDE при этом не нужна – "тонкие" клиенты ее не используют, в том числе, и отчуждаемые "тонкие" клиенты. Цель этой лабораторной работы – научиться использовать компонент TCLientDataSet в качестве интерфейса небольшой базы данных.
Упражнение 5.1. Приложение с базой данных без использования BDE Прежде чем проектировать требуемые нам приложения создадим приложение для переноса данных из таблиц в файл, содержащий кэш компонента TCLientDataSet. Для этого создадим новый проект и поместим на его главную форму три комплекта из компонент TTable и TDataSourse, один компонент TClientDataSet, один компонент TDBGrid и один компонент TDBNavigator (последние два компонента нужны только для контроля и просмотра данных и, по существу, совершенно не обязательны). Установим свойства этих компонентов в соответствии с табл. 11. Ñâîéñòâà êîìïîíåíòîâ "ìàëåíüêîãî" ïðèëîæåíèÿ
Таблица 11
Свойства компонентов «маленького» приложения
Компонент DBGrid1 DBNavigator1 Table1
Свойство DataSource DataSource DatabaseName
DataSource1 Table2
TableName Active DataSet DatabaseName
DataSource2 Table3
TableName IndexFieldNames MasterFields MasterSource Active DataSet DatabaseName
ClientDataSet1 DataSource3
TableName IndexFieldNames MasterFields MasterSource Active ProviderName Active DataSet
Значение DataSource3 DataSource3 BCDEMOS (или DBDEMOS) customer.db true Table1 BCDEMOS (или DBDEMOS) orders.db CustNo CustNo DataSource1 true Table2 BCDEMOS (или DBDEMOS) items.db OrderNo OrderNo DataSource2 true Provider1 true ClientDataSet1
Далее выберем из контекстного меню компонента ClientDataSet1 опцию Assign Local Data и в появившемся списке выберем Table1. Рис. 36. Выбор источника данных для заполнения компонента TClientDataSet.
После этого в кэш компонента TCLientDataSet будут загружены данные.
Рис. 37. Главная форма приложения после выбора источника данных для TCLientDataSet . Теперь из контекстного меню этого же компонента выберем опцию Save To File и в появившейся диалоговой панели открытия файла введем имя файла, в котором будут храниться данные из кэша. Итак, файл с данными готов. А теперь приступим к созданию нашего "маленького" приложение. Для этого достаточно просто удалить с формы компоненты Table1, Table2, Table3, DataSource1, DataSource2 – они больше не нужны. Есть две возможности создания таких "маленьких" приложений. Самый простой из них – хранить данные непосредственно в исполняемом файле приложения (если их объем невелик). Для этой цели следует из контекстного меню компонента TClientDataSet выбрать опцию Load From File и выбрать имя файла, сохраненного прежде. Теперь данные из этого файла содержатся в ресурсах формы, в чем можно убедиться, открыв форму в текстовом представлении. Если скомпилировать такое приложение, его можно передать пользователю. Единственное, что требуется добавить в комплект поставки – файл dbclient.dll из каталога Winnt\System32 (или Windows\System).
Еще один вариант – выполнить метод LoadFromFile компонента TClientDataSet в обработчике события OnCreate формы. В этом случае файл с кэшированными данными следует также включить в комплект поставки приложения, и объем его может быть достаточно велик (насколько именно – зависит от ресурсов рабочей станции, на которой используется такое приложение). Отметим, что Delphi 4 и C++Builder 4 позволяют хранить в таком файле данные из нескольких связанных таблиц (именно это и было нами сделано). Поэтому в полученном наборе данных имеется поле типа TDataSetField, предоставляющее доступ к detail-записям (в нашем случае к записям таблиц orders.db и items.db).
Рис. 38. Приложение, использующее локальную копию данных, на этапе выполнения. Пользователи Delphi 3 и C++Builder 3 могут создавать несколько компонентов TCLientDataSet и организовывать связь между ними в приложении так же, как и в случае обычных таблиц, используя свойства MasterField и MasterSource. Отметим, что, помимо настольных приложений, сохраненные в файле кэшированные данные могут быть использованы при создании демоверсий и прототипов клиент–серверных приложений, где затруднена или
исключена в силу лицензионных ограничений поставка полноценной версии СУБД.
Упражнение 5.2. Экономим место на форме при отображении таблиц В приложениях с базами данных, использующих несколько связанных таблиц, места на форме для их отображения, как правило, не хватает – это общая проблема проектирования интерфейса подобных приложений. Компонент TCLientDataSet может помочь и в этом случае. Возьмем наше самое первое приложение, содержащее три компонента TTable и компонент TClientDataSet, и добавим в него компонент TDataSetProvider. Установим значение его свойства DataSet равным Table1. Затем свойство ProviderName компонента ClientDataSet1 установим равным DataSetProvider1. Теперь наше приложение позволяет редактировать данные из всех трех таблиц, при этом интерфейс приложения окажется примерно тем же, что и на предыдущем рисунке. Единственное, о чем дополнительно следует позаботиться, это о пересылке отредактированных данных обратно в исходные таблицы с помощью метода ApplyUpdates компонента TClientDataSet. Обычно для этой цели к форме добавляют какой-либо интерфейсный элемент, инициирующий выполнение этого метода. Иногда этот метод добавляют к обработчику события AfterPost компонента TClientDataSet. В C++Builder этот код имеет вид: void __fastcall TForm1::ClientDataSet1AfterPost(TDataSet *DataSet) { ClientDataSet1->ApplyUpdates(-1); } Отметим, однако, что это не самый эффективный способ сохранения отредактированных записей, так как метод Post в данном случае выполняется локально, а метод ApplyUpdates требует обращения к базе данных, и при использовании сетевой СУБД лучше выполнять его не так часто, как метод Post.
Упражнение 5.3. Сортировка данных в компоненте TClientDataSet Для сортировки данных в компоненте TClientDataSet можно использовать свойство IndexFieldNames (точно так же, как и в случае использования компонента TTable). Помимо этого, компонент TClientDataSet обладает методами AddIndex и DeleteIndex. Эти методы позволяют произвести сортировку данных на этапе выполнения.
На форму приложения, содержащую данные из компонента TClientDataSet, добавим компонент TListBox и создадим два обработчика события. void __fastcall TForm1::FormCreate(TObject *Sender) { ListBox1->Items=ClientDataSet1->FieldList; } //-----------------------------------------------------------void __fastcall TForm1::ListBox1Click(TObject *Sender) { AnsiString fn=ListBox1->Items->Strings[ListBox1->ItemIndex]; IndexOptions opts; opts << ixCaseInsensitive; ClientDataSet1->AddIndex(fn + "Index", fn , opts, "", "",0); ClientDataSet1->IndexName = fn + "Index"; Edit1->Text=fn; } Теперь на этапе выполнения в компоненте TListBox будет отображаться список полей компонента TClientDataSet, и выбор из этого списка приведет к пересортировке записей.
Упражнение 5.4. Универсальный инструмент для сохранения содержимого таблиц в локальных файлах В заключение лабораторной работы создадим приложение, позволяющее сохранять в локальных файлах компонента TClientDataSet любые доступные таблицы. С этой целью создадим форму, содержащую два компонента TListBox, два компонента TDBGrid, один компонент TSplitter, два компонента TEdit, две кнопки и один TCheckBox. Поместим также на форму TSaveDialog, TDatabase, TSession, TTable, TClientDataSet, TDataSetProvider, два компонента TDataSource.
Рис. 39. Форма приложения для сохранения таблиц в локальных файлах. Установим следующие соответствии с табл. 12.
значения
свойств
этих
компонентов
в
P DataSetProvider1 r o v i d e r N a m e A false c t i v e D Table1 a t
a S e t D ClientDataSet1 a t a S e t D Table1 a t a S e t D DataSource1 a t a S o u r c e
C Files|*.cds|All files|*.* l i e
n t D a t a S e t D *.cds e f a u l t E x t Создадим обработчики событий, связанные с нажатиями на кнопки, выбором из списков и созданием формы приложения: //-----------------------------------------------------------#include <vcl.h> #pragma hdrstop #include "uni_cds1.h" //-----------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //-----------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //---------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TStringList *DBList = new TStringList();
try { Session1->GetAliasNames(DBList); for (int I = 0; I < DBList->Count; I++) ListBox1->Items->Add(DBList->Strings[I]); } __finally { delete DBList; } } //------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Database1->Connected=false; ClientDataSet1->Close(); Database1->Params->Clear(); Database1->AliasName= ListBox1->Items->Strings[ListBox1->ItemIndex]; Database1->Params->Add("User Name="+Edit1->Text); Database1->Params->Add("Password="+Edit2->Text); TStringList *TabList = new TStringList(); try { Database1->Connected=true; Session1->GetTableNames("MyDB","",!(CheckBox1->Checked),false, TabList) ; ListBox2->Items=TabList; } __finally { delete TabList; } } //----------------------------------------------------------void __fastcall TForm1::ListBox2Click(TObject *Sender) { Table1->Close(); ClientDataSet1->Close(); Table1->DatabaseName=Database1->DatabaseName ;
Table1->TableName= ListBox2->Items->Strings[ListBox2->ItemIndex]; Table1->Open(); ClientDataSet1->Open(); } void __fastcall TForm1::Button2Click(TObject *Sender) { if (SaveDialog1->Execute()) { ClientDataSet1->SaveToFile(SaveDialog1->FileName); } } //----------------------------------------------------------В момент создания формы создается список всех доступных баз данных с помощью метода GetAliasNames компонента TSession. При выборе элемента из этого списка, вводе имени пользователя и пароля происходит соединение с соответствующей базой данных и создание списка ее таблиц. Флажок SQL server нужен для того, чтобы указать, нужно ли выводить в этом списке расширения для имен таблиц. При выборе таблицы из списка ее данные отображаются в верхнем из компонентов TDBGrid и заполняют кэш компонента TCLientDataSet, отображаемый в нижнем из компонентов TDBGrid. При нажатии кнопки Save to CDS file появляется диалог сохранения файла, в котором следует ввести имя файла для сохранения таблицы, после чего происходит сохранение содержимого таблицы в файле. Отметим, что объем сохраняемых таблиц должен быть разумным – CLientDataSet хранит данные в оперативной памяти рабочей станции.
Отчет о лабораторной работе Надеюсь, Вы успешно справились с заданием и пришла пора подумать об отчёте. Итак, в качестве отчётных материалов по лабораторной работе № 5 Вам необходимо предоставить нижеследующие материалы. 1. Красиво оформленные исходные тексты всех Ваших программ, выполненные в едином стиле. Все файлы каждого упражнения должны храниться в отдельных папках; 2. Загружаемые программы, собранные без подключения пакетов и без run-time библиотек. Всего таких файлов должно быть 3. Отклонения от учебного пособия в сторону расширения функциональности всяческим образом приветствуется;
3. По результатам работы проводится зачетный тест.