    28     ГЛАВА 

    Приложения для работы с Internet 

    Целью этой главы является исследование возможностей Delphi по разработке 
приложений, работа с которыми или доступ к которым осуществляется через 
Internet. Delphi предоставляет в ваше распоряжение множество методов для 
разработки небольших приложений специальной архитектуры (Internet app), 
предназначенных для работы сив Internet, Мы сосредоточим свое внимание в 
основном на приложениях баз данных для Internet. 
    Было время, когда создание приложений для Internet, особенно работающих с 
базами данных, являлось сложной задачей, но сейчас все изменилось. С 
появлением последней версии Delphi процесс создания сложных приложений для 
Internet существенно упростился. Благодаря хорошей поддержке разработки как 
клиентной, так и серверной части приложений для Internet, система Delphi стала 
стандартом, на который равняются другие средства. 
    В этой главе мы рассмотрим пять различных проектов, которые позволят вам 
получить полное представление о разработке приложений Delphi для Internet. В 
первом проекте с помощью элементов управления вкладки Internet палитры 
компонентов Delphi будет создан Web-броузер. Во втором проекте мы используем 
технологию WebBridge для создания приложения базы данных, соответствующего 
стандартам ISAPI/NSAPI, предназначенного для работы с Web-сервером. При работе 
над третьим проектом вы познакомитесь с процессом создания и совершенствования 
приложения баз данных ActiveForm. В четвертом проекте вы создадите компактное 
клиентное приложение (thin-client), представляющее собой форму заказа, которую 
можно распространять и использовать через Internet. В последнем проекте этой 
главы будет построено компактное клиентное приложение ActiveForm для 
осуществления покупок по сети Internet. Эти пять проектов представляют собой 
лишь малую часть из бесконечного множества (от простых до самых сложных) типов 
приложени й, связанных с Internet, которые можно создать с помощью Delphi. 
    Большинство современных средств разработки отважились войти хотя бы на 
несколько шагов в бурный поток Internet. К сожалению, многие из них в этом не 
преуспели — обеспечиваемая ими поддержка работы в Internet зачастую является 
слишком поверхностной. Часто это выглядит так, словно их основной целью 
является желание побыстрее заявить в рекламных материалах о поддержке Internet 
(или даже Java). Delphi же — полноценная среда разработки приложений для 
Internet. Создание приложений для Internet в Delphi — это не косметические 
украшения старого компилятора, а одна из базовых технологий системы. Delphi не 
является редактором для создания HTML-файлов, но вряд ли можно найти среду 
разработки, основанную на компиляции, с более удобными средствами создания 
приложений для Internet. 
    Вам часто придется слышать термин WebBroker, который широко используется 
для обозначения многочисленных средств Delphi по разработке приложений для 
Internet. He существует отдельной программы или утилиты, на которую можно было 
бы указать "Это — WebBroker". В некотором смысле таким брокером (или 
посредником) можно было бы назвать компонент WebModule Delphi, но технология 
Delphi работы с Web также охватывает удаленные модули данных, генерацию 
ActiveForm и приложения для Web-сервера. Этот термин употребляется для 
обозначения некоего посредника (брокера), который взаимодействует с Web и 
упрощает жизнь вам и создаваемым вами приложениям. Не имеет значения, что 
конкретно стоит за этим термином в Delphi — технология WebBridge, компонент 
WebModule или встроенная поддержка технологии ActiveForm. Важно только, что 
эти средства помогают создавать приложения для Internet. 

    Глава 28, Приложения для работы с Internet 551 

    Проект номер один: Web-броузер 

    Первая наша задача — создать простой Web-броузер. Это не значит, что миру 
на самом деле нужен новый Web-броузер. Цель этого упражнения — 
продемонстрировать, насколько легко создать приложение для Internet, что было 
сложной задачей еще несколько лет назад. Компонентный подход к разработке 
приложений в Delphi позволяет сосредоточиться на том, что вы хотите получить, 
а не на том, как этого добиться. 
    Сначала создадим в Delphi новый проект. Выберите в меню Delphi команду 
File^New Application, чтобы инициализировать новый проект приложения. При 
запуске этого нового проекта будет создана форма для вашего приложения 
Internet — Web-броузера под названием Surf. В качестве значения свойства 
Caption этой формы укажите Surf и назовите форму fmSURFOO. Свойству Position 
присвойте значение poScreenCenter. Прежде чем двигаться дальше, сохраните 
новое приложение под именем Surf .DPR, а соответствующую ему форму под именем 
Surf 00. PAS. 
    Приступая к созданию главной формы приложения Surf выполните следующие действия. 
    1.    Поместите компонент TTabControl в верхнюю часть формы и присвойте 
ему имя tcURL. Этот управляющий элемент будет служить для просмотра списка 
URL, чтобы можно было легко вернуться к одному из адресов, который вы уже посетили. 
    2.   Задайте alTop в качестве значения свойства Align этого элемента 
управления. Благодаря значению alTop, компонент TTabControl всегда будет 
занимать всю верхнюю часть формы, независимо от размера самой формы. 
    3.    Установите размер компонента TTabControl таким образом, чтобы его 
высота составляла примерно 50 пикселей. Чтобы задать размер по вертикали, 
можно перетащить мышью нижнюю границу компонента TTabControl. 
    4.    Задайте в качестве значения свойства TabWidth этого компонента 145. 
Тогда размер каждой вкладки будет достаточным, чтобы на ней помещался простой 
URL, и вкладка не занимала бы слишком много места. 

    На рис. 28.1 показано, как теперь должна выглядеть ваша форма. 

    После того как верхняя часть формы броузера создана, выполните следующие 
действия по оформлению ее нижней части. 
    1.    Поместите компонент TPanel в нижнюю часть формы и установите для 
свойства Align значение alBottom. Как и в случае с компонентом TTabControl, 
это приведет к тому, что данный управляющий элемент всегда будет занимать 
нижнюю часть формы, независимо от ее размера. 
    2.    Нажмите <F11>, чтобы перейти в окно Object Inspector. Затем удалите 
значение свойства Capticr. компонента TPanel. 
    3.    Задайте для свойства TabOrder этого компонента значение 0. Тогда 
используемый по умолчанию компонент панели (который вы поместите на нее в 
следующем пункте) будет назначен используемым по умолчанию компонентом формы. 
    4.    После настройки компонента TPanel можно поместить на него его 
дочерние управляющие элементы Сначала у правой границы первого компонента 
TPanel поместим еще один компонент TPanel. 
    5.    Задайте для свойства Align нового компонента TPanel значение alRight 
и удалите значение его свойства Caption. При этом управляющие элементы всегда 
будут размещаться в правом углу формы, независимо от изменения размеров самой формы. 
    6.   Задайте для свойства BevelOuter компонента TPanel значение bvNone. 
Тогда границы панели не будут отображаться. Поскольку целью создания этой 
панели является лишь управление размещением других компонентов на форме, то 
границы вокруг самой панели действительно не нужны. 
    7.    Поместите на вновь созданную панель два компонента TBitBtn. 
    8.   Назовите первую кнопку bbGo и установите для ее свойства Caption 
значение Go!. 
    9.    Второй кнопке присвойте имя bbHome и задайте для ее свойства Caption 
значение Ноте. 
    10. Задайте для свойства Glyph каждой кнопки любое растровое изображение 
на свой вкус. 
    11. Установите для свойства Default кнопки bbGo значение true. Это 
позволит пользователю просто ввести URL и нажать клавишу <Enter> для перехода 
по этому адресу. При нажатии кнопки bbGo броузер открывает указанный вами URL, 
в то время как при нажатии клавиши bbHome броузер перейдет прямо на начальную 
страницу. Далее я покажу, как определить начальную страницу в исходном тексте броузера. 

    552	Часть IV. Темы повышенной сложности 

    Рис. 28.1. Форма Web-броузера, содержащая компонент TTabControl 
    Рис. 28.2. Форма Web-броузера с верхней и нижней панелями 

    Глава 28. Приложения для работы с Internet 553 

    12. Разместив кнопки, поместите компонент TComboBox у левого края нижнего 
компонента TPanel и присвойте ему имя cbURL. В этом комбинированном списке 
будут содержаться адреса Web-страниц, которые можно просмотреть. Для просмотра 
определенной страницы в процессе работы броузера Surf нужно ввести ее URL в 
строке ввода комбинированного списка cbURL и щелкнуть на кнопке с именем btGo. 
    13. Удалите значение свойства Text компонента cbURL и растяните этот 
управляющий элемент на максимальную ширину. 
    На рис. 28.2 показано, как теперь должна выглядеть форма. 
    Теперь, когда верхняя и нижняя часть главной формы броузера Surf созданы и 
настроены, можно приступить к оформлению средней части. Поместите компонент 
THTML на середину формы и присвойте ему имя htBrowser. В этом месте будут 
появляться просматриваемые вами Web-страницы. Выберите alciient в качестве 
значения свойства Align компонента htBrowser, чтобы он занимал оставшееся 
пространство формы На рис. 28.3 показано, как должна выглядеть форма после 
размещения на ней всех элементов управления. 

    Оживление новой формы 

    Завершив оформление внешнего вида главной формы броузера Surf, необходимо 
связать с ее событиями определенный программный код. Главное событие, которое 
нужно настроить, — это событие OnBeginRetrieval компонента THTML. Кроме того, 
необходимо создать обработчики событий OnClic : обеих кнопок и обработчик 
события OnChange компонента TTabControl. 
    Сначала щелкните на компоненте TTabControl и в окне Object Inspector 
щелкните дважды на значении его события OnChange. В окне редактора исходного 
текста Delphi введите следующие операторы. 

    cbURL.Text:=tcURL.Tabs[tcURL.Tablndex]; cbURL.SelectAll;: bbGo.Click; 

    Присваивая свойству Text компонента cbURL текст вкладки, на которой 
выполнен щелчок мышью, вь обеспечиваете синхронизацию элементов управления 
cbURL и tcURL. Благодаря вызову метода SelectAll. значение свойства Text 
компонента TComboBox выделяется. Обращение к событию Click компонента 
ЬЬС-имитирует щелчок на реальной кнопке. В результате щелчок на вкладке и на 
кнопке Go! приводит к одном' и тому же результату. 

    Рис. 28.3. Новая форма, содержащая управляющие элементы 


    554 Часть IV. Темы повышенной сложности 

    Теперь щелкните на компоненте htBrowser и в окне Object Inspector дважды 
щелкните на значении его события OnBeginRetrieval. В окне редактирования 
исходного текста введите следующие операторы. 

    Cursor:=crHourGlass;: 
    if (cbURL.Items.IndexOf(htBrowser.URL)=-1) then begin 
    cbURL.Items.Add(htBrowser.URL); 
    cbURL.Text:=htBrowser.URL; 
    cbURL.SelectAll; 
    tcURL.Tabs.Add(htBrowser.URL) ; 
    tcURL.TabIndex:=tcURL.Tabs.Count-1; 
    bbGo.Click; end; 

    Здесь проверяется, действительно ли адрес URL нужной страницы содержится в 
списке cbURL. Если данный URL отсутствует в списке, то он добавляется как в 
cbURL, так и в tcURL. 
    Обратите внимание на присвоение значения свойству Cursor данной формы. Эта 
операция используется для индикации того, что форма находится в процессе 
загрузки Web-страницы. 
    Поскольку указатель мыши имеет форму песочных часов, то, естественно, в 
какой-то момент его форму необходимо восстановить. Это делается с помощью 
события OnEndRetrieval компонента htBrowser. Введите в исходный текст 
обработчика события OnEndRetrieval следующий оператор. 

    Cursor:=crDefault; 

    При этом после завершения загрузки Web-страницы форма указателя мыши изменится. 
    Помимо изменения значения свойств в окне Object Inspector, вы можете 
щелкнуть правой кнопкой мыши на компоненте htBrowser и задать другие свойства, 
определяющие поведение этого компонента. Например, можно щелкнуть правой 
кнопкой мыши на компоненте и задать растровое изображение для постоянного фона броузера. 
    Теперь, определив события для компонентов tcURL и htBrowser, можно 
приступить к настройке работы кнопок. Щелкните дважды на кнопке bbGo, чтобы 
открыть окно редактирования исходного текста ее обработчика события OnClick. 
Добавьте в текст соответствующего метода следующий оператор. 

    htBrowser.RequestDoc(cbURL.text); 

    С помощью этого оператора в htBrowser открывается Web-страница, 
находящаяся по адресу URL, указанному в cbURL. 
    Определив обработчик события щелчка на кнопке Go!, дважды щелкните на 
кнопке ЬЬНоте, чтобы сделать то же самое для нее. Добавьте в исходный текст 
обработчика события OnClick кнопки ЬЬНоте следующий оператор. 

    htBrowser.RequestDoc(HOMEPAGE); 

    В этом операторе используется константа HOMEPAGE, которую необходимо 
определить заранее. Перейдите к началу файла Surf 00 . PAS и добавьте 
следующее объявление константы HOMEPAGE. 

    Const 
    HOMEPAGE  =   'http://www.borland.com'; 

    Объявление константы HOMEPAGE целесообразно включить в раздел 
Implementation модуля Surf 00. PAS. Таким образом, создание броузера в 
основном завершено. В листингах 28.1—28.3 показано, как должен выглядеть 
исходный текст вашего нового приложения. 

    Листинг 28.1. Файл проекта Web-броузера Surf 
    program Surf; 
    uses Forms, surfOO in 'surfOO.pas' {fmSurfOO}; 
    {$R *.RES} 

    Глава 28. Приложения для работы с Internet 555 

    begin 
    Application.Initialize; 
    Application.CreateForm(TfmSurf00, fmSurfOO); 
    Application.Run; end. 

    Листинг 28.2. Исходный текст программного модуля главной формы fmSurf 00 
броузера Surf 

    unit surfOO; interface 
    uses 
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
ComCtrls, OleCtrls, isp3, StdCtrls, Buttons, ExtCtrls; 
    type 
    TfmSurfOO = class(TForm) 
    tcURL: TTabControl; 
    Panell: TPanel; 
    Panel2: TPanel; 
    bbGo: TBitBtn; 
    bbHome: TBitBtn; 
    cbURL: TComboBox; 
    htBrowser: THTML; 
    procedure tcURLChange(Sender: TObject); 
    procedure htBrowserBeginRetrieval(Sender: TObject); 
    procedure htBrowserEndRetrieval(Sender: TObject); 
    procedure bbGoClick(Sender: TObject); 
    procedure bbHomeClick(Sender: TObject); private 
    { Private declarations } public 
    { Public declarations } end; 
    var 
    fmSurfOO: TfmSurfOO; 
    implementation {$R *.DFM} 
    Const 
    HOMEPAGE = 'http://www.borland.com'; 
    procedure TfmSurf00.tcURLChange(Sender: TObject); begin 
    cbURL.Text:=tcURL.Tabs[tcURL.Tablndex]; 
    cbURL.SelectAll; 
    bbGo.Click; end; 
    procedure TfmSurf00.htBrowserBeginRetrieval(Sender: TObject); begin 
    Cursor:=crHourGlass; 
    if (cbURL.Items.IndexOf(htBrowser.URL)=-1) then begin 
cbURL.Iterns.Add(htBrowser.URL); cbURL.Text:=htBrowser.URL; cbURL.SelectAll; 
tcURL.Tabs.Add(htBrowser.URL); tcURL.Tablndex:=tcURL.Tabs.Count-1; bbGo.Click; end; 

    556	Часть IV. Темы повышенной сложности 

    end; 
    procedure TfmSurf00.htBrowserEndRetrieval(Sender: TObject); begin 
    Cursor:=crDefault; end; 
    procedure TfmSurf00.bbGoClick(Sender: TObject); begin 
    htBrowser.RequestDoc(cbURL.Text); end; 
    procedure TfmSurf00.bbHomeClick(Sender: TObject); begin 
    htBrowser.RequestDoc(HOMEPAGE); end; 
    end. 

    Листинг 28.3. Текстовое представление главной формы fmSurf 00 броузера Surf 

    object fmSurfOO: TfmSurf00 
    Left = 200 
    Top = 108 
    Width = 544 
    Height = 375 
    Caption = 'Surf Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText 
Font.Height = -11 Font.Name = 'MS Sans Serif Font.Style = [] Position = 
poScreenCenter PixelsPerlnch = 96 TextHeight = 13 object tcURL: TTabControl 
Left = 0 Top = 0 Width = 536 Height = 50 Align = alTop TabOrder = 1 TabWidth 
=145 OnChange = tcURLChange end 
    object Panell: TPanel Left = 0 Top = 307 Width = 536 Height = 41 Align = 
alBottom TabOrder = 0 object Panel2: TPanel Left = 360 Top = 1 Width = 175 
Height = 39 Align = alRight BevelOuter = bvNone TabOrder = 0 object bbGo: 
TBitBtn Left = 8 Top = 8 Width =75 
    
    Глава 28. Приложения для работы с Internet	557 

    Height = 25 Caption = 'Go ! ' Default = True TabOrder = 0 OnClick = 
bbGoClick Glyph.Data = { 
    76010000424D7601000000000000760000002800000020000000100000000100 
0400000000000001000000000000000000001000000010000000000000000000 
800000800000008080008000000080008000808000007F7F7FOOBFBFBFOOOOOO 
FFOOOOFFOOOOOOFFFFOOFFOOOOOOFFOOFFOOFFFFOOOOFFFFFF00555555555555 
555555FFFFFFFFFF55555000000000055555577777777775FFFFOOB8B8B8B8BO 
0000775F5555555777770BOB8B8B8B8BOFF07F75F555555575F70FBOB8B8B8B8 
BOF07F575FFFFFFFF7F70BFBOOOOOOOOOOF07F557777777777570FBFBFOFFFFF 
FFF07F55557F5FFFFFF70BFBFBOFOOOOOOF07F55557F777777570FBFBFOFFFFF 
FFF075F5557F5FFFFFF750FBFBOFOOOOOOF0575FFF7F777777575700000FFFFF 
FFF05577777F5FF55FF75555550FOOFF00005555557F775577775555550FFFFF 
OFO55555557F55557F755555550FFFFFO0555555557FFFFF7755555555000000 
0555555555777777755555555555555555555555555555555555} NumGlyphs = 2 end 
    object bbHome: TBitBtn Left = 88 Top = 8 Width = 75 Height = 25 Caption = 
'Home' TabOrder = 1 OnClick = bbHomeClick Glyph.Data = {     • 
    76010000424D7601000000000000760000002800000020000000100000000100 
0400000000000001000000000000000000001000000010000000000000000000 
800000800000008080008000000080008000808000007F7F7FOOBFBFBFOOOOOO 
FFOOOOFFOOOOOOFFFFOOFFOOOOOOFFOOFFOOFFFFOOOOFFFFFF00330000000000 
03333377777777777F333301111111110333337F333333337F33330111111111 
0333337F333333337F333301111111110333337F333333337F33330111111111 
0333337F333333337F333301111111110333337F333333337F33330111111111 
0333337F3333333F7F333301111111B10333337F333333737F33330111111111 
0333337F333333337F333301111111110333337F33FFFFF37F3333011EEEEE11 
0333337F377777F37F3333011EEEEE110333337F37FFF7F37F3333011EEEEE11 
0333337F377777337F333301111111110333337F333333337F33330111111111 
0333337FFFFFFFFF7F3333000000000003333377777777777333} NumGlyphs = 2 end end 
    object cbURL: TComboBox Left = 8 Top = 12 Width = 345 Height = 21 
ItemHeight = 13 TabOrder = 1 end end 
    object htBrowser: THTML Left = 0 Top = 50 Width = 536 Height = 257 
ParentColor = False ParentFont = False Align = alClient TabOrder = 2 
    OnBeginRetrieval = htBrowserBeginRetrieval OnEndRetrieval = 
htBrowserEndRetrieval ControlData = { 

    558	Часть IV. Темы повышенной сложности 

    170996190800000066370000901АОООООООА000000000001010100011ЕОООООО 
0100000000COCOCOOOOOOOOOOOOOOOFFOOFFOOFF00010000009001COD401000F 
54696D6573204E657720526F6D616E010000009001A08601000B436F75726965 
72204E657701000000BC0280A903000F54696D6573204E657720526F6D616E01 
OOOOOOBC0220BF02000F54696D6573204E657720526F6D616E01000000BC02EO 
2202000F54696D6573204E657720526F6D616E01000000BC02COD401000F5469 
6D6573204E657720526F6D616E01000000BC02A08601000F54696D6573204E65 
7720526F6D616E01000000BC02803801000F54696D6573204E657720526F6D61 6Е} end end 
    Теперь можно запустить броузер Surf. Сохраните свой проект, а затем 
нажмите <F9> для запуска приложения. На рис. 28.4 показан броузер Surf в 
процессе работы. 

    htp://vadAliL;.kievA»:80|htlp:W'«ad.di8l.lu.k>=v.ua-80/<h!tp://vaddiiil.iu.k«vua80/<| 

    Getting Started with Microsoft Personal Web Server 
    Personal Web Server transmit! information in Hypertext Markup Language 
(HTML) pages by using the Hypertext Transport Protocol (HTTP). It provides 
most of the functionality of Microsoft Internet Information Server, including 
the ability to: 
    •	Publish Web pages on the Internet or over a LAN on an intranet. 
    •	Support Microsoft ActiveX programs. 
    •	Transmit or receive files by using the FTP service. 
    Creating a Web site on your own computer 
    After you install Personal Web Server, you need to complete several steps. 
    1. By default, your home directory is C:\Webshare\Wwwroot Copy any HTML 
files you want on your site to this folder 
    2. Test your Personal Web Server installation. 
    After you have logged in, if Personal Web Server does not start, you can 
start it as follows. To start Personal Web Server 
    1. In Control Panel, double-click the Personal Web Server icon 
    2. On the Startup tab, click Start. 
    Tip 
    • To have Personal Web Server start automatically when you restart your 
computer, select the check box labeled Run The Web Server Automatically At Startup. 
    After you have started your server, it is a good idea to test it by using 
Internet Explorer to view the files in your home 

    Hif»rt^fw 
    П i 

    Рис. 28.4. Броузер Surf в процессе работы 

    Если у вас установлен Microsoft Internet Explorer (IE) версии 3.0 или 
более поздней, вы можете заменить элемент управления THTML в приложении Surf 
на элемент управления осх, лежащий в основе работы IE 3.x. Для этого выполните 
следующий действия. 
    1. Выберите из главного меню Delphi команду Components Import ActiveX Control. 
    2. Щелкните в списке зарегистрированных элементов управления на Microsoft 
Internet Controls. 
    3. Щелкните на кнопке Install.	
    4. Щелкните на кнопке ОК в диалоговом окне Install, чтобы добавить к 
пакету dclusrSO элемент управления TWebBrowser. 
    5. В ответ на запрос, следует ли перестраивать пакет dclusrSO, щелкните на 
кнопке Yes. 
    6. Щелкните на кнопке ОК, чтобы закрыть диалоговое окно Information, 
которое появится после перестройки 
пакета.	

    Глава 28. Приложения для работы с Internet 559 

    7. Щелкните на кнопке закрытия диалогового окна Package, а затем щелкните 
на кнопке Yes, чтобы сохра нить внесенные в пакет изменения. 
    8. Теперь элемент управления TWebBrowser добавлен к палитре компонентов 
Delphi. Щелкните на вклад| ке ActiveX, чтобы указать его месторасположение. 
Дело в том, что Microsoft \E 3.x реализован как эле мент управления OLE. Это 
означает, что создаваемые вами с его помощью приложения-броузеры буду 
функционировать точно так же, как и сам IE 3.x. 
    На рис. 28.5 показана версия броузера Surf, созданная с помощью элемента 
управления ActiveX Web-броузера компании Microsoft. 

    htp,7/vaddta!.RikteY.ua:8 
    Ihtlp //vaddialru.kiev.ua:80A HtpjVvad datru.kiev ua:80/d 
    FTP Server Administration 
    In addition to the World Wide Web service, Microsoft Personal Web Server 
includes File Transfer Protocol (FTP). To create ал FTP site on your computer 
    1. In Control Panel, double-click the Personal Web Server icon. 
    2. Click the Services tab. 
    3 On the Services property sheet, select FTP, and then click Start. 
    By default, your home directory is C:\WebShare\Ftproot You can, however, 
set a different directory to be the FTP home directory. 
    To set a different directory to be the home directory 
    1. In Control Panel, double-click the Personal Web Server icon. 
    2. On the Administration tab, click Administration. 
    3 On the Internet Services Administrator page, click FTP Administration 
    4 On the Directories tab, click Add 
    5. Type the path of a directory on your computer, or click Browse to 
search for the directory 
    6. Click Home Directory 
    7. To give people write access to the directory, in the Access box, select 
the Write check box -or-To give people read-only access to the directory, 
select the Read check box. 
    After you have set up your home directory, you can configure it. 

    Рис. 28.5. Для создания своего броузера можно использовать элемент 
управления ActiveX броузера IE 3.x 


    Проект номер два: приложение для Web-сервера 

    Во втором проекте мы создадим приложение базы данных, предназначенное для 
работы с Web-сервером. Это приложение должно предоставлять информацию о 
продажах товара, выбранного из списка. В качестве данных этого приложения 
будут использованы объекты базы данных, созданные в главе 17, "InterBase". 
    Благодаря использованию технологии WebBridge Delphi, это приложение будет 
удовлетворять сразу двум стандартам: NSAPI и ISAPI. WebBridge позволяет 
создавать приложения Web-сервера, не заботясь о выборе API. Разработчику не 
приходится думать о соответствии постоянно изменяющимся стандартам. Вы можете 
возложить задачу урегулирования конфликта API Web-серверов на Microsoft и 
Netscape и не поддерживать в этом конфликте ни одну из сторон. 
    Технология WebBridge Delphi автоматически используется всякий раз при 
создании нового приложения Web-сервера с использованием команды File^New^Web 
Server Application. Вам не нужно выполнять никаких специальных действий для 
включения технологии WebBridge — она работает "за кулисами" и облегчает тем 
самым вашу жизнь. 
    Приложения Delphi, предназначенные для работы с Web-сервером, делятся на 
три категории: ISAPI/NSAPI-совместимые библиотеки DLL, CGI-совместимые 
выполняемые модули и WinCGI-совместимые выполняемые модули. Мы займемся 
разработкой приложения первого типа. 

    560 Часть IV. Темы повышенной сложности 

    Создание базы данных 

    Чтобы создать приложение базы данных, которое будет работать под 
управлением Web-сервера, необходимо создать базу данных InterBase SALES.GDB, 
которая впервые упоминалась в главе 17. В листинге 28.4 содержится сценарий 
SQL, который можно запустить с помощью утилиты WISQL InterBase, чтобы создать 
базу данных SALES, если вы ее еще не создали и не хотите отрываться от работы 
и изучать главу 17. 

    На заметку 
    Перед запуском сценария SQL удостоверьтесь, что вы создали каталог С: 
\DATA\IB. При выполнении данного сценария предполагается, что такой каталог 
существует, и в нем создается база данных SALES. Если этот каталог не был 
создан заранее, то выполнение сценария завершится неудачно. 

    Листинг 28.4. Сценарий SQL SALES . SQL для создания базы данных SALES . GDB 

    /* Создание базы данных c:\data\ib\SALES.gdb. */ 
    CREATE DATABASE "c:\data\ib\SALES.gdb" USER "SYSDBA" PASSWORD "masterkey"; 
    /* Таблица: CUSTOMER, пользователь: SYSDBA. */ 
    CREATE TABLE CUSTOMER (CUSTOMERNUMBER INTEGER NOT NULL, 
    LASTNAME CHAR(30), 
    FIRSTNAME CHAR(30), 
    STREETADDRESS CHAR(30), 
    CITY CHAR(20), 
    STATE CHAR(2), 
    ZIP CHAR(10), PRIMARY KEY (CUSTOMERNUMBER)); 
    /* Таблица: CUSTOMERARRAY, пользователь: SYSDBA. */ 
    CREATE TABLE CUSTOMERARRAY (CUSTOMERNUMBER INTEGER NOT NULL, 
    LASTNAME CHAR(30), 
    FIRSTNAME CHAR(30), 
    STREETADDRESS CHAR(30) [1: 3] , 
    CITY CHAR(20), 
    STATE CHAR(2), 
    ZIP CHAR(10)); 
    /* Таблица: GOVITEM, пользователь: SYSDBA. */ CREATE TABLE GOVITEM 
(ITEMNUMBER INTEGER NOT NULL, 
    DESCRIPTION CHAR(30), 
    PRICE FLOAT, 
    GOVERNMENTPRICE COMPUTED BY (Price - Price * .15)); 
    /* Таблица: ITEM, пользователь: SYSDBA. */ CREATE TABLE ITEM (ITEMNUMBER 
INTEGER NOT NULL, 
    DESCRIPTION CHAR(30), 
    PRICE FLOAT); 
    /* Таблица: REPORTLOG, пользователь: SYSDBA. */ 
    CREATE TABLE REPORTLOG (REPORTUSER VARCHAR(20) DEFAULT USER 
    Г 
    REPORTDATE DATE DEFAULT "TODAY" 
    REPORTDATETIME DATE DEFAULT "NOW" ); 
    /* Таблица: SALE, пользователь: SYSDBA. */ CREATE TABLE SALE (SALENUMBER 
INTEGER NOT NULL, 
    SALEDATE DATE, 
    CUSTOMERNUMBER INTEGER NOT NULL, 
    ITEMNUMBER INTEGER NOT NULL, 
    AMOUNT FLOAT); 

    Глава 28. Приложения для работы с Internet	561 

    /* Таблица: SALE_HISTORY, пользователь: SYSDBA */ 
    CREATE TABLE SALE_HISTORY EXTERNAL FILE "C:\DATA\IB\SALEHIST.DAT" 
(SALENUMBER INTEGER 
    NOT NULL, 
    SALEDATE DATE, 
    CUSTOMERNUMBER INTEGER NOT NULL, 
    ITEMNUMBER INTEGER NOT NULL, 
    AMOUNT FLOAT); ALTER TABLE SALE ADD FOREIGN KEY (CUSTOMERNUMBER) 
REFERENCES CUSTOMER(CUSTOMERNUMBER); 
    /* Внесение данных в таблицы. */ 
    INSERT INTO CUSTOMER (CustomerNumber, LastName, FirstName, StreetAddress, 
City, State, 
    Zip) 
    VALUES(1,'Doe','John','123 Sunnylane','Anywhere', 'MO','73115'); 
    INSERT INTO CUSTOMER (CustomerNumber, LastName, FirstName, StreetAddress, 
City, State, 
    Zip) 
    VALUES(2,'Doe','Jane','123 Sunnylane','Anywhere', 'MO','73115'); 
    INSERT INTO CUSTOMER (CustomerNumber, LastName, FirstName, StreetAddress, 
City, State, 
    Zip) 
    VALUES(3,'Philgates','Buck','57 Riverside','Reo1,'AR','65803'); 
    INSERT INTO ITEM (ItemNumber, Description, Price) VALUES(1001, 'Zoso LP',13.45); 
    INSERT INTO ITEM (ItemNumber, Description, Price) VALUES(1002, 'White LP',67.90);: 
    INSERT INTO ITEM (ItemNumber, Description, Price) VALUES(1003, 'Bad Co. LP',11.45); 
    INSERT INTO SALE (SaleNumber, SaleDate, CustomerNumber, ItemNumber, 
Amount) VALUES(101, ' 07/07/95',1,1001,13.45); 
    INSERT INTO SALE (SaleNumber, SaleDate, CustomerNumber, ItemNumber, 
Amount) VALUES(102,'07/08/95',2,1002,67.90); 
    INSERT INTO SALE (SaleNumber, SaleDate, CustomerNumber, ItemNumber, 
Amount) VALUES(103, '07/09/95 ',3,1003,11.45) ; 
    INSERT INTO SALE (SaleNumber, SaleDate, CustomerNumber, ItemNumber, 
Amount) VALUES(104,'07/10/95',1,1002,67.90); 
    /*  Исключительные ситуации. */ 
    CREATE EXCEPTION SALE_TOO_LOW "The sale is too low. Only sales of $5 or 
more are allowed. "; COMMIT WORK; SET AUTODDL OFF; SET TERM Л ; 
    /* Хранимые процедуры. */ 
    CREATE PROCEDURE GET_CUSTOMER_BY_STATE AS BEGIN EXIT; END Л 
    CREATE PROCEDURE INSERTITEM AS BEGIN EXIT; END л 
    CREATE PROCEDURE INSERTSALE AS BEGIN EXIT; END л 
    ALTER PROCEDURE GET_CUSTOMER_BY_STATE (STATE CHAR(2)) 
    RETURNS (LASTNAME CHAR(30)) 
    AS 
    BEGIN 
    FOR SELECT LastName FROM CUSTOMER WHERE State = :State INTO :LastName 

    562 Часть IV. Темы повышенной сложности 

    DO 
    SUSPEND; 
    END 
    ALTER PROCEDURE INSERTITEM (ITEMNUMBER INTEGER, DESCRIPTION CHAR(30), 
PRICE FLOAT) AS 
    BEGIN 
    INSERT INTO ITEM (ItemNumber, Description, Price) VALUES (:ItemNumber, 
:Description, :Price); 
    END 
    ALTER PROCEDURE INSERTSALE (SALENUMBER INTEGER, 
    SALEDATE DATE, 
    CUSTOMERNUMBER INTEGER, 
    ITEMNUMBER INTEGER, 
    AMOUNT FLOAT) 
    AS 
    BEGIN 
    IF (:Amount < 5) THEN 
    EXCEPTION SALE_TOO_LOW; ELSE 
    INSERT INTO SALE 
    VALUES (:SaleNumber, :SaleDate, :CustomerNumber, :ItemNumber, :Amount); END 
    SET TERM ; л COMMIT WORK ; SET AUTODDL ON; 
    /*  Объявление внешних функций. */ 
    DECLARE EXTERNAL FUNCTION MAXINT 
    INTEGER, INTEGER 
    RETURNS INTEGER BY VALUE 
    ENTRY_POINT "Maxlnt" MODULE_NAME "IBUDFLIB.DLL"; 
    DECLARE EXTERNAL FUNCTION MININT 
    INTEGER, INTEGER 
    RETURNS INTEGER BY VALUE 
    ENTRY POINT "Minlnt" MODULE NAME "IBUDFLIB.DLL"; 

    На заметку 
    Этот файл содержится также на прилагаемом компакт-диске. 
    Запустите этот сценарий с помощью команды меню File^Run an ISQL Script в 
WISQL. После создания базы данных можно приступать к разработке приложения Web-сервера. 

    Создание приложения 

    Чтобы начать создание нового приложения Web-сервера выполните следующие действия. 
    1.    Выберите команду File^New, а затем дважды щелкните на пиктограмме 
Web Server Application в диалоговом окне New Items. 
    2.    В диалоговом окне New Web Server Application щелкните на кнопке OK, 
чтобы выбрать используемый по умолчанию тип приложения Web-сервера: 
ISAPI/NSAPI Dynamic Link Library. 

    Глава 28. Приложения для работы с Internet 563 

    3.    Измените предлагаемое по умолчанию имя модуля WebModule на wmSales. 
Компонент TWebModule подобен TDataModul, поскольку он содержит невизуальные 
компоненты приложения Web-сервера (например, TDataSetTableProducer, 
TPageProducer, TTable и т.д.). Если у вас есть модуль данных, содержащий 
необходимые для приложения невизуальные компоненты, то вы можете заменить 
TWebModule на TDataModule, добавив к нему компонент TWebDispatcher. Тогда он 
будет обладать теми же возможностями, что и TWebModule, поскольку в ответ на 
запросы HTTP он будет передавать эти запросы и ответы на них соответствующим 
объектам. Заметим, что в приложениях, предназначенных для работы с 
Web-сервером, можно использовать только один модуль данных Web.	I 
    Компонент TWebModule возглавляет группу объектов, отвечающих на запросы 
HTTP. Вам нужно настроить эти объекты, чтобы они отвечали на все запросы HTTP, 
необходимые для вашего приложения Они подобны обработчикам событий Delphi, 
поскольку реагируют на внешние события. Чтобы настроить модуль данных Web, 
выполните следующие действия. 
    4.    Создайте псевдоним BDE (если вы еще не сделали этого) для ссылки на 
созданную ранее базу данных InterBase SALES. Присвойте этому псевдониму имя IB_SALES. 
    5.    Поместите на форму компонент TDatabase и установите его свойство 
AliasName равным IB_SALES. Компонент TDatabase будет использоваться для того, 
чтобы при подключении вашего приложения к базе данных SALES не запрашивался 
пароль. Это необходимо, поскольку приложение будет представлять В собой 
библиотеку DLL, размещенную на вашем Web-сервере. Если BDE будет запрашивать 
имя пользова-теля и пароль, то окно регистрации не появится на экране, 
поскольку библиотека DLL Web-сервера не может непосредственно 
взаимодействовать с пользовательским интерфейсом на сервере или с броузером. В 
результате будет казаться, что при попытке открыть базу данных SALES 
приложение зависает. 
    6.    Присвойте в качестве значения свойств Name и DatabaseName компонента 
TDatabase имя dbSALES. Щ Задавая свойство DatabaseName, вы объявляете 
внутренний псевдоним BDE, который смогут использовать другие компоненты этого приложения. 
    7.    Установите свойство LoginPrompt компонента TDatabase в False, затем 
дважды щелкните на этом компоненте и переопределите следующие параметры: 

    USER NAME=SYSDBA PASSWORD=masterkey 

    Тогда приложение не будет запрашивать пароль при подключении к базе данных 
SALES. Поскольку свойство LoginPrompt имеет значение False, то компонент 
TDatabase будет использовать параметры, задаваемые свойством Params. 
    8. Поместите компонент TSession в модуль данных Web и задайте для его 
свойства AutoSessionName значение True. Тогда каждый экземпляр приложения 
Web-сервера будет использовать отдельный поток. При этом свойство SessionName 
всех компонентов модуля данных Web автоматически изменится и будет ссылаться 
на TSession. 
    9. Поместите на форму компонент TTable и присвойте ему имя taltem. Задайте 
для его свойства DatabaseName значение dbSALES, а для свойства TableName — 
значение ITEM. Эта таблица будет использоваться в главной форме приложения при 
выборе товара, для которого запрашивается информация об объеме продаж. 
    10. Щелкните дважды на этом компоненте и нажмите <Ctrl+A>, чтобы добавить 
на форму компоненты его полей. После этого закройте редактор полей. 
    11. Поместите на форму компонент TDataSource и задайте для его свойства 
Name значение dsltem. Измените его свойство DataSet, определив ссылку на taltem. 
    12. Поместите на форму компонент TQuery и присвойте ему имя quSales. 
Измените его свойство DatabaseName, определив ссылку на компонент dbSales 
класса TDatabase, созданный ранее. 
    13. Дважды щелкните на свойстве SQL компонента quSales и введите следующий оператор. 

    select  *   FROM SALE 
    WHERE  ItemNumber=:ItemNumber 

    14. Измените значение свойства DataSource компонента quSales на dsltem. 
Тогда его параметр ItemNumber может быть получен прямо из таблицы ITEM. 

    564	Часть IV. Темы повышенной сложности 564	Часть IV. Темы повышенной сложности 

    15. Щелкните дважды на свойстве Params этого компонента и задайте для 
свойства itemNumber тип данных Integer. 
    16. Щелкните дважды на самом компоненте TQuery и нажмите <CtrH-A>, чтобы 
добавить компоненты полей к модулю Web. Добавив поля на форму, закройте 
редактор полей. 
    17. Поместите компонент TQueryTableProducer (находится на вкладке Internet 
палитры компонентов) на форму модуля данных Web и присвойте ему имя qpSales. 
Задайте для его свойства Caption значение Item Sales. 
    18. Задайте для свойства Query компонента qpSales значение quSales. Таким 
образом будет установлена взаимосвязь между компонентом QueryTableProducer и 
вашим компонентом Query. Это означает, что qpSales будет выполнять код HTML, 
необходимый для отображения строк, возвращаемых компонентом quSales. 
    19. Щелкните дважды на свойстве Columns этого компонента для вызова 
редактора столбцов. Щелкните на кнопке Add All Fields, чтобы добавить столбцы 
для всех полей в таблице, а затем закройте диалоговое окно. 
    20. Измените параметры Border, CellPadding и CellSpacing свойства 
TableAttributes компонента QueryTableProducer с -1 на 1. Это приведет к тому, 
что в процессе выполнения приложения вокруг таблицы появится рамка и 
обозначатся границы ячеек. Это придаст таблице вид сетки, что и требовалось 
получить. При использовании заданного по умолчанию значения -1 рамка и сетка 
не видны. 
    21. Отредактируйте исходный текст модуля данных Web и добавьте следующую 
строку к разделу private объявления класса. 

    ScriptName: String; 

    Свойство ScriptName будет использоваться для создания списка связей 
каждого товара на начальной странице приложения. 
    22. Поместите на форму компонент TPageProducer и задайте для него имя 
ppitemList. Он будет использоваться для отображения списка товаров на 
начальной странице приложения. 
    23. Щелкните дважды на событии OnHTMLTag этого компонента и приведите 
исходный текст обработчика к следующему виду: 

    procedure  TwmSales.ppItemListHTMLTag(Sender:   TObject;   Tag:   TTag 
    const   TagString:   String;   TagParams:   TStrings;   var  ReplaceText:  
 String); 
    Items   :   String; begin 
    if  CompareText(TagString,    'ITEMLIST')   =  0  then begin Items   :=   
' ' ; taltem.First; while  not   taltem.Eof   do  begin 
    Items   :=Items   +   ' <A HREF="'   +   ScriptName  +   
'/runquery?ItemNumber='+ taltemltemNumber.AsString  +   '">>   +  
taltemDescription.AsString  + '</AXBR>' ; taltem.Next; end; end; 
    ReplaceText   :=   Items; end; 

    Этот обработчик события при получении дескриптора ITEMLIST создает 
форматированный список товаров из таблицы ITEM. Он обрабатывает этот список 
как одну длинную строку HTML, которую затем возвращает с помощью параметра RaplaceText.	"*"" 
    24. Каким образом этот компонент получает свойство ITEMLIST? От себя 
самого — дважды щелкните на его свойстве HTMLDoc и введите следующий код. 

    <HEAD> 
    <TITLE>Product Listing Web Server App</TITLE> 
    </HEAD> 
    <BODY> 
    <H2>Item Sales Informationx/H2> 

    Глава 28. Приложения для работы с Internet 565 

    <HR> 
    Click an item to view its sales.<P> 
    <#ITEMLISTXP> 
    </BODY> 
    </HTML> 

    Обратите внимание, что в теле этой программы имеется ссылка на #ITEMLIST. 
Таким способом уста-j навливается дескриптор HTML, который вызывает обработчик 
события OnHTMLTag. Затем дескриптору] возвращается список наименований товаров 
в виде строки HTML. 
    25. Щелкните дважды на самом модуле данных Web для вызова редактора действий. 
    26. Щелкните   на   кнопке   Add,   чтобы   добавить   новое   действие.   
Присвойте   этому   действию   имя 
    ppltemList и задайте для его свойства Default значение True. 
    27. Щелкните дважды на событии OnAction и введите следующий оператор. 

    Response.Content:=pp!temList.Content; 

    Этот оператор устанавливает для приложения список наименований, 
используемых по умолчанию. Для этого используется свойство Content компонента 
ppltemList В результате вызывается обработчик события OnHTMLTag компонента 
ppltemList и создается список наименований. 
    28. После возвращения в редактор действий щелкните на кнопке Add еще раз и 
добавьте новый элемент I под именем QueryAction. Задайте для его свойства 
Pathlnfo значение /runquery. Если вы внимательно посмотрите на код HTML для 
события OnHTMLTag, то заметите, что каждый выполняемый элемент содержит ссылку 
на /runquery. 
    29. Щелкните дважды на событии OnAction нового действия и введите 
следующие операторы. 

    if taltem.Locate('ItemNumber',   Request.QueryFields.Values['ItemNumber'], 
   []; then  begin 
    qpSales.Header.Clear; 
    qpSales.Header.Add('Sales   of:    '   +   taltemDescription.AsString); 
    Response.Content   :=  qpSales.Content; end else 
    Response. Content   :=   Format ('<HTMLXBODYXB>Item:    %s   not   
found</BX/BODYX, [Request.QueryFields.Values['ItemNumber']]); 

    С помощью этих операторов в модуле Web должен производиться поиск элемента 
в таблице t a ITEM, на котором выполнен щелчок в начальной форме приложения. 
Если этот элемент найден (а так должно быть всегда), то для компонента 
TQueryTableProducer задается заголовок, отражающий выбор наименования, а затем 
значение свойства Content компонента qpSales присваивается одноименному 
свойству Content компонента Response. При возвращении свойства Content 
компонента qpSales открывается запрос quSales, и его результат возвращается в 
виде строки HTML. 
    30. Закройте редактор действия, а затем дважды щелкните на событии 
OnCreate модуля данных Web в окне Object Inspector. Приведите исходный текст 
обработчика к следующему виду. 

    procedure  TwmSales.wmSalesCreate(Sender:   TObject); var 
    FN:   array[0..MAX_PATH   -   1]   of  char; begin 
    taltem.Open; 
    SetString(ScriptName, FN, GetModuleFileName(hlnstance, 
    ScriptName := ExtractFileName(ScriptName); end; 
    FN,   SizeOf(FN) 

    Здесь при первом создании модуля открывается компонент таблицы и задается 
базовое имя файла для перехода по гиперссылке, представленной наименованием товара. 
    31. Итак, создание приложения в основном завершено. Сохраните его на 
диске, если вы еще не сделали этого. Назовите форму модуля данных Web 
WEBSRVOO. PAS, а сам проект — WEBSRV. DPR. 
    32. Как отмечалось ранее, приложение Web-сервера должно быть реализовано в 
виде NSAPI/ISAPI DLL. Теперь создайте DLL, скомпилировав свой проект. После 
этого в каталоге, содержащем проект, вы найдете файл WEBSRV. DLL. 

    566 Часть IV. Темы повышенной сложности 

    Тестирование приложения Web-сервера 

    Чтобы протестировать вновь созданное приложение, скопируйте его в 
подкаталог сценариев главного каталога Web-сервера (например, подкаталог 
inetpub\scripts сервера Internet Information Server компании Microsoft.) После 
этого можно запустить приложение, открыв URL http: //<имя ИеЬ-сервера>/ 
<nonKaTaAor>/websrv.dii в Web-броузере. Замените <имя МеЬ-сервера> на имя 
вашего Web-сервера или его адрес IP и укажите имя подкаталога, в котором 
находится модуль DLL. Например, если вы подключены к серверу Microsoft US, 
который работает на том же компьютере, что и ваш броузер, то можете указать 
http://127.0.0.1/scripts/websrv.dll (или  
http://localhost/scripts/websrv.dll), поскольку 12^7. O.O.I— это зацикливающий 
фиктивный адрес TCP/IP. На рис. 28.6 показано, как выглядит новое приложение в Web-броузере. 

    На заметку 
    Чтобы приложение Web-сервера можно было использовать с сервером FastTrack 
Netscape, необходимо дополнительно выполнить следующие действия. 
    1. Скопируйте файл ISAPITER. DLL из каталога Bin Delphi в каталог \Net.scape\Server\Nsapi\Examples. 
    2. Завершите работу Web-сервера. Это можно сделать с помощью утилиты 
Netscape Administration, входящего в комплект поставки сервера FastTrack. 
    3. В файле \Netscape\Server\Httpd-<MMH Web-cepBepa>\Config\OBJ.CONF 
найдите строку Init fn=load-types n-iime-types^mime. types и добавьте после 
нее следующую строку: 
    Init funcs="handle-isapi, check-isapi, log-isa.pi" fn:="load~пlodules" 
shiib="c:/netscape/server/nsapi/examples/I SAP Iter.dll " Этот текст 
необходимо ввести как одну строку. 
    4. Затем в разделе <object name==default> файла OBJ.CONF найдите строку 
NameTrans fn^document-root root="C:Netscape/Server/docs и добавьте следующую строку: 
    NameTrans from="/scripts" fn°s"pfx2dir" 
dir="C:/Netscape/Server/docs/scripts" naine="isapi" 
    5. В конец файла OBJ. CONF добавьте следующие строки: 

    <0bject name="isapi"> 
    PathCheck fn=="check-isapi" 
    ObjectType fn=="force-type" type="magnus-internal/isapi" 
    Service fn="handle-isapi" 

    6. В конец файла \Netscape\Server\Httpd-<MMH Web-cepBepa^XMIMET'TYPES Добавьтё'СТрЬку: 

    type=magnus-internal/isapi exts=dll 

    7. Перезапустите Web-сервер. При получении административного сообщения 
сервера о том, что конфигурация была изменена вручную, щелкните на кнопке 
Apply в окне Administration, чтобы загрузить новые параметры конфигурации. 
    Щелкните на одном из наименований в списке, чтобы просмотреть информацию 
об объемах продаж этого товара. При этом активизируется запрос SQL, 
обеспечивающий сбор информации из таблицы SALE относительно объема продаж 
выбранного товара. На рис. 28.7 показано, как должна выглядеть страница, 
содержащая информацию об объеме продаж. 
    На этом работа приложения Web-сервера завершается. Чтобы выйти из него, 
завершите работу Webсервера. 
    В листингах 28.5—28.7 содержится полный исходный код приложения WEBSRV. DLL. 

    Глава 28. Приложения для работы с Internet 567 

    
    Рис. 28.6. Приложение базы данных для работы с  Web-сервером в окне 
броузера Netscape Navigator 
    
    Рис. 28.7. Страница Web-сервера с информацией об объеме продаж 
    
    568 Часть IV. Темы повышенной сложности 

    Листинг 28.5. Исходный текст файла проекта WEBSRV. DPR 

    library websrv; 
    uses 
    HTTPApp, 
    ISAPIApp, 
    websrvOO in 'websrvOO.pas' {wmSales: TWebModule}; 
    f$R *.RES} 
    exports 
    GetExtensionVersion, 
    HttpExtensionProc, 
    TerminateExtension; 
    begin 
    Application.Initialize; 
    Application.CreateForm(TwmSales, wmSales); 
    Application.Run; end. 
    Листинг 28.6. Исходный текст главного модуля WEBSRVOO . PAS приложения WEBSRV 
    unit websrvOO; interface 
    uses 
    Windows, Messages, SysUtils, Classes, HTTPApp, DBWeb, DBTables, Db; 
    type 
    TwmSales = class(TWebModule) 
    dbSALES: TDatabase; 
    Sessionl: TSession; 
    taltem: TTable; 
    taltemlTEMNUMBER: TIntegerField; 
    taltemDESCRIPTION:" TStringField; 
    taltemPRICE: TFloatField; 
    quSales: TQuery; 
    quSalesSALENUMBER: TIntegerField; 
    quSalesSALEDATE: TDateTimeField; 
    quSalesCUSTOMERNUMBER: TIntegerField; 
    quSalesITEMNUMBER: TIntegerField; 
    quSalesAMOUNT: TFloatField; 
    qpSales: TQueryTableProducer; 
    dsltem: TDataSource; 
    ppItemList: TPageProducer; 
    procedure ppItemListHTMLTag(Sender: TObject; Tag: TTag; const TagString: 
String; TagParams: TStrings; var ReplaceText: String); 
    procedure wmSalesppItemListAction(Sender: TObject; 
    Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); 
    procedure wmSalesQueryActionAction(Sender: TObject; 
    Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); 
    procedure wmSalesCreate(Sender: TObject); private 
    { Private declarations } 
    ScriptName: String; public 
    { Public declarations } end; 
    var 

    Глава 28. Приложения для работы с Internet 569 

    wmSales: TwmSales; implementation {$R *.DFM} 
    procedure TwmSales.ppItemListHTMLTag(Sender: TObject; Tag: TTag; 
    const TagString: String; TagParams: TStrings; var ReplaceText: String); var 
    Items : String; begin 
    if CompareText(TagString, 'ITEMLIST') = 0 then begin Items := ''; 
taltem.First; while not taltem.Eof do begin 
    Items := Items + '<A HREF="' + ScriptName + '/runquery?ItemNumber=' + 
taltemltemNumber.AsString + '">' + taltemDescription.AsString + 
    '</AXBR>' ; taltem.Next; end; end; 
    ReplaceText := Items; end; 
    procedure TwmSales.wmSalesppItemListAction(Sender: TObject; 
    Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin 
    Response.Content:=pp!temList.Content; end; 
    procedure TwmSales.wmSalesQueryActionAction(Sender: TObject; 
    Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin 
    if taltem.Locate('ItemNumber', Request.QueryFields.Values['ItemNumber'], 
[]) then begin 
    qpSales.Header.Clear; 
    qpSales.Header.Add('Sales of: ' + taltemDescription.AsString); 
Response.Content := qpSales.Content; end else 
    Response. Content := Format (' <HTMLXBODYXB>Item: %s not found</BX/BODYX/HTML>', 
    [Request.QueryFields.Values['ItemNumber']]); end; 
    procedure TwmSales.wmSalesCreate(Sender: TObject); var 
    FN: array[0..MAX_PATH - 1] of char; begin 
    taltem.Open; 
    SetString(ScriptName, FN, GetModuleFileName(hlnstance, FN, SizeOf(FN))); 
    ScriptName := ExtractFileName(ScriptName); end; 
    end. 
    Листинг 28.7. Текстовое представление формы модуля данных Web WEBSRVOO . 
DFM приложения WEBSRV 
    object wmSales: TwmSales OnCreate = wmSalesCreate Actions = < item 
    Default = True Name = 'ppItemList' OnAction = wmSalesppItemListAction end 

    570	Часть IV. Темы повышенной сложности 

item

Name = 'QueryAction' Pathlnfo = '/runquery' OnAction = wmSalesQueryActionAction end>

Left = 200 Top = 108 Height = 150 Width = 215

object dbSALES: TDatabase AliasName = 4B_SALES' DatabaseName = 'dbSales' LoginPrompt = False Params.Strings = ( 'USER NAME=SYSDBA' 1PASSWORD=masterkey') SessionName = 'Sessionl_6' Left = 128 Top = 72 end

object Session!: TSession AutoSessionName = True Left = 168 Top =40 end object taltem: TTable

DatabaseName = 'dbSales' SessionName = 'Sessionl_6' TableName = ЧТЕМ' Left = 8 Top = 8

object taltemlTEMNUMBER: TIntegerField FieldName = 'ITEMNUMBER' Required = True end

object taltemDESCRIPTION: TStringField FieldName = 'DESCRIPTION' Size = 30 end object taltemPRICE: TFloatField

FieldName = 'PRICE' end end

object quSales: TQuery DatabaseName = 'dbSales' SessionName = 'Sessionl_6' DataSource = dsltem SQL.Strings = (

'select * FROM SALE' 'WHERE ItemNumber=:ItemNumber')

Params.Data = {010001000A4954454D4E554D42455200030400000000000100} Left = 8 Top = 72

object quSalesSALENUMBER: TIntegerField FieldName = 'SALENUMBER' Origin = 'SALE.SALENUMBER' end

object quSalesSALEDATE: TDateTimeField FieldName = 'SALEDATE' Origin = 'SALE.SALEDATE' end

object quSalesCUSTOMERNUMBER: TIntegerField FieldName = 'CUSTOMERNUMBER' Origin = 'SALE.CUSTOMERNUMBER' end

Глава 28. Приложения для работы с Internet	571

object quSalesITEMNUMBER: TIntegerField FieldName = 'ITEMNUMBER' Origin = 'SALE.ITEMNUMBER' end

object quSalesAMOUNT: TFloatField FieldName = 'AMOUNT' Origin = 'SALE.AMOUNT' end end

object qpSales: TQueryTableProducer Caption = 'Item Sales' Columns = < item

FieldName = 'SALENUMBER' end item

FieldName = 'SALEDATE' end item

FieldName = 'CUSTOMERNUMBER' end item

FieldName = 'ITEMNUMBER1 end item

FieldName = 'AMOUNT' end>

Query = quSales TableAttributes.Border = 1 TableAttributes.CellSpacing = 1 TableAttributes.CellPadding = I Left = 64 Top = 72 end

object dsltem: TDataSource DataSet = taltem Left = 64 Top = 8 end

object ppItemList: TPageProducer HTMLDoc.Strings = ( 1<HEAD>'

'<TITLE>Product Listing Web Server App</TITLE>' '</HEAD>' '<BODY>'

'<H2>Item Sales Information</H2>' '<HR>'

'Click an item to view its sales.<P>' '<#ITEMLIST><P>' '</BODY>' '</HTML>')

OnHTMLTag = ppItemListHTMLTag Left = 128 Top = 8 end end

Отладка приложений Web-сервера

Поскольку Delphi позволяет отлаживать библиотеки динамической компоновки так же, как и выполняемые файлы, то при необходимости вы можете легко отладить свое приложение Web-сервера. Для этого необходимо, чтобы Web-сервер работал на вашем локальном компьютере — нельзя отлаживать приложение, работающее на удаленных Web-серверах. Чтобы отладить WEBSRV.DLL на локальном Web-сервере, выполните следующие действия.

572	Часть IV. Темы повышенной сложности

    item 
    Name = 'QueryAction' Pathlnfo = '/runquery' OnAction = 
wmSalesQueryActionAction end> 
    Left = 200 Top = 108 Height = 150 Width = 215 
    object dbSALES: TDatabase AliasName = 4B_SALES' DatabaseName = 'dbSales' 
LoginPrompt = False Params.Strings = ( 'USER NAME=SYSDBA' 
1PASSWORD=masterkey') SessionName = 'Sessionl_6' Left = 128 Top = 72 end 
    object Session!: TSession AutoSessionName = True Left = 168 Top =40 end 
object taltem: TTable 
    DatabaseName = 'dbSales' SessionName = 'Sessionl_6' TableName = ЧТЕМ' Left 
= 8 Top = 8 
    object taltemlTEMNUMBER: TIntegerField FieldName = 'ITEMNUMBER' Required = 
True end 
    object taltemDESCRIPTION: TStringField FieldName = 'DESCRIPTION' Size = 30 
end object taltemPRICE: TFloatField 
    FieldName = 'PRICE' end end 
    object quSales: TQuery DatabaseName = 'dbSales' SessionName = 'Sessionl_6' 
DataSource = dsltem SQL.Strings = ( 
    'select * FROM SALE' 'WHERE ItemNumber=:ItemNumber') 
    Params.Data = {010001000A4954454D4E554D42455200030400000000000100} Left = 
8 Top = 72 
    object quSalesSALENUMBER: TIntegerField FieldName = 'SALENUMBER' Origin = 
'SALE.SALENUMBER' end 
    object quSalesSALEDATE: TDateTimeField FieldName = 'SALEDATE' Origin = 
'SALE.SALEDATE' end 
    object quSalesCUSTOMERNUMBER: TIntegerField FieldName = 'CUSTOMERNUMBER' 
Origin = 'SALE.CUSTOMERNUMBER' end 

    Глава 28. Приложения для работы с Internet	571 

    object quSalesITEMNUMBER: TIntegerField FieldName = 'ITEMNUMBER' Origin = 
'SALE.ITEMNUMBER' end 
    object quSalesAMOUNT: TFloatField FieldName = 'AMOUNT' Origin = 
'SALE.AMOUNT' end end 
    object qpSales: TQueryTableProducer Caption = 'Item Sales' Columns = < item 
    FieldName = 'SALENUMBER' end item 
    FieldName = 'SALEDATE' end item 
    FieldName = 'CUSTOMERNUMBER' end item 
    FieldName = 'ITEMNUMBER1 end item 
    FieldName = 'AMOUNT' end> 
    Query = quSales TableAttributes.Border = 1 TableAttributes.CellSpacing = 1 
TableAttributes.CellPadding = I Left = 64 Top = 72 end 
    object dsltem: TDataSource DataSet = taltem Left = 64 Top = 8 end 
    object ppItemList: TPageProducer HTMLDoc.Strings = ( 1<HEAD>' 
    '<TITLE>Product Listing Web Server App</TITLE>' '</HEAD>' '<BODY>' 
    '<H2>Item Sales Information</H2>' '<HR>' 
    'Click an item to view its sales.<P>' '<#ITEMLIST><P>' '</BODY>' '</HTML>') 
    OnHTMLTag = ppItemListHTMLTag Left = 128 Top = 8 end end 
    Отладка приложений Web-сервера 
    Поскольку Delphi позволяет отлаживать библиотеки динамической компоновки 
так же, как и выполняемые файлы, то при необходимости вы можете легко отладить 
свое приложение Web-сервера. Для этого необходимо, чтобы Web-сервер работал на 
вашем локальном компьютере — нельзя отлаживать приложение, работающее на 
удаленных Web-серверах. Чтобы отладить WEBSRV.DLL на локальном Web-сервере, 
выполните следующие действия. 

    572	Часть IV. Темы повышенной сложности 

    1.   Завершите работу Web-сервера, если он в данный момент работает. Для 
отладки необходимо начать новый сеанс работы. 
    2.    Если вы подключены к локальному серверу Microsoft Internet 
Information Server, введите следующие параметры в диалоговом окне, 
открывающемся при выборе команды RunoRarameters. 
    Host  application:   c:\winnt\system32\inetsrv\inetinfo.exe Run 
parameters:   -e  w3svc 
    3. Если вы используете локальный сервер Netscape FastTrack Server, введите 
в диалоговом окне, открывающемся при выборе команды Run^Parameters, следующие параметры. 
    Host  application:   c:\Netscape\server\bin\httpd.exe 
    Run  parameters:   Netscape\server\httpd-<^Mj?  ffeJb-cepBepa>\conf.ig 
    Замените <имя Web-cepeepa> на имя вашего Web-сервера. 
    4. Если вы работаете с локальным сервером Windows 95 Personal Web Server, 
введите следующие параметры. 
    Host  application:   c:\Program Files\websvr\system\inetsw95.exe Run  
parameters:   -wSsvc 
    Конечно, каталоги и диски могут отличаться от каталогов и дисков, 
указанных в этих примерах. 
    5. Вернитесь в Delphi и установите точки прерывания, в которых вы хотите 
приостановить работу приложения в процессе отладки. 
    6. Щелкните на кнопке Run, чтобы запустить приложение. При этом начнется 
сеанс работы вашего Web-сервера под управлением отладчика Delphi. 
    7. Откройте Web-броузер и попытайтесь просмотреть DLL вашего приложения, 
как было описано выше. Когда приложение достигнет точки прерывания, поверх 
окна броузера появится всплывающее окно Delphi с приложением, приостановленным 
в этой точке. 
    Поскольку система Windows NT обеспечивает более надежное разделение задач 
и большую безопасность, то она не позволяет отлаживать DLL по умолчанию. Чтобы 
обеспечить возможность отладки DLL под Windows NT, выполните следующие действия. 
    1. В окне утилиты User Manager Windows NT выберите команду Policies'^User Rights. 
    2. Установите флажок Show Advanced User Rights. 
    3. В списке прав выберите Act As Part Of The Operating System. 
    4. Щелкните на кнопке Add, чтобы добавить свое имя (или имя вашей группы) 
к списку Grant To. 
    5. Щелкните на кнопке ОК. Теперь можно отлаживать библиотеки DLL под 
Windows NT. 

    Проект номер три: приложение ActiveForm 

    Следующее приложение будет состоять из одного элемента управления OCX, 
позволяющего запускать запросы SQL, выполнять хранимые процедуры и открывать 
таблицы из Web-страниц. Приложение WeblSQL, подобно утилите WISQL InterBase, 
позволит вводить запросы SQL и выполнять их. Единственное отличие от этой 
утилиты будет состоять в том, что наше приложение может быть внедрено в 
Web-страницу, с которой к нему осуществляется доступ. 
    Приложение будет создано в виде одной формы ActiveX, которая называется 
ActiveForm. Delphi скомпилирует эту форму в элемент управления OCX и создаст 
для него исходный HTML-файл. Вы скопируете этот элемент управления и 
соответствующий ему HTML-файл в корневой подкаталог документов Web-броузера. 
Это позволит вам открыть HTML-файл в Web-броузере и таким образом получить 
доступ к ActiveForm. 

    Построение приложения 

    Чтобы создать форму fmWeblSQL приложения WeblSQL, выполните следующие действия. 
    1.    Выберите команду File^New Application для создания нового 
приложения. Щелкните на пункте меню File^New, а затем щелкните на вкладке 
ActiveX и дважды щелкните на пиктограмме ActiveForm. 

    Глава 28. Приложения для работы с Internet 573 

    2.   В диалоговом окне ActiveForm Wizard введите имя нового элемента 
управления fmWebiSQL, задайте в качестве имени модуля WeblSQLO, а проекту 
присвойте имя WeblSQL. Затем щелкните на кнопке ОК. 
    3.    Появится сообщение о том, что вы не можете добавить новую форму к 
текущему проекту, поскольку он не является библиотекой ActiveX. Щелкните на 
кнопке ОК, чтобы инициализировать новый проект библиотеки ActiveX. 
    4.    Измените заголовок новой формы на WeblSQL и задайте для ее свойства 
AutoScroll значение True. 
    5.   Установите для свойства Height этой формы значение 325, а для 
свойства Width — значение 53.. При этом размер формы будет уменьшен примерно 
до размера области, выделяемой Delphi для элемента управления в 
сгенерированном HTML-файле. Задавая такой (или меньше) размер формы по 
умолчанию, вы избегаете возможного ее обрезания при просмотре в Web-броузере. 
Можно было бы внести изменения в сгенерированный Delphi HTML-файл, но в данном 
случае легче просто уменьшить размер элемента управления. 
    6.    Поместите на новую форму три компонента т Panel и удалите значения 
их свойства Caption. 
    7.    Задайте для свойства Align первой панели значение alTop, второй 
панели — alBottom и третьей панели — alClient. Настройте размеры верхней и 
нижней панелей таким образом, чтобы средняя и нижняя панели занимали примерно 
по 40% пространства формы по вертикали. 
    8.    Поместите в правой части формы компонент TSession и задайте для его 
свойства AutoSessionName значение True. Добавив на форму компонент TSession, 
вы исключите возможность взаимных помех отдельных потоков выполнения приложения. 
    9.    Поместите компонент TDatabase рядом с компонентом TSession и задайте 
для его свойства Name значение dbWeblSQL. Для свойства DatabaseName тоже 
установите значение dbWeblSQL. Этот компонент будет координировать доступ к 
базе данных приложения WeblSQL. 
    10. Поместите компонент TQuery рядом с компонентом TDatabase и присвойте 
ему имя quWebiSQL. 
    11. Поместите компонент ТТаЫе рядом с компонентом TQuery и присвойте ему 
имя taWeblSQL. 
    12. Установите для свойства DatabaseName компонентов ТТаЫе и TQuery 
значение dbWeblSQL. 
    13. Поместите компонент TDataSource рядом с компонентом ТТаЫе и присвойте 
ему имя dsWeblSQ-Установите его свойство DataSet в quWebiSQL. 
    14. Поместите компонент TComboBox в левой части верхней панели и присвойте 
ему имя cbAliases. Этот список будет использоваться для выбора псевдонима BDE 
для выполнения ваших запросов. 
    15. Поместите под ним еще один компонент TComboBox. Присвойте ему имя 
cbTables. Он будет использоваться для выбора открываемой таблицы (приложение 
WeblSQL будет открывать таблицы напрямую, так же как и выполнять запросы SQL). 
    16. Очистите свойство Text обоих комбинированных списков. 
    17. Поместите компоненты TLabel в левой части обоих комбинированных 
списков и укажите для них соответствующие заголовки. 
    18. Поместите компонент DBNavigator справа от cbTables и сожмите его до 
минимально возможного размера. Назовите его dnaWeblSQL. 
    19. Прямо над DBNavigator поместите компонент TSpeedButton и присвойте ему 
имя sbGo. Эта кнопка будет использоваться для непосредственного выполнения 
ваших запросов, поэтому для ее свойства Glyph задайте растровое изображение, 
адекватно отражающее эту функцию. Для этой цели подойдет растровое изображение 
VCRPLAY из подкаталога Images \Buttons главного каталога Delphi. 
    20. Поместите компонент TLabel слева от sbGo и задайте для него 
соответствующий заголовок. 
    21. Поместите на среднюю панель компонент TMemo, присвойте ему имя meQuery 
и очистите значение его свойства Lines. Задайте для его свойства Align 
значение alClient, чтобы этот компонент занимал всю панель. 
    22. Поместите на нижнюю панель компонент TDBGrid и присвойте ему имя 
dgdResults. Задайте для его свойства Align значение alClient. Здесь будут 
отображаться результаты выполнения запросов. 
    23. Установите для свойства DataSource компонентов DBNavigator и DBGrid 
значение dsWeblSQL. На рис. 28.8 показано, как должна выглядеть форма fmWebiSQL. 

    574	Часть IV. Темы повышенной сложности 

    .Рис. 2А и1. Я«й главной формы fmWeblSQL приложения WeblSQL 

    Добавление кода 

    Теперь, когда разработан внешний вид формы, можно приступить к встраиванию 
в нее программного кода, обеспечивающего дееспособность формы. Чтобы задать 
программный код для приложения WeblSQL, выполните следующие действия. 
    1.    Выберите в окне Object Inspector саму форму, затем щелкните дважды 
на ее событии OnCreate и введите следующие операторы. 

    Sessionl.GetAliasNames(cbAliases.Items); if  cbAliases.Items.Count>0  then begin 
    cbAliases.Itemlndex:=0; 
    cbAliasesClick(Sender); end else  cbAliases.Itemlndex:=-!; 

    Таким образом форма будет инициализировать комбинированный список 
cbAliases псевдонимов для выбора. Если такие псевдонимы существуют, то первый 
из них будет использоваться и открываться в приложении. 
    2. Затем щелкните на компоненте cbAliases и дважды щелкните на значении 
его обработчика события OnClick в окне Object Inspector. Введите в окне 
редактирования исходного текста следующие операторы. 

    if  cbAliases. Texto1 '   then with dbWeblSQL do begin 
    If Connected then Close; 
    AliasName:=cbAliases.Items[cbAliases.Itemlndex]; 
    cbTables.Items.Clear; 
    cbTables.Text:=''; 
    Open; 
    Sessionl.GetTableNames(AliasName,'',not(IsSQLBased), 
    IsSQLBased,cbTables.Items); end; 

    Этот обработчик события будет обеспечивать выбор псевдонима из 
комбинированного списка cbAliases для подключения к нему dbWeblSQL. Он также 
будет вносить список таблиц этого псевдонима в комбинированный список cbTables. 

    Глава 28. Приложения для работы с Internet 575 

    3. Добавьте в обработчик события OnClick компонента cbTables следующие операторы. 

    if  cbTables.Texto''   then With  taWeblSQL do begin 
    If Active   then  Close; 
    TableName:=cbTables.Items[cbTables.Itemlndex]; 
    dsWeblSQL.DataSet:=taWebISQL; 
    Open; end; 

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

    with  quWeblSQL  do  begin If Active  then Close; 
    if  taWeblSQL.Active   then  taWeblSQL.Close; SQL.Clear; 
    SQL.AddStrings(meQuery.Lines); dsWeblSQL.DataSet:=quWebISQL; If   
(CompareText(Copy(SQL.Text,1,4) , 'OPEN1)=0)   then  begin 
    taWeblSQL.TableName:=Trim(Copy(SQL.Text,6,30)); 
    dsWeblSQL.DataSet:=taWebISQL; 
    taWeblSQL.Open; 
    end  else   If   (CompareText(Copy(SQL.Text,1,4),'EXEC1)=0)   then  
ExecSQL else  Open; end; 

    Данные операторы удаляют значение свойства SQL компонента quWeblSQL и 
присваивают ему значение, содержащиеся в свойстве Lines компонента meQuery. 
Затем проверяется тип запроса, чтобы определить, какие действия выполняет 
запрос: открывает таблицу, выполняет хранимую процедуру или просто опрашивает 
базу данных. Обратите внимание на поддержку виртуальной команды Open. Несмотря 
на то. что команда Open не открывает таблиц в ANSI SQL, WeblSQL позволяет 
использовать ее как быстродействующую альтернативу для команды Select * FROM 
tablename. Вы можете добавить сколь угодно много таких виртуальных команд. 
Если таблица открыта с помощью команды Open или комбинированного списка 
cbTables, то ее можно редактировать в сетке dgdResults. 

    Совершенствование приложения 

    Теперь, когда приложение создано, можно приступать к его доработке и 
тестированию. Для доработки приложений ActiveX используются параметры, доступ 
к которым можно получить с помощью команды Web Deployment Options из меню 
Project. Эти параметры позволяют указать, где конкретно будет установлено 
приложение и как к нему будет осуществляться доступ из Web-броузера. Для 
доработки приложения ActiveForm, выполните следующие действия. 
    1.   Сохраните новый модуль под именем WeblSQL.PAS, а файл проекта — под 
именем WeblSQL.DPR. 
    2.    Нажмите <Ctrl+F9>, чтобы скомпилировать приложение и получить 
выполняемый файл (он будет иметь расширение OCX). 
    3.   Выберите команду Project^Web Deployment Options. 
    4.    В диалоговом окне Web Deployment Options необходимо задать как 
минимум три параметра. Нужно указать Target dir, Target URL и HTML dir. Target 
dir показывает Delphi, где разместить модуль ActiveForm OCX. Target URL задает 
URL, который будет внедрен в HTML-файл для ссылки на элемент управления 
ActiveForm. Он представляет собой относительный путь от корневого каталога 
документов вашего сервера. HTML dir определяет, где будет размещаться 
соответствующий элементу управления HTML-файл. 
    Значения этих параметров будут зависеть от вашего Web-сервера, но в 
качестве значений Target dir и HTML dir обычно выбирается один и тот же 
каталог. Нет причины отделять элемент управления осх от соответствующего ему HTML-файла. 

    576	Часть IV. Темы повышенной сложности 

    Например, чтобы установить форму на Web-сервере Microsoft US, можно 
использовать следующие параметры. 

    Target  dir:   C:\inetpub\wwwroot 
    Target   URL:   / 
    HTML  dir:      C:\inetpub\wwwroot 

    При этом элемент управления и соответствующий HTML-файл будут размещены в 
корневом каталоге документов сервера, и сгенерированный URL будет ссылаться на 
этот каталог. Помимо этих параметров можно задать еще множество других, но 
пока можно этого не делать. Задав обязательные параметры, щелкните на кнопке 
ОК для выхода из диалогового окна Web Deployment Options. 
    Хотя поддержка ActiveX — это в первую очередь функция Web-броузера, 
элемент управления ActiveX можно протестировать даже без помощи Web-сервера. 
Для этого замените каталоги в диалоговом окне Web Deployment Options на 
локальный каталог. Например, можно задать следующие каталоги: 

    Target dir: c:\mywebpages 
    Target URL: file:///с:/mywebpages 
    HTML dir:  c:\mywebpages 

    Теперь элемент управления осх и соответствующий ему HTML-файл будут 
располагаться в каталоге c:\mywebpages, а сгенерированный HTML-файл будет 
ссылаться на элемент управления как на локальный файл. 
    5. Задав нужные параметры в этом диалоговом окне, вы можете завершить 
подготовку приложения с помощью команды меню Project^Web Deploy. При этом 
элемент управления ActiveForm OCX и соответствующий HTML-файл будут 
скопированы в каталоги, указанные вами в диалоговом окне Web Deployment 
Options.	~"~ """ 

    Тестирование приложения 

    На заметку 
    Прежде чем тестировать новое приложение ActiveForm, удостоверьтесь, что в 
диалоговом окне Internet Explorer 3 ViewoOptions^Security включен режим Enable 
ActiveX controls and plug-ins. Приложение ActiveForm на самом деле 
представляет собой лишь элемент управления ActiveX, внедренный в 
HTML-документ, поэтому для его корректной работы необходимо иметь возможность 
подгружать и запускать элементы управления ActiveX.	, « 
    Как и разработанное ранее приложение, использующее функции программных 
интерфейсов NSAPI и ISAPI, ActiveForm представляет собой приложение для 
Web-сервера. Чтобы протестировать его, необходи-\ю_запустить Web-сервер, а 
затем открыть сгенерированный Delphi HTML-файл с использованием Web-броузера, 
поддерживающего элементы управления ActiveX. В настоящее время ActiveX 
поддерживаются только броузерами IE 3.x компании Microsoft, поэтому вы не 
сможете протестировать приложение с использованием Netscape Navigator или 
других броузеров. Когда-то сообщалось, что броузеры Netscape могут 
поддерживать ActiveX, но до настоящего времени они ориентированны на Java и Java-апплеты. 
    Как и в случае с приложением NSAPI/ISAPI, если Web-сервер и броузер 
работают на одном компьютере, то на новое приложение можно ссылаться, 
используя зацикливающий фиктивный адрес TCP/IP. Если вы установили приложение 
в корневой каталог документов сервера, то можете ссылаться на него по адресу 
http: //127 . О . О . 1/webisql. htm. Когда вы откроете файл НТМ, на экране 
появится диалоговое окно с сообщением о том, что в файле задействован элемент 
управления WeblSQL.OCX. Для установки этого компонента щелкните на кнопке Yes 
в диалоговом окне. На рис. 28.9 показано, как должен выглядеть этот компонент 
в процессе работы. 
    Эту форму можно использовать для ввода запросов SQL, выполнения хранимых 
процедур и редактирования таблиц. Обратите внимание, что, поскольку этот 
компонент использует BDE, то необходимо, чтобы система BDE была установлен на 
том же компьютере, что и Web-броузер. 

    Глава 28. Приложения для работы с Internet 19 Delphi 3 и системы 
клиент/сервер 577 

    Поскольку созданная вами форма ActiveForm на самом деле представляет собой 
лишь элемент управления ActiveX, который может содержать другие элементы 
управления, то ее можно использовать не только в приложениях для Web. Ее можно 
добавить к палитре компонентов Delphi и использовать в обычных приложениях 
Windows, как любой другой элемент управления OCX. Для этого выберите команду 
меню Component^lmport ActiveX Conrtol. 
    Рис. 28.9. Приложение WebiSQL ActiveForm в окне Internet Explorer 3.x Для 
выхода из приложения закройте броузер и завершите работу Web-сервера. 
    Броузер Microsoft IE 3.x кэширует элементы управления ActiveX в каталоге 
OCCASHE, который находится в каталоге Windows (или WINNT). Если вам необходимо 
изменить и переустановить компонент ActiveForm, то перед его просмотром 
удостоверьтесь, что вы удалили его из каталога OCCASHE, и IE 3.x не использует 
кэ-шированную копию вместо новой. 
    Содержимое этого каталога автоматически не корректируется, и при просмотре 
Web-страниц, содержащих элементы управления ActiveX, каталог продолжает 
заполняться. Поэтому время от времени следует чистить каталог, чтобы не 
заполнять жесткий диск множеством ненужных элементов ActiveX. Элементы 
управления OCX бывают достаточно большими, и значительное дисковое 
пространство может быть занято элементами, которые никогда не пригодятся. 

    Проект номер четыре: компактное клиентное приложение 

    В этом проекте мы займемся созданием компактного клиентного приложения 
(thin-client app), которое можно запускать через Internet. Благодаря 
особенностям его разработки, оно также может работать в корпоративной 
интрасети (intranet) или локальной сети. 

    578 Часть IV. Темы повышенной сложности 

    Этот проект является более смелым чем те, которые мы разрабатывали до сих 
пор. Это связано, главным образом, с тем, что мы собираемся перестроить 
несколько форм демонстрационного приложения Delphi MASTAPP. А именно, мы 
построим компактную клиентную версию формы заказа приложения MASTAPP 
(EDORDERS.PAS). Мы превратим EDORDERS в отдельное приложение, не требующее 
использования BDE. При этом потребуется удалить все его связи с модулем данных 
MASTAPP и создать СОМ-сервер, который будет выполнять работу с данными. 

    На заметку 
    I: Я бы не советовал вам производить изменения в демонстрационных 
программах Delphi. Лучше оставить их нетронутыми, чтобы иметь возможность 
воспользоваться ими позже. Создавайте копии форм, которые вы хотите изменить, 
и модифицируйте их. 
    Различные варианты компактных клиентных приложений приобретают все большую 
популярность благодаря легкости управления ими. Для работы приложения на 
основе такого подхода не требуется устанавливать BDE, драйверы ODBC и другие 
специальные драйверы. По существу, компактное клиентное приложение передает 
все необходимые данные другому приложению, которое непосредственно 
осуществляет связь с сервером базы данных. Приложения второго типа обычно 
называются серверами приложения (application server) и могут обслуживать 
несколько клиентных приложений одновременно. Использование сети позволяет 
осуществлять централизованное управление бизнес-правилами и объектами базы 
данных, уведомлять о нарушении ограничений и бизнес-правил приложения и 
сэкономить ресурсы из-за небольших размеров приложений. Одним словом, вы 
получаете все блага мира. 
    Изменения, необходимые для преобразования формы Orders приложения MASTAPP 
в компактное клиентное приложение, слишком многочисленны, чтобы перечислять их 
отдельно. Поэтому я буду акцентировать внимание на основных моментах и приведу 
общую схему действий, необходимых для преобразования разработанных вами 
приложений в компактную клиентную модель. В этом вам поможет приведенный 
исходный код как для серверной, так и для клиентной части приложения. 
    Для создания компактной клиентной версии EDORDERS, выполните следующие действия. 
    1.	Создайте приложение сервера данных, которое использует модуль данных 
MASTDATA приложения MASTAPP. 
    2.	Создайте модуль удаленных данных на сервере данных, включающий четыре 
компонента Provider. 
    3.	Укажите в качестве имен таблиц для этих четырех компонентов таблицы 
CUSTOMER, ORDERS, ITEM и 
    PARTS МОДУЛЯ ДаННЫХ MASTDATA. 
    4.	Запустите приложение-сервер, чтобы зарегистрировать его как СОМ-сервер. 
    5.	Создайте новый проект приложения и локальные копии форм EDORDERS и 
SRCHDLG приложения MASTAPP. 
    6.	Из обеих форм удалите все прямые ссылки на модуль данных MASTDATA 
приложения MASTAPP. 
    7.	Поместите на форму четыре компонента ClientDataSet, а также четыре 
компонента DataSource. 
    8.   Установите связь компонента ClientDataSet с компонентом DataSource, и 
DataSource — с компонентами, содержащими данные формы. 
    9.    Поместите на форму компонент RemoteServer и установите для него 
связь с СОМ-сервером, созданным ранее. 
    10. Установите связь компонента ClientDataSet с компонентом ReportServer и 
объектами, на которые он ссылается. 
    11. Создайте локальные копии полей выборки и вычисляемых полей, изначально 
определенных в модуле MASTDATA, поскольку они недоступны для сервера данных. 
    12. Переустановите связи вида "главная-подчиненная" между таблицами, 
поскольку они также не обеспечиваются сервером данных. 
    13. Внесите другие изменения, необходимые для достижения завершенности 
формы EDORDERS, и удалите ее связи с проектом MASTAPP. 
    На рис. 28.10 показано, как выглядит компактная клиентная версия 
приложения EDORDERS в процессе выполнения. 
    При преобразовании созданных вами приложений к компактной клиентной модели 
принимайте во внимание следующие общие советы. 

    Глава 28. Приложения для работы с Internet	579 

    Вам потребуются два отдельных приложения — серверное и клиентное. 
Серверное приложение должно содержать обычные компоненты, используемые Delphi 
для создания приложения базы данных — ТТаЫе, TQuery, TDatabase и другие. 
Клиентное приложение должно содержать компоненты RemoteServer и ClientDataSet, 
способные осуществлять связь этих элементов с серверным приложением. 
    Компоненты-потомки TDataSet можно экспортировать прямо из модуля удаленных 
данных без использования компонента TProvider. Здесь я использовал компоненты 
TProvider, поскольку мне требовался обычный (не удаленный) модуль данных. Для 
экспортирования DataSet из модуля удаленных данных щелкните на нем правой 
кнопкой мыши и выберите из всплывающего меню пункт Export...from data module. 

    Рис. 28.10. Перестроенная версия приложения EDORDERS 

    Q  Необходимо запустить приложение, содержащее модуль удаленных данных, 
чтобы зарегистрировать содержащийся в нем СОМ-сервер. После этого можно 
завершить приложение. 
    Q  Компонент RemoteServer работает как шлюз между клиентньш и серверным 
приложением. Он, в некотором смысле, аналогичен компоненту TDatabase, 
поскольку осуществляет обмен данными между сервером и клиентом. Ни один из 
элементов управления в клиентном приложении не ссылается непосредственно на 
сервер, за исключением компонента RemoteServer. 
    Q  Поля выборки, вычисляемые поля и связи вида "главная-подчиненная" 
сервером не экспортируются. Вам необходимо самостоятельно установить их в 
клиентных приложениях, чтобы эти приложения могли их использовать. 
    О  При ссылках на серверное приложение, расположенное на другом 
компьютере, используется свойство ComputerName компонента RemoteServer. Имя 
компьютера должно соответствовать универсальному соглашению о присвоении имен 
UNC (Universal Naming Convention). Теоретически, компьютеры клиента и сервера 
могут находиться на очень большом расстоянии друг от друга. Поскольку в 
глобальных сетях (Wide Area Network) они могут взаимодействовать через системы 
DCOM или OLEnterprise, то ваше приложение должно работать корректно. 
    Итак, оценив задачу с высоты птичьего полета, давайте спустимся на грешную 
землю и проверим наши выводы. В листингах 28.8-28.12 представлен исходный 
текст компактной клиентной версии приложения 
    
    EDORDERS. 

    580 Часть IV. Темы повышенной сложности 

    На заметку 
    Для краткости я опустил в этих листингах исходный текст форм DFM, 
поскольку некоторые из форм содержат большое количество данных в бинарном 
формате (например, растровые изображения). Полный исходный текст можно найти 
на прилагаемом компакт-диске, 

    Листинг 28.8. Исходный текст файла проекта сервера данных ORDSRV. DPR 

    program ordsvr; 
    uses 
    Forms, 
    ordsvrOO in 'ordsvrOO.pas' (Forml}, 
    ordsvr_TLB in 'ordsvr_TLB.pas', 
    ordsvrOl in 'ordsvrOl.pas' (dmOrders: TDataModule} {dmOrders: CoClass}, 
    DataMod in 'D:\Program Files\Borland\Delphi 
3\Demos\DB\MASTAPP\Datamod.pas' {MastData: TDataModule}; 
    {$R *.RES} {$R *.TLB} 
    begin 
    Application.Initialize; 
    Application.CreateForm(TForml, Forml); 
    Application.CreateForm(TMastData, MastData); 
    Application.Run; end. 
    
    Листинг 28.9. Исходный текст модуля удаленных данных dmOrder приложения ORDSRV 

    unit ordsvrOl; interface 
    uses 
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
ComServ, ComObj, VCLCom, StdVcl, BdeProv, DataBkr, ordsvr_TLB, Provider; 
    type 
    TdmOrders = class(TDataModule, IdmOrders) 
    prOrders: TProvider; 
    prltems: TProvider; 
    prParts: TProvider; 
    prCust: TProvider; private 
    { Private declarations } public 
    { Public declarations } protected 
    function Get_prOrders: IProvider; safecall; 
    function Get_prltems: IProvider; safecall; 
    function Get_prParts: IProvider; safecall; 
    function Get_prCust: IProvider; safecall; end; 
    var 
    dmOrders: TdmOrders; 
    implementation uses DataMod; {$R *.DFM} 

    Глава 28. Приложения для работы с Internet 581 

    function TdmOrders.Get_prOrders: IProvider; begin 
    Result := prOrders.Provider; end; 
    function TdmOrders.Get_prltems: IProvider; begin 
    Result := prltems.Provider; end; 
    function TdmOrders.Get_prParts: IProvider; begin 
    Result := prParts.Provider; end; 
    function TdmOrders.Get_prCust: IProvider; begin 
    Result := prCust.Provider; end; 
    initialization 
    TComponentFactory.Create(ComServer, TdmOrders, 
    Class_dmOrders, ciMultilnstance); end. 

    На заметку 
    Главная форма приложения ORDSRV на самом деле никак не используется. Она 
просто создается по умолчанию при запуске нового приложения. Хотя вы вполне 
можете отменить вывод этой формы на экран, я не рекомендую так поступать, 
поскольку вы не сможете завершить работу приложения без использования команды 
Program Reset меню Run Delphi. 
    Кроме того, модуль данных MASTDATA из демонстрационного приложения MASTAPP 
остается неизменным. Поэтому я опустил его в приведенных здесь листингах 
исходного текста, 

    Листинг 28.10. Исходный текст проекта клиентного приложения ORDCLIENT . DPR 

    program ordclient; 
    uses Forms, 
    EDOrders in 'Edorders.pas' {EdOrderForm}, SrchDlg in 'SrchDlg.pas' {SearchDlg}; 
    ($R *.RES} 
    begin 
    Application.Initialize; 
    Application.CreateForm(TEdOrderForm, EdOrderForm); 
    Application.CreateForm(TSearchDlg, SearchDlg); 
    Application.Run; end. 

    Листинг 28.11. Исходный текст программного модуля компактной клиентной 
версии формы EDORDERS . PAS приложения MASTAPP 

    unit EDOrders; interface 
    uses 
    SysUtils, Windows, Messages, Classes, Graphics, Controls, 

    582 Часть IV. Темы повышенной сложности 

    Dialogs, Forms, StdCtrls, DBGrids, DBCtrls, DBTables, DB, Buttons, Grids, 
DBLookup, ExtCtrls, Mask, DBClient, ComCtrls; 
    type 
    TEdOrderForm = class(TForm) HeaderPanel: TPanel; ShipToAddlEdit: TDBEdit; 
ShipToAdd2Edit: TDBEdit; CustAddlEdit: TDBEdit; CustAdd2Edit: TDBEdit; 
ShipToCompanyEdit: TDBEdit; CustCityEdit: TDBEdit; CustStateEdit: TDBEdit; 
CustZipEdit: TDBEdit; ShipToCityEdit: TDBEdit; ShipToStateEdit: TDBEdit; 
ShipToZipEdit: TDBEdit; Modelndicator: TLabel; POEdit: TDBEdit; TermsCombo: 
TDBComboBox; PaymentCombo: TDBComboBox; ShipViaCombo: TDBComboBox; Speedbar: 
TPanel; DBNavBtns: TDBNavigator; DBEditBtns: TDBNavigator; ActiveSource: 
TDataSource; ItemsGrid: TDBGrid; AmountPaidEdit: TDBEdit; TotalEdit: TDBEdit; 
TaxTotalEdit: TDBEdit; FreightEdit: TDBEdit; AmountDueEdit: TDBEdit; 
TaxRateEdit: TDBEdit; CloseBtn: TButton; CancelBtn: TButton; PostBtn: TButton; 
Image1: ТImage; PrintBtn: TSpeedButton; SoldByCombo: TDBLookupComboBox; 
Bevell: TBevel; DBTextl: TDBText; CustNoEdit: TDBEdit; dsOrders: TDataSource; 
dsltems: TDataSource; dsCustomer: TDataSource; csOrders: TClientDataSet; 
csltems: TClientDataSet; csCust : TClientDataSet; csParts : TClientDataSet; 
dsParts: TDataSource; RemoteServerl: TRemoteServer; csOrdersOrderNo: 
TFloatField; csOrdersCustNo: TFloatField; csOrdersSaleDate: TDateTimeField; 
csOrdersShipDate: TDateTimeField; csOrdersEmpNo: TIntegerField; 
csOrdersShipToContact: TStringField; csOrdersShipToAddrl: TStringField; 
csOrdersShipToAddr2: TStringField; csOrdersShipToCity: TStringField; 
csOrdersShipToState: TStringField; csOrdersShipToZip: TStringField; 
csOrdersShipToCountry: TStringField; csOrdersShipToPhone: TStringField; 
csOrdersShipVIA: TStringField; 

    Глава 28. Приложения для работы с Internet 583 

    csOrdersPO: TStringField; 
    csOrdersTerms: TStringField; 
    csOrdersPaymentMethod: TStringField; 
    csOrdersItemsTotal: TCurrencyField; 
    csOrdersTaxRate: TFloatField; 
    csOrdersFreight: TCurrencyField; 
    csOrdersAmountPaid: TCurrencyField; 
    csOrdersTaxTotal: TCurrencyField; 
    csOrdersAmountDue: TCurrencyField; 
    csItemsOrderNo: TFloatField; 
    csItemsItemNo: TFloatField; 
    csItemsPartNo: TFloatField; 
    csItemsQty: TIntegerField; 
    csItemsDiscount: TFloatField; 
    csIternsDescription: TStringField; 
    csItemsSellPrice: TCurrencyField; 
    csItemsExtPrice: TCurrencyField; 
    DateTimePickerl: TDateTimePicker; 
    SaleDateEdit: TDBEdit; 
    csOrdersCustCompany: TStringField; 
    DBLookupComboBoxl: TDBLookupComboBox; 
    procedure ItemsGridEnter(Sender: TObject); 
    procedure ActiveSourceStateChange(Sender: TObject); 
    procedure ItemsGridExit(Sender: TObject); 
    procedure CancelBtnClick(Sender: TObject); 
    procedure PostBtnClick(Sender: TObject); 
    procedure PickPartNo(Sender: TObject); 
    procedure PickDate(Sender: TObject); 
    procedure PrintBtnClick(Sender: TObject); 
    procedure SaleDateEditKeyPress(Sender: TObject; var Key: Char); 
    procedure OrdersSourceStateChange(Sender: TObject); 
    function DataSetApplyUpdates(DataSet: TDataSet; Apply: Boolean): Boolean; 
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
    procedure SoldByComboKeyPress(Sender: TObject; var Key: Char); 
    procedure csOrdersCalcFields(DataSet: TDataSet); 
    procedure csItemsCalcFields(DataSet: TDataSet); 
    procedure FormShow(Sender: TObject); 
    procedure dsOrdersDataChange(Sender: TObject; Field: TField); 
    procedure DateTimePickerlExit(Sender: TObject); 
    procedure SaleDateEditDblClick(Sender: TObject); 
    procedure CloseBtnClick(Sender: TObject); public 
    procedure Enter; 
    procedure Edit(OrderNo: Double); end; 
    var 
    EdOrderForm: TEdOrderForm; 
    implementation uses SrchDlg; ($R *.DFM} 
    const 
    DatasetStates: array[TDataSetState] of string = 
    ('Not active', 'Browsing', 'Editing', 'Inserting', '', '', '', '', '', '') 
    HelpTopicEdit = 2; HelpTopicBrowse = 3; 
    { ================== Общедоступные методы 
    procedure TEdOrderForm.Enter; 

    584 Часть IV. Темы повышенной сложности 

    begin 
    dsOrders.OnStateChange := OrdersSourceStateChange; try 
    csOrders.Open; csOrders.Insert; ShowModal; finally 
    dsOrders.OnStateChange := nil; end; end; 
    procedure TEdOrderForm.Edit(OrderNo: Double); begin 
    dsOrders.OnStateChange := OrdersSourceStateChange; try 
    csOrders.Open; 
    csOrders.Locate('OrderNo', OrderNo, []); ShowModal; finally 
    dsOrders.OnStateChange := nil; end; end; 
    { ======================  Обработчики событий  ====================== } 
    { Следующие два методы позволяют навигаторам обслуживать таблицы Orders и 
Items путем переключения значения свойства ActiveSource. } 
    procedure TEdOrderForm.ItemsGridEnter(Sender: TObject); begin 
    ActiveSource.Dataset := csltems; end; 
    procedure TEdOrderForm.ItemsGridExit(Sender: TObject); begin 
    ActiveSource.Dataset := csOrders; end; 
    { Обновление индикатора режима при изменении состояния свойства "Active" 
источника данных. } 
    procedure TEdOrderForm.ActiveSourceStateChange(Sender: TObject); begin 
    with ActiveSource do begin 
    if Dataset <> nil then Modelndicator.Caption := 
    Format('[%S: %S]', [Dataset.Name, DatasetStates[State]]); if State in 
[dsEdit, dslnsert] then begin 
    HelpContext := HelpTopicEdit; Modelndicator.Font.Color := clRed; end" else begin 
    HelpContext := HelpTopicBrowse; Modelndicator.Font.Color := clBlue; end; 
end; end; 
    { Включение и отключение кнопок при изменении состояния таблицы заказов. } 
    procedure TEdOrderForm.OrdersSourceStateChange (Sender: TObject); 
    begin 
    end; 

    Глава 28. Приложения для работы с Internet	585 

    { Выбор даты выписки счета из календаря. } 
    procedure TEdOrderForm.PickDate(Sender: TObject); 
    begin 
    end; 
    { Нажатие <Ctrl+Enter> в SaleDate приводит к открытию окна PickDate. } 
    procedure TEdOrderForm.SaleDateEditKeyPress(Sender: TObject; var Key: 
Char); begin 
    if Key = AJ then begin 
    PickDate(Sender); Key := #0; end; end; 
    { Щелчок на кнопке PartNo сетки приводит к открытию окна PickPartNo. } 
    procedure TEdOrderForm.PickPartNo(Sender: TObject); begin 
    if ItemsGrid.SelectedField = csItemsPartNo then { PartNo column only } begin 
    if csItemsPartNo.Value <> 0 then 
    SearchDlg.PartNo := csItemsPartNo.Value; { start with current PartNo } if 
SearchDlg.ShowModalParts = mrOk then begin 
    csltems.Edit; 
    csItemsPartNo.Value := SearchDlg.PartNo; end; end; end; 
    { Начало серии событий BeforePost и AfterPost. } 
    procedure TEdOrderForm.PostBtnClick(Sender: TObject); begin 
    csOrders.Post ; end; 
    { Отмена вставки или модификации строки таблицы Orders. } 
    procedure TEdOrderForm.CancelBtnClick(Sender: TObject); 
    begin 
    //  csOrdersAfterCancel(csOrders); 
    csOrders . Cancel/end; 
    { Печать формы. Отчет можно получить из главного окна. } 
    procedure TEdOrderForm.PrintBtnClick(Sender: TObject); begin 
    if (Application.MessageBox('Print image of this form window? 
','',MB_YESNOCANCEL)=IDYES) then Print; end; 
    function TEdOrderForm.DataSetApplyUpdates(DataSet: TDataSet; Apply: 
Boolean): Boolean; begin 
    Result := True; 
    with TDBDataSet(DataSet) do 
    begin 
    if (State in dsEditModes) or UpdatesPending then begin 
    if Apply then 

    586 Часть IV. Темы повышенной сложности 

    begin 
    Database.ApplyUpdates ( [DataSet as TDBDataSet]); { Always call 
CancelUpdates to remove any discard changes } CancelUpdates; end else begin 
    if (MessageDlg('Unsaved changes, exit anyway?', mtConfirmation, [mbYes, 
mbCancel], 0) = mrYes) then CancelUpdates else 
    Result := False; end; end; end; end; 
    procedure TEdOrderForm.FormCloseQuery(Sender: TObject; 
    var CanClose: Boolean); begin 
    CanClose := True; //DataSetApplyUpdates(csOrders, ModalResult = mrOK); end; 
    procedure TEdOrderForm.SoldByComboKeyPress(Sender: TObject; var Key: 
Char); begin 
    if not (Key in [#13, 127]) then 
    Key := #0; end; 
    procedure TEdOrderForm.csOrdersCalcFields(DataSet: TDataSet); begin 
    csOrdersTaxTotal.Value := csOrdersItemsTotal.Value * 
(csOrdersTaxRate.Value / 100); 
    csOrdersAmountDue.Value := csOrdersItemsTotal.Value + 
csOrdersTaxTotal.Value + 
    csOrdersFreight.Value - csOrdersAmountPaid.Value; end; 
    procedure TEdOrderForm.csItemsCalcFields(DataSet: TDataSet); begin 
    csItemsExtPrice.Value := csItemsQty.Value * 
    csItemsSellPrice.Value * (100 - csItemsDiscount.Value) / 100; end; 
    procedure TEdOrderForm.FormShow(Sender: TObject); begin 
    ActiveSource.DataSet:=csOrders; end; 
    procedure TEdOrderForm.dsOrdersDataChange(Sender: TObject; Field: TField); begin 
    PostBtn.Enabled := csOrders.State in dsEditModes; 
    CancelBtn.Enabled := PostBtn.Enabled; 
    CloseBtn.Enabled := csOrders.State = dsBrowse; end; 
    procedure TEdOrderForm.DateTimePickerlExit(Sender: TObject); begin 
    DateTimePiekerI.Visible:=False; 
    csOrders.Edit; 
    csOrdersSaleDate.AsDateTime:=Trunc(DateTimePicker1.Date); 
    SaleDateEdit.Visible:=True; end; 
    procedure TEdOrderForm.SaleDateEditDblClick(Sender: TObject); 

    Глава 28. Приложения для работы с Internet	587 

    begin 
    SaleDateEdit.Visible:=False; 
    DateTimePickerl.Date:=csOrdersSaleDate.AsDateTime; 
    DateTimePickerl. Visible:=True; end; 
    procedure TEdOrderForm.CloseBtnClick(Sender: TObject); begin 
    Close; end; 
    end. 

    Листинг 28.12. Исходный текст программного модуля формы поиска SRCHDLG. 
PAS приложения 

    unit SrchDlg; interface 
    uses 
    SysUtils, Windows, Messages, Classes, Graphics, Controls, 
    Forms, Dialogs, DBTables, DB, StdCtrls, ExtCtrls, Grids, DBGrids, Buttons, 
    DBClient; 
    type 
    TSearchDlg = class(TForm) DataSource: TDataSource; DBGridl: TDBGrid; 
OKBtn: TButton; CancelBtn: TButton; SearchEd: TEdit; OrderCombo: TComboBox; 
Label1: TLabel; Label2: TLabel; SearchButton: TSpeedButton; csCust: 
TClientDataSet; rsOrders: TRemoteServer; csParts: TClientDataSet; 
csPartsPartNo: TFloatField; csPartsVendorNo: TFloatField; csPartsDescription: 
TStringField; csPartsOnHand: TFloatField; csPartsOnOrder: TFloatField; 
csPartsCost: TCurrencyField; csPartsListPrice: TCurrencyField; csCustCustNo: 
TFloatField; csCustCompany: TStringField; csCustAddrl: TStringField; 
csCustAddr2: TStringField; csCustCity: TStringField; csCustState: 
TStringField; csCustZip: TStringField; csCustCountry: TStringField; 
csCustPhone: TStringField; csCustFAX: TStringField; csCustTaxRate: 
TFloatField; csCustContact: TStringField; csCustLastlnvoiceDate: 
TDateTimeField; procedure DBGridlDblClick(Sender: TObject); procedure 
SearchButtonClick(Sender: TObject); procedure OrderComboChange(Sender: TObject); 
    procedure SearchEdKeyPress(Sender: TObject; var Key: Char); procedure 
SearchEdChange(Sender: TObject); private 

    588 Часть IV. Темы повышенной сложности 

    SrchFld: TField; 
    function GetCustNo: Double; 
    procedure SetCustNo(NewCustNo: Double); 
    function GetPartNo: Double; 
    procedure SetPartNo(NewPartNo: Double); public 
    property PartNo: Double read GetPartNo write SetPartNo; 
    property CustNo: Double read GetCustNo write SetCustNo; 
    function ShowModalCust: Integer; 
    function ShowModalParts: Integer; end; 
    var 
    SearchDlg: TSearchDlg; 
    implementation {$R *.DFM} 
    function TSearchDlg.GetCustNo: Double; begin 
    Result := csCustCustNo .Value/end ; 
    procedure TSearchDlg.SetCustNo(NewCustNo: Double); begin 
    csCust.Locate('CustNo', NewCustNo, []); end; 
    function TSearchDlg.GetPartNo: Double; begin 
    Result := csPartsPartNo.Value; end; 
    procedure TSearchDlg.SetPartNo(NewPartNo: Double); begin 
    csParts.Locate('PartNo', NewPartNo, []); end; 
    function TSearchDlg.ShowModalCust: Integer; begin 
    OrderCombo.Items.Clear; 
    OrderCombo.Items.Add('Company'); 
    OrderCombo.Items.Add('CustNo'); 
    OrderCombo.Itemlndex := 0; 
    Datasource.Dataset := csCust; 
    OrderComboChange(nil); 
    Caption := 'Select a Customer'; 
    Result := ShowModal; end; 
    function TSearchDlg.ShowModalParts: Integer; begin 
    OrderCombo.Items.Clear; 
    OrderCombo.Items.Add('Description'); 
    OrderCombo.Iterns.Add('PartNo'); 
    OrderCombo.Itemlndex := 0; 
    Datasource.Dataset := csParts; 
    OrderComboChange(nil); 
    Caption := 'Select a Part'; 
    Result := ShowModal; end; 
    procedure TSearchDlg.DBGridlDblClick(Sender: TObject); begin 

    Глава 28. Приложения для работы с Internet 589 

    ModalResult := mrOK; end; 
    procedure TSearchDlg.SearchButtonClick(Sender: TObject); begin 
    if not Datasource.Dataset.Locate(OrderCombo.Text, SearchEd.Text, 
[loCaselnsensitive, loPartialKey]) then 
    MessageDlg('No matching record found.', mtlnformation, [mbOK] , 0) ; end; 
    procedure TSearchDlg.OrderComboChange(Sender: TObject); begin 
    SrchFld := Datasource.Dataset.FieldByName(OrderCombo.Text); 
    SearchEd.Text := ''; end; 
    procedure TSearchDlg.SearchEdKeyPress(Sender: TObject; var Key: Char); begin 
    if Assigned(SrchFld) and (Key > ' ') and not (SrchFld.IsValidChar(Key)) 
then begin 
    MessageBeep(0); Key := #0; end; end; 
    procedure TSearchDlg.SearchEdChange(Sender: TObject); begin 
    SearchButton.Enabled := SearchEd.Text <> ''; end; 
    end. 


    Проект номер пять: создание приложения 
    для покупок по сети 

    Карточка заказа (shopping cart) бесспорно является одним из самых 
популярных типов приложений, которые можно встретить в Internet. В настоящее 
время их можно найти в Web повсюду. Они используются для покупки любых товаров 
— от музыкальных компакт-дисков для автомобилей до книг, подобных той, что вы 
держите в руках. Научившись создавать приложения такого типа с использованием 
стандартных средств разработки Windows, таких как Delphi, вы убьете сразу двух 
зайцев: освоите вездесущую технологию разработки приложений Windows 
"перетащить и опустить" и ощутите гибкость и широту использования World Wide 
Web. Это мощное сочетание трудно переоценить. 
    На самом деле термин карточка заказа — это просто общепринятое название 
приложения для покупок по сети Internet. Приложение карточки заказа позволяет 
заказать товары в интерактивном режиме, как любое классическое приложение по 
оформлению заказа. С точки зрения создания приложений, карточки заказа не 
являются слишком сложными. Сложность заключается в том, что они должны 
работать в Internet. 
    Отсутствие в Internet постоянного канала подключения является главным 
фактором, определяющим сложность приложений типа карточки заказа. Приложения 
по обслуживанию заказов по своей природе требуют постоянной связи с сетью для 
непрерывного взаимодействия с базой данных. Поэтому архитектура Web, не 
предполагающая постоянного подключения компьютеров друг к другу, затрудняет 
создание таких приложений. Поскольку HTML-файл просматривается броузером 
пользователя, то непрерывное общение с Web-сервером становится ненужным. Без 
применения Java или ActiveX просматриваемая броузером информация остается 
инертной и неоперативной. Это создает значительные трудности для интерактивных 
приложений баз данных. 
    Приложения карточек заказа традиционно создавались путем бессистемного 
написания HTML-файлов, сценариев Perl, JavaScript и CGI, аплетов Java и тому 
подобного. Естественно, использование такого пестрого разнообразия средств для 
создания простых интерактивных приложений влечет за собой множество проблем. В 
качестве альтернативы Delphi выдвигает технологию ActiveForm. ActiveForm — это 
обычная форма Delphi, оформленная в виде элемента управления ActiveX. Она 
подобна любой другой форме 

    590 Часть IV. Темы повышенной сложности 

    Delphi, за исключением того, что ее можно внедрить на Web-страницу и 
просматривать в броузере, поддерживающем элементы управления ActiveX (в 
настоящее время элементы управления ActiveX поддерживает только Internet 
Explorer компании Microsoft). 
    При создании приложения карточки заказа будут использованы знания, 
полученные при работе над проектами 3 и 4 этой главы. В проекте 3 вы научились 
создавать приложения ActiveForm, а в проекте 4 — компактные клиентные 
приложения. Карточка заказа будет представлять собой компактное клиентное 
приложение ActiveForm. Оно будет устанавливаться как элемент управления OCX и 
не будет требовать BDE для доступа к объектам базы данных. 
    Первым шагом в создании приложения карточки заказа является построение его 
сервера данных. Поскольку это приложение будет представлять собой компактное 
клиентное приложение, то вам необходимо создать отдельный сервер приложений 
для обмена данными с сервером базы данных. Для построения сервера приложения 
карточки заказа выполните следующие действия. 
    1.   Удостоверьтесь, что ваш сервер InterBase работает. 
    2.    Запустите приведенный в листинге 28.13 сценарий SQL, чтобы создать 
главную базу данных приложения карточки заказа и объекты базы данных. В этом 
сценарии предполагается, что база данных будет создана в каталоге С: \DATA\IB, 
но вы можете указать другой каталог. 

    На заметку 
    Этот сценарий SQL необходимо запустить с помощью команды утилиты wiSQL 
File^Run an ISQL Script. Вы можете сами ввести исходный текст сценария в окне 
редактирования Delphi либо загрузить его с прилагавмого компакт-диска. 

    Листинг 28.13. Исходный текст сценария для создания главной базы данных 
приложения карточки заказа 

    CREATE DATABASE "C:\DATA\IB\SHOPCART.GDB" USER "SYSDBA" PASSWORD "masterkey"; 
    CREATE TABLE ITEM 
    (ItemNo int NOT NULL PRIMARY KEY, 
    Description char(30) NOT NULL, 
    Price decimal(5,2) NOT NULL); 
    CREATE TABLE CART (CustomerName char(30) NOT NULL, ItemNo int NOT NULL 
REFERENCES ITEM(ItemNo), CartDate date NOT NULL, Price decimal(5,2) NOT NULL); 
    CREATE TABLE DUAL (NoName char(1)); 
    CREATE VIEW TODAYCART AS SELECT * FROM CART WHERE CustomerName=USER AND 
CartDate="TODAY" WITH CHECK OPTION; 
    CREATE GENERATOR GEN_ITEMNO; 
    SET TERM !! ; 
    CREATE TRIGGER ITEM_INSERT FOR ITEM BEFORE INSERT 
    POSITION 0 AS BEGIN 
    NEW.ItemNo = GEN_ID(GEN_ITEMNO, 1) ; END! !  I 
    SET TERM ; !! 
    INSERT INTO ITEM (DESCRIPTION, PRICE) 
    VALUES ("DR. BUCK PHILGATE'S LINIMENT",29.95); 

    Глава 28. Приложения для работы с Internet 591 

    INSERT INTO ITEM (DESCRIPTION, PRICE) VALUES ("ALLEN PUCK'S FALL ALERT",99.95); 
    INSERT INTO ITEM (DESCRIPTION, PRICE) 
    VALUES ("MRS. TUCK'S ICR MOOSE-OFF FOAM",23.95); 
    INSERT INTO DUAL VALUES ('X'); 

    3.    Создайте псевдоним BDE IB_SHOPCART, который ссылается на новую базу 
данных. Это можно сделать в BDE Administrator или в Database Explorer. 
Поскольку в таблицах InterBase содержатся поля Decimal, удостоверьтесь, что 
для свойства Enable BCD этого псевдонима установлено значение True. 
    4.    Выберите команду File^New Application, чтобы создать новое 
приложение Delphi. 
    5.    Измените  значение  свойства  Caption  главной  формы  на  
ShopCartServer,  а  значение  свойства 

    WindowsState — на wsMinimized. 

    6.    Выберите команду File^New и дважды щелкните на элементе Remote Data 
Module в диалоговом окне New Items. 
    7.   В окне Remote Dataset Wizard введите имя класса ShopCartServer и 
щелкните на кнопке ОК. Кли-ентные приложения будут использовать это имя для 
ссылки на компонент семейства DataSet модуля удаленных данных. 
    8.    Поместите на форму два компонента Table и один компонент Query. 
Присвойте первому компоненту Table имя taCART, а второму — talTEM. Компонент 
Query назовите quUSERNAME. Компонент taCART будет обслуживать таблицу CART с 
помощью представления TODAYCART базы данных. TODAYCART — это модифицируемое 
представление, которое ограничивает число отображаемых строк, выводя только 
строки, введенные текущим пользователем сегодня. Это облегчит работу 
пользователя, так как он сможет возвращаться к карточке заказов по несколько 
раз в день, добавляя и удаляя наименования товаров. 
    9.    Поместите на форму по одному компоненту Database и Session. 
    10. Для свойства AutoSessionName компонента Session задайте значение True. 
    11. Для свойства AliasName компонента Database задайте значение 
определенного ранее псевдонима ; IB_SHOPCART. Установите его свойство 
DatabaseName в dbSHOPCART. 
    12. Для свойства HandleShared компонента Database задайте значение True. 
    13. Задайте для свойства DatabaseName компонентов taCART, talTEM и 
quUSERNAME значение dbSHOPCART. Они будут маршрутизировать доступ к базе 
данных через компонент этой формы 

    Database. 

    14. Задайте для свойства TableName компонента taCART ссылку на 
представление TODAYCART, созданное с помощью сценария, приведенного в листинге 
28.13. Установите для свойства TableName компонента talTEM ссылку на таблицу ITEM. 
    15. Для свойства SQL компонента quUSERNAME задайте значение 

    SELECT DISTINCT USER AS USERNAME FROM DUAL 

    Тогда сервер базы данных будет возвращать имя текущего пользователя. 
Поскольку представление TODAYCART определяет возвращаемые строки с помощью 
поля CustomerName (в котором хранится регистрационный номер пользователя), то 
каждая добавляемая к базе данных с помощью taCART строка должна содержать 
регистрационный номер текущего пользователя. Лучше всего получить эту 
информацию от сервера. Данный запрос делает именно это. 
    16. Щелкните правой кнопкой на каждом из компонентов DataSet (taCART, 
talTEM и quUSERNAME) и выберите из всплывающего меню команду Export...from 
data module. Тогда эти компоненты станут доступными клиентному приложению 
карточки заказа. 
    17. Сохраните проект на диске. Программный модуль Unit2 сохраните под 
именем SCSERV01. PAS, модуль Unitl — под именем SCSERVOO. PAS, а проект — под 
именем SCSERV. DPR. 
    18. Теперь запустите приложение SCSERV, чтобы зарегистрировать его 
СОМ-сервер. Сразу же после загрузки его можно закрыть. 

    592 Часть IV. Темы повышенной сложности 

    Создав сервер данных, можно приступить к созданию самого приложения 
карточки заказа. Для построения приложения SHPCRT выполните следующие действия. 
    1.   Выберите команду File^New Application, чтобы начать новое приложение Delphi. 
    2.    Выберите команду File^New^ActiveX^ActiveForm, чтобы создать новую 
форму ActiveX. Вы получите сообщение о том, что к текущему проекту нельзя 
добавить форму AcitveForm, поскольку этот проект не является библиотекой 
ActiveX. Щелкните на кнопке ОК, чтобы запустить новый проект библиотеки ActiveX. 
    3.   Введите для новой формы ActiveX имя SHOPCART, для проекта — имя 
SHPCRT.DPR, а для модуля реализации — имя SHPCRTOO.PAS (рис. 28.11). 
    4.    В правой части новой формы поместите три компонента ClientDataSet, 
два компонента DataSource и один компонент RemoteServer. 
    5.    Присвойте первому компоненту ClientDataSet имя csCART, второму — 
csiTEM и третьему — CSUSERNAME. Эти компоненты будут выполнять функции 
соответствующих компонентов модуля удаленных данных ShopCartServer. 
    6.    Задайте для свойства ServerName компонента RemoteServer значение 
SCSERV. ShopCartServer. 
    7.    Задайте для свойства RemoteServer всех трех компонентов 
ClientDataSet значение RemoteServerl. 

    Рис. 28.11. Мастер ActiveForm Wizard поможет вам создать форму типа ActiveX 

    8.    Задайте для csCART имя провайдера taCART, для csiTEM — talTEM и для 
csUSERNAME — quUSERNAME. Таким образом будет установлена связь между 
локальными наборами данных и их прототипами в 

    ShopCartServer. 

    9.    Щелкните дважды на компоненте CSCART, чтобы вызвать редактор полей, 
затем нажмите <Ctrl+A> и добавьте к этому компоненту все поля представления 
TODAYCART. Таким образом будут созданы компоненты TField ДЛЯ всех полей TODAYCART. 
    10. Щелкните дважды на компоненте CSITEM для вызова редактора полей, а 
затем добавьте к этому компоненту все поля таблицы ITEM. 
    11. Щелкните дважды на компоненте csUSERNAME для вызова редактора полей, а 
затем добавьте поле 

    USERNAME Компонента quUSERNAME. 

    Глава 28. Приложения для работы с Internet 593 

    12. Добавьте к CSCART новое поле выборки Description. Установите в этом 
поле поиск содержимого столбца Description компонента csITEM по столбцу 
ItemNo. В этом поле карточки заказа будут отображаться наименования товаров. 
    13. Щелкните дважды на событии Af terOpen компонента CSCART и введите 
следующий оператор. 

    RefreshTotal; 

    Таким образом мы добавили вызов метода Ref reshTotal. Он будет 
использоваться для пересчета общей стоимости товаров в карточке заказа при 
добавлении или удалении каких-то наименований. 
    14. Задайте для свойства Readonly компонента CSITEM значение True. Тогда 
содержимое этого компонента нельзя будет изменить. Поскольку приложение 
карточки заказа не должно модифицировать перечень возможных товаров, 
необходимо запретить редактирование таблицы ITEM. 
    15. Задайте для свойства Active всех трех компонентов ClientDataSet 
значение True. 
    16. Задайте для свойства DataSet компонента DataSourcel ссылку на csCART, 
а для свойства DataSet второго компонента — ссылку на CSITEM. 
    17. Поместите на форму два компонента DBGrid. Расположите их один над 
другим таким образом, чтобы от их первого столбца до левой границы формы 
оставалось примерно 40 пикселей. Задайте для них ширину примерно 350 пикселей. 
Верхний компонент DBGrid будет отображать текущее содержимое карточки заказа. 
Нижний компонент будет отображать содержимое таблицы ITEM — перечень 
наименований, которые можно включить в карточку заказа. 
    18. Верхний компонент DBGrid назовите dbgCART, а нижний — dbgiTEM. 
    19. Щелкните дважды на свойстве Columns компонента dbgCART и добавьте к 
списку столбцов все поля этой таблицы. Затем удалите все столбцы, за 
исключением Description и Price, поскольку вся остальная информация, например 
CustomerName, в карточке заказа не понадобится — там должно содержаться только 
описание каждого товара и его стоимость. 
    20. Для облегчения восприятия поместите столбец Description перед столбцом 
Price, а затем закройте редактор столбцов. 
    21. Задайте для свойства Readonly компонента dbgCART значение True, чтобы 
предотвратить возможность редактирования таблиц. 
    22. Измените свойство Options обоих компонентов DBGrid, включив свойство 
RowSelect. Тогда при попытке выбрать отдельные поля будут выделяться целые 
строки, что нам и требуется. 
    23. Прямо под верхним компонентом DBGrid поместите компонент DBNavigator и 
присвойте ему имя dbnCART. Измените его свойство VisibleButtons таким образом, 
чтобы включить только кнопки nbFirst, nbPrior, nbNext, nbLast и nbDelete, и в 
соответствии с этим измените размер компонента. Тогда с помощью навигатора 
нельзя будет вставлять и редактировать строки, однако с помощью кнопок 
навигатора пользователь сможет добавлять и удалять наименования товаров в 
карточке заказа. 
    24. Щелкните дважды на компоненте dbnCART и введите в исходном тексте 
обработчика события OnClick следующий оператор. 

    If   (Button=nbDelete)   then RefreshTotal; 

    Он предназначен для того, чтобы при удалении наименования товара из 
карточки заказа общая стоимость заказа пересчитывалась. 
    25. Задайте для свойства showHint компонента dbnCART значение True. Тогда 
для кнопок навигатора на экран будут выводиться всплывающие подсказки. 
    26. Задайте для свойства DataSource верхнего компонента DBGrid и 
компонента DBNavigator значение DataSourcel, чтобы установить связь этих 
компонентов с набором данных csCART. 
    27. Задайте для свойства DataSource нижнего компонента DBGrid значение 
DataSource2, чтобы установить связь этого компонента с набором данных CSITEM. 
    28. В верхнем левом углу каждого компонента DBGrid разместите компонент 
Label. В качестве заголовка верхнего компонента Label задайте Shopping Cart 
Contents, а нижнего компонента — Items. 
    29. Поместите компонент Label справа от компонента DBNavigator. Задайте 
для него заголовок Total:. 

    594	Часть IV. Темы повышенной сложности 

    30. Справа от метки Total: поместите еще один компонент Label и присвойте 
ему имя laTotal. Выровняйте его по вертикали по столбцу Price компонента 
dbgCART. С помощью этого компонента будет отображаться текущая общая стоимость 
при добавлении и удалении наименований товаров карточки заказа. 
    31. Выделите оба компонента Label и установите их свойство Height равным 
12 пикселей. 
    32. Измените размер для свойства Font обоих компонентов на 12 пикселей и 
включите атрибут полужирного шрифта. Тогда общая стоимость будет выделяться 
среди остальных элементов формы. 
    33. Задайте для свойства Alignment компонента laTotal значение 
taRightJustify. Тогда величина общей стоимости и расположенный выше столбец 
Price, будут выравниваться по правому краю, как принято для столбцов чисел. 
    34. Поместите под компонентом dbglTEM две кнопки. Первую из них назовите 
btAdd, а вторую — btCheckOut. Кнопка btAdd будет использоваться для добавления 
наименований товаров в карточку заказа. Кнопка btCheckOut будет использоваться 
для передачи списка покупаемых товаров на сервер для проверки и подсчета общей 
стоимости заказа. 
    35. Задайте для свойства Caption компонента btAdd значение Add Item to 
Shopping Cart, а в качестве заголовка кнопки btCheckOut введите Check Out. 

    На рис. 28.12 показано, как теперь должна выглядеть ваша форма. 

    Рис. 28.12. Форма приложения карточки заказа 

Чтобы завершить создание приложения карточки заказа выполните следующие действия. 
    1.    В окне редактирования исходного текста Delphi добавьте следующую 
строку в объявление класса TShopCart. procedure  RefreshTotal; 
    2.   Добавьте процедуру метода RefreshTotal в исходный текст класса 
TShopCart, поместив в нее следующие операторы. 
    prodedure  TSHOPCART.RefreshTotal; var TempBM:   TBookmark; 

    Глава 28. Приложения для работы с Internet 595 

    Total:   Currency; begin 
    with  csCART  do  begin TempBM:=GetBookmark; First; Total:=0; 
    While  not   eof  do  begin Total:=Total+csCARTPrice.Value; Next; end; 
    GotoBookmark(TempBM); FreeBookmark(TempBM) ; end; 
    laTotal.Caption:=FloatToStrF(Total,ffCurrency,6,2); end; 

    Эта процедура будет использоваться для вычисления общей стоимости покупки 
при добавлении или удалении наименований товаров в карточке заказа. 
    3.    Вернитесь в форму, поместите компонент Panel в верхнюю часть сетки 
dbg ITEM и присвойте ему имя paCheckOut. Задайте его размер и положение таким 
образом, чтобы он накрывал метку над сеткой и содержимое самого компонента 
DBGrid. Этот компонент будет использоваться для вывода сообщений о завершении 
сеанса работы приложения карточки заказа. 
    4.    В качестве заголовка этой панели задайте Your order has been 
received. Thank you! и задайте для ее свойства Visible значение False. 
    5.    Щелкните правой кнопкой на компоненте paCheckOut и выберите из 
всплывающего меню команду Send To Back. Тогда в процессе работы приложения эта 
панель будет располагаться позади компонентов DBGrid и Label. Когда при 
завершении работы приложения возникнет необходимость вывести панель на экран, 
ее свойство Visible примет значение True, а свойство Visible компонентов 
DBGrid и Label примет значение False. В результате сетка dbg ITEM и 
соответствующая ей метка будут скрыты. 
    6.    Щелкните дважды на кнопке btAdd и добавьте в обработчик события 
OnClick следующие операторы. 

    with csCART do begin 
    AppendRecord([csUSERNAMEUserName.Value, csITEMItemNo.Value, Date, csITEMPrice.Value 
    ]); 
    end; RefreshTotal; 

    Здесь вызывается метод AppendRecord компонента csCART для добавления 
нового товара в карточку заказа. Обратите внимание, что для хранения 
регистрационного номера текущего пользователя используется поле UserName из 
набора данных csUSERNAME. После добавления нового наименования товара 
вызывается метод RefreshTotal для пересчета текущей общей стоимости покупки. 
    7.    Щелкните дважды на кнопке btCheckOut и введите в окне редактирования 
исходного текста Delphi следующие операторы. 


    btAdd.Visible:=False ; dbglTEM.Visible:=False; paCheckOut.Visible:=True; 
dbgCART.Enabled:=False; dbnCART.Enabled:=False,• btCheckOut.Enabled:=False; 
csCART.ApplyUpdates(-1) ; 

    Здесь компонент paCheckOut становится видимым и выводится сообщение о 
завершении работы приложения, а затем список добавленных и удаленных 
наименований товаров карточки заказа передается на сервер. Поскольку метод 
ApplyUpdates вызывается с параметром -1, то при выявлении ошибок в процессе 
обновления данных на сервере этот процесс прекращается. 
    8.   Итак, приложение карточки заказа создано. Сохраните его на диске. На 
рис. 28.13 показано готовое приложение SHOPCART. 

    596	Часть IV. Темы повышенной сложности 

    Теперь можно приступить к испытанию приложения. Как отмечалось ранее в 
этой главе, для настройки приложений ActiveForm используется команда меню 
Project^Web Deployment Options. В соответствующем диалоговом окне указываются 
названия каталогов, в которых будут располагаться файлы OCX и нтм, а также 
относительный путь к Web-броузеру, который будет использоваться для получения 
доступа к ним. Чтобы настроить приложение карточки заказа, выполните следующие действия. 

    Рис. 28.13. Готовое приложение SHOPCART 

    1.    Нажмите комбинацию клавиш <Ctrl+F9>, чтобы скомпилировать приложение. 
    2.    Выберите команду Project^Web Deployment Options. 
    3.   Значения параметров, указываемые в диалоговом окне Web Deployment 
Options, зависят от вашего Web-сервера, но в качестве значений Target dir и 
HTML dir обычно выбирается один и тот же каталог. Нет необходимости отделять 
элемент управления OCX от соответствующего ему нтмь-файла. 
    Например, чтобы установить форму на Web-сервере Microsoft US, можно 
использовать следующие параметры. 

    Target   dir:   C:\inetpub\wwwroot 
    Target  URL:   / 
    HTML  dir:      C:\inetpub\wwwroot 

    При этом элемент управления и соответствующий HTML-файл будут размещены в 
корневом каталоге документов сервера. Значение / для Target URL задает 
относительный путь в IIS к этому каталогу. 
    4.    Щелкните на вкладке Additional Files и добавьте к списку требуемых 
для приложения файлов DBCLIENT.DLL. Для приложения карточки заказа не 
требуется вся система BDE, а лишь файл DBCLIENT . DLL для связи с приложением 
сервера. Не изменяйте используемые по умолчанию спецификации Target URL и 
Target directory, а щелкните на кнопке ОК для выхода из диалогового окна. 
    5.   Задав нужные параметры в диалоговом окне Options, выберите команду 
меню Project^Web Deploy для настройки приложения. При этом файл карточки 
заказа OCX и соответствующий HTML-файл будут скопированы в каталоги, указанные 
вами в диалоговом окне Web Deployment Options. 

    Глава 28. Приложения для работы с Internet 597 

    Как отмечалось ранее, для тестирования приложений, использующих элементы 
управления ActiveX не обязательно иметь собственный Web-сервер — достаточно 
иметь лишь Internet Explorer 3.0. Чтобы протестировать приложение карточки 
заказа с помощью IE, измените параметры каталогов в диалоговом окне Web 
Deployment Options на локальный каталог. Например, можно задать следующие каталоги: 

    Target dir: c:\mywebpages 
    Target URL: file:///с.:/mywebpages 
    HTML dir:  c:\mywebpages 

    Теперь элемент управления осх и соответствующий ему HTML-файл будут 
находиться в каталоге с: \mywebpages, а сгенерированный нтмь-файл будет 
ссылаться на элемент управления как на локальный файл. Затем можно просто 
открыть HTML-файл, воспользовавшись командой File^Open или введя в командной 
строке IE имя файла SHPCRT . нтм. 
    Чтобы протестировать установленное приложение, выполните следующие действия. 
    1.    Если вы тестируете приложение с использованием Web-сервера, 
удостоверьтесь, что он работает. 
    2.    Запустите сервер InterBase, если он еще не запущен.    
    3.   Откройте сгенерированный Delphi файл SHPCRT.HTM и измените значения 
width и height на 545 и 400 соответственно. Тогда содержимое карточки заказа 
Active Form не будет обрезаться при выводе на экран. Этот файл должен 
находиться в каталоге, указанном ранее в качестве HTML dir. 
    4.   Откройте в Internet Explorer файл SHPCRT.HTM. Если вы тестируете 
приложение с использованием Web-сервера, необходимо указать адрес сервера в 
Internet, например: 

    http://localhost/shpcrt.htm 

    Приложение ActiveForm можно запустить прямо из Delphi, как любое другое 
приложение следующим образом. 
    1. Скомпилируйте приложение ActiveForm, для получения элемента управления 
осх, а затем используйте команду Project^Web Deploy, чтобы установить его на 
диске, доступном для вашего компьютера. 
    2. Выберите команду Run^Parameters и задайте следующие параметры. 
    H^Host Application:   C:\Program FilesXPlus!\Microsoft  
Internet\Iexplore.exe	: t|Run parameters:   http://localhost/shpcrt .htm       
                                                                               
             f |t!Подставьте в эти параметры адрес вашего Web-сервера. 
    3. Нажмите клавишу <F9> или щелкните на кнопке Run, чтобы запустить 
приложение ActiveForm. При этом будет запущен Internet Explorer, и в нем будет 
открыт файл SHPCRT . нтм. 
    5.   При некоторых параметрах защиты, установленных для Internet Explorer, 
вам, возможно, придется подтвердить разрешение на загрузку элемента управления 
SHPCRT. OCX. Щелкните на кнопке Yes для подтверждения разрешения. 
    6.    После загрузки страницы вам будет предложено ввести имя пользователя 
и пароль. Введите имя пользователя InterBase и пароль, которые вы использовали 
при создании базы данных SHOPCART (по умолчанию SYSDBA И masterkey). 
    7.    Щелкните на кнопке btAdd, чтобы добавить наименование товаров в 
карточку заказа, а чтобы удалить их — щелкните на кнопке Delete навигатора. На 
рис. 28.14 показано, как должна выглядеть заполненная форма. 
    8.    Чтобы отправить заказ, щелкните на кнопке btCheckOut. На рис. 28.15 
показано, как будет выглядеть приложение в момент получения уведомления о 
приеме заказа. 

    598 Часть IV. Темы повышенной сложности 

    Рис. 28.14. Приложение карточки заказа в процессе работы 

    Рис. 28.15. Приложение карточки заказа после приема заказа 

    Глава 28. Приложения для работы с Internet	599 

    При повторной компиляции и переустановке приложений, использующих 
компоненты ActiveX, не забывайте удалять старые копии элементов управления осх 
из каталога ОССАСНЕ. Как уже отмечалось ранее в этой главе, невыполнение этого 
требования может привести к тому, что Internet Explorer будет использовать 
устаревшие копии элементов управления ActiveX, игнорируя внесенные в них изменения. 
    В листингах 28.14-28.19 содержится полный исходный текст приложения 
карточки заказа. Листинг 28.14. Исходный текст проекта SCSERV. DPR серверной 
части приложения карточки заказа 

    program SCSERV; 
    uses Forms, 
    SCSERVOO in 'SCSERVOO.PAS' {Forml}, SCSERVJTLB in 'SCSERVJTLB.pas', 
SCSERV01 in 'SCSERV01.PAS' {ShopCartServer: TDataModule} {ShopCartServer: CoClass}; 
    {$R *.RES} {$R *.TLB} 
    begin 
    Application.Initialize; 
    Application.CreateForm(TForml, Forml); 
    Application.Run; end. 

    Листинг 28.15. Текстовое представление главной формы SCSERVOO . DFM 
приложения SCSERV 

    object Forml: TForml 
    Left = 200 
    Top = 108 
    Width = 544 
    Height = 375 
    Caption = 'ShopCartServer' 
    Font.Charset = DEFAULT_CHARSET 
    Font.Color = clWindowText 
    Font.Height = -11 
    Font.Name = 'MS Sans Serif 
    Font.Style = [) 
    WindowState = wsMinimized 
    PixelsPerlnch = 96 
    TextHeight = 13 end 

    Листинг 28.16. Текстовое представление формы SCSERVOI . DFM для модуля 
удаленных данных ShopCartServ серверного приложения 

    object ShopCartServer: TShopCartServer Left = 200 Top = 108 Height = 234 
Width = 338 object taCART: TTable 
    DatabaseName = 'dbSHOPCART' 
    SessionName = 'Sessionl_2' 
    TableName = 'TODAYCART' 
    Left = 24 
    Top =16 end 

    600	Часть IV. Темы повышенной сложности 

    '''•object talTEM: TTable 
    DatabaseName = 'dbSHOPCART' 
    SessionName = 'Sessionl_2' 
    TableName = 'ITEM1 
    Left = 72 
    Top =16 end object quUSERNAME: TQuery 
    DatabaseName = 'dbSHOPCART' 
    SessionName = 'Sessionl_2' 
    SQL.Strings = ( 
    'SELECT DISTINCT USER AS USERNAME FROM DUAL') 
    Left = 24 
    Top =72 end object Databasel: TDatabase 
    AliasName = 'IB_SHOPCART' 
    DatabaseName = 'dbSHOPCART' 
    HandleShared = True 
    Params.String   = ( 'USER NAME=SYSDBA') 
    SessionName = 'Sessionl_2' 
    Left = 120 
    Top = 16 end object Sessionl: TSession 
    Active = True 
    AutoSessionName = True 
    Left = 152 
    Top = 64 end end 

    Листинг 28.17. Исходный текст проекта SHPCRT . DPR клиентной части 
приложения карточки заказа 

    library SHPCRT; 
    uses 
    ComServ, 
    SHPCRTJTLB in 'SHPCRTJTLB.pas', 
    SHPCRTOO in 'SHPCRTOO.pas' {SHOPCART: TActiveForm} {SHOPCART: CoClass}; 
    exports 
    DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer; 
    {$R *.TLB} {$R *.RES} {$E ocx} 
    begin end. 

    Листинг 28.18. Исходный текст программного модуля SHPCRTOO . PAS для 
клиентной части приложения карточки заказа 

    unit SHPCRTOO; 
    interface 
    uses 

    Глава 28. Приложения для работы с Internet 601 

    Windows,	Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
    ActiveX,	AxCtrls, SHPCRT_TLB, Db, Grids, DBGrids, DBClient, StdCtrls, 
    DBCtrls,	ExtCtrls; 
    type 
    TSHOPCART = class(TActiveForm, ISHOPCART) 
    csITEM: TClientDataSet; 
    RemoteServerl: TRemoteServer; 
    dbgCART: TDBGrid; 
    dbglTEM: TDBGrid; 
    DataSourcel: TDataSource; 
    DataSource2: TDataSource; 
    btAdd: TButton; 
    Labell: TLabel; 
    Label2: TLabel; 
    csCART: TClientDataSet; 
    csCARTDESCRIPTION: TStringField; 
    LabelS: TLabel; 
    csITEMITEMNO: TIntegerField; 
    csITEMDESCRIPTION: TStringField; 
    csITEMPRICE: TBCDField; 
    btCheckOut:  TButton; 
    csCURTCUSTOMERNAME: TStringField; 
    csCARTCARTDATE: TDateTimeField; 
    csCARTPRICE: TBCDField; 
    csUSERNAME: TClientDataSet; 
    csUSERNAMEUSERNAME: TStringField; 
    dbnCART: TDBNavigator; 
    laTotal: TLabel; 
    paCHECKOUT: TPanel; 
    procedure btAddClick(Sender: TObject); 
    procedure btCheckOutClick(Sender: TObject); 
    procedure RefreshTotal; 
    procedure dbnCARTClick(Sender: TObject; Button: TNavigateBtn); 
    procedure csCARTAfterOpen(DataSet: TDataSet); private 
    { Private declarations } 
    FEvents: ISHOPCARTEvents; 
    procedure ActivateEvent(Sender: TObject); 
    procedure ClickEvent(Sender: TObject); 
    procedure CreateEvent(Sender: TObject); 
    procedure DblClickEvent(Sender: TObject); 
    procedure DeactivateEvent(Sender: TObject); 
    procedure DestroyEvent(Sender: TObject); 
    procedure KeyPressEvent(Sender: TObject; var Key: Char); 
    procedure PaintEvent (Sender: TObject); protected 
    { Protected declarations } 
    procedure EventSinkChanged(const EventSink: lUnknown); override; 
    procedure Initialize; override; 
    function Get_Active: WordBool; safecall; 
    function Get_AutoScroll: WordBool; safecall; 
    function Get_AxBorderStyle: TxActiveFormBorderStyle; safecall; 
    function Get_Caption: WideString; safecall; 
    function Get_Color: TColor; safecall; 
    function Get_Cursor: Smallint; safecall; 
    function Get_DropTarget: WordBool; safecall; 
    function Get_Enabled: WordBool; safecall; 
    function Get_Font: Font; safecall; 
    function Get_HelpFile: WideString; safecall; 
    function Get_KeyPreview: WordBool; safecall; 
    function Get_PixelsPerInch: Integer; safecall; 
    function Get_PrintScale: TxPrintScale; safecall; 
    function Get_Scaled: WordBool; safecall; 
    function Get Visible: WordBool; safecall; 

    602 Часть IV. Темы повышенной сложности 

    function Get_WindowState: TxWindowState; safecall; 
    procedure Set_AutoScroll(Value: WordBool); safecall; 
    procedure Set_AxBorderStyle(Value: TxActiveFormBorderStyle); safecall; 
    procedure Set_Caption(const Value: WideString); safecall; 
    procedure Set_Color(Value: TColor); safecall; 
    procedure Set_Cursor(Value: Smallint); safecall; 
    procedure Set_DropTarget(Value: WordBool); safecall; 
    procedure Set_Enabled(Value: WordBool}; safecall; 
    procedure Set_Font(const Value: Font); safecall; 
    procedure Set__HelpFile(const Value: WideString); safecall; 
    procedure Set__KeyPreview(Value: WordBool); safecall; 
    procedure Set_PixelsPerInch(Value: Integer); safecall; 
    procedure Set_PrintScale(Value: TxPrintScale); safecall; 
    procedure Set_Scaled(Value: WordBool); safecall; 
    procedure Set_Visible(Value: WordBool); safecall; 
    procedure Set_WindowState(Value: TxWindowState); safecall; public 
    { Public declarations } end; 
    implementation uses ComServ; {$R *.DFM} { TSHOPCART } 
    procedure TSHOPCART.EventSinkChanged(const EventSink: lUnknown); begin 
    FEvents := EventSink as ISHOPCARTEvents; end; 
    procedure TSHOPCART.Initialize; begin 
    OnActivate := ActivateEvent; 
    OnClick := ClickEvent; 
    OnCreate := CreateEvent; 
    OnDblClick := DblClickEvent; 
    OnDeactivate := DeactivateEvent; 
    OnDestroy := DestroyEvent; 
    OnKeyPress := KeyPressEvent; 
    OnPaint := PaintEvent; end; 
    function TSHOPCART.Get_Active: WordBool; begin 
    Result := Active; end; 
    function TSHOPCART.Get_AutoScroll: WordBool; begin 
    Result := AutoScroll; 
    end; 
    function TSHOPCART.Get_AxBorderStyle: TxActiveFormBorderStyle; begin 
    Result := Ord(AxBorderStyle); end; 
    function TSHOPCART.Get_Caption: WideString; begin 
    Result := WideString(Caption); end; 

    Глава 28. Приложения для работы с Internet 603 

    function TSHOPCART.Get_Color: TColor; begin 
    Result := Color; end; 
    function TSHOPCART.Get_Cursor: Smallint; begin 
    Result := Smallint(Cursor); end; 
    function TSHOPCART.Get_DropTarget: WordBool; begin 
    Result := DropTarget; end; 
    function TSHOPCART.Get_Enabled: WordBool; begin 
    Result := Enabled; end; 
    function TSHOPCART.Get_Font: Font; begin 
    GetOleFont(Font, Result); end; 
    function TSHOPCART.Get__HelpFile: WideString; begin 
    Result := WideString(HelpFile); end; 
    function TSHOPCART.Get_KeyPreview: WordBool; begin 
    Result := KeyPreview; end; 
    function TSHOPCART.Get_PixelsPerInch: Integer; begin 
    Result := PixelsPerlnch; end; 
    function TSHOPCART.Get_PrintScale: TxPrintScale; begin 
    Result := Ord(PrintScale) ; end; 
    function TSHOPCART.Get_Scaled: WordBool; begin 
    Result := Scaled; end; 
    function TSHOPCART.Get_Visible: WordBool; begin 
    Result := Visible; end; 
    function TSHOPCART.Get_WindowState: TxWindowState; begin 
    Result := Ord(WindowState); end; 
    procedure TSHOPCART.Set_AutoScroll(Value: WordBool); begin 
    AutoScroll := Value; end; 

    604 Часть IV. Темы повышенной сложности 

    procedure TSHOPCART.Set_AxBorderStyle(Value: TxActiveFormBorderStyle); begin 
    AxBorderStyle := TActiveFormBorderStyle(Value); end; 
    procedure TSHOPCART.Set_Caption(const Value: WideString); begin 
    Caption := TCaption(Value); end; 
    procedure TSHOPCART.Set_Color(Value: TColor); begin 
    Color := Value; end; 
    procedure TSHOPCART.Set_Cursor(Value: Smallint); begin 
    Cursor := TCursor(Value); end; 
    procedure TSHOPCART.Set_DropTarget(Value: WordBool); begin 
    DropTarget := Value; end; 
    procedure TSHOPCART.Set_Enabled(Value: WordBool); begin 
    Enabled := Value; end; 
    procedure TSHOPCART.Set_Font(const Value: Font); begin 
    SetOleFont(Font, Value); end; 
    procedure TSHOPCART.Set_HelpFile(const Value: WideString); begin 
    HelpFile := String(Value); end; 
    procedure TSHOPCART.Set_KeyPreview(Value: WordBool); begin 
    KeyPreview := Value; end; 
    procedure TSHOPCART.Set_PixelsPerInch(Value: Integer); begin 
    PixelsPerlnch := Value; end; 
    procedure TSHOPCART.Set_PrintScale(Value: TxPrintScale); begin 
    PrintScale := TPrintScale(Value); end; 
    procedure TSHOPCART.Set_Scaled(Value: WordBool); begin 
    Scaled := Value; end; 
    procedure TSHOPCART.Set__Visible(Value: WordBool); begin 
    Visible := Value; end; 

    Глава 28. Приложения для работы с Internet	605 

    procedure TSHOPCART.Set_WindowState(Value: TxWindowState) ; begin 
    WindowState := TWindowState(Value); end; 
    procedure TSHOPCART.ActivateEvent(Sender: TObject); begin 
    if FEvents <> nil then FEvents.OnActivate; end; 
    procedure TSHOPCART.ClickEvent(Sender: TObject); begin 
    if FEvents <> nil then FEvents.OnClick; end; 
    procedure TSHOPCART.CreateEvent(Sender: TObject); begin 
    if FEvents <> nil then FEvents.OnCreate; end; 
    procedure TSHOPCART.DblClickEvent(Sender: TObject); begin 
    if FEvents <> nil then FEvents.OnDblClick; end; 
    procedure TSHOPCART.DeactivateEvent(Sender: TObject); begin 
    if FEvents <> nil then FEvents.OnDeactivate; end; 
    procedure TSHOPCART.DestroyEvent(Sender: TObject); begin 
    if FEvents <> nil then FEvents.OnDestroy; end; 
    procedure TSHOPCART.KeyPressEvent(Sender: TObject; var Key: Char); var 
    TempKey: Smallint; begin 
    TempKey := Smallint(Key); 
    if FEvents <> nil then FEvents.OnKeyPress(TempKey); 
    Key := Char(TempKey); end; 
    procedure TSHOPCART.PaintEvent(Sender: TObject); begin 
    if FEvents <> nil then FEvents.OnPaint; end; 
    procedure TSHOPCART.btAddClick(Sender: TObject); begin 
    with csCART do begin 
    AppendRecord([csUSERNAMEUserName.Value, 
    csITEMItemNo.Value, 
    Date, 
    csITEMPrice.Value 
    ]); 
    end; end; 
    procedure TSHOPCART.btCheckOutClick(Sender: TObject); begin 
    btAdd.Visible:=False ; 
    dbglTEM.Visible:=False; 
    paCHECKOUT.Vis ible:=True ; 

    606	Часть IV. Темы повышенной сложности 

    dbgCART.Enabled:=False; dbnCART.Enabled:=False; btCheckOut.Enabled:=False; 
csCART.ApplyUpdates(-1) ; end; 
    procedure TSHOPCART.RefreshTotal; var 
    TempBM : TBookmark; Total : Currency; begin 
    with csCART do begin TempBM:=GetBookmark ; First; Total:=0; While not eof 
do begin 
    Total:=Total+csCARTPrice.Value; Next; end; 
    GotoBookmark(TempBM); FreeBookmark(TempBM); end; 
    laTotal.Caption:=FloatToStrF(Total, ffCurrency, 6,2); end; 
    procedure TSHOPCART.dbnCARTClick(Sender: TObject; Button: TNavigateBtn); begin 
    if (Button=nbDelete) then RefreshTotal; end; 
    procedure TSHOPCART.csCARTAfterOpen(DataSet: TDataSet); begin 
    RefreshTotal; end; 
    initialization 
    TActiveFormFactory.Create( ComServer, TActiveFormControl, TSHOPCART, 
Class_SHOPCART, I, 
    1 I 
    OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL); end. 

    Листинг 28.19. Текстовое представление формы SHPCRTOO . DFM клиенткой 
части приложения карточки заказа 

    object SHOPCART: TSHOPCART Left = 200 Top = 108 Width = 544 Height = 391 
Caption = 'SHOPCART' Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText 
Font.Height = -11 Font.Name = 'MS Sans Serif Font.Style = [] PixelsPerlnch = 
96 TextHeight = 13 object Labell: TLabel 
    Left = 40 
    Top = 168 

    Глава 28. Приложения для работы с Internet	607 

    Width = 25 Height = 13 Caption = 'Items' end object Label2: TLabel 
    Left = 40 
    Top = 0 
    Width = 112 
    Height = 13 
    Caption = 'Shopping Cart Contents' end object Label3: TLabel 
    Left = 168 
    Top =  144 
    Width =46 
    Height = 20 
    Caption = 'Total: ' 
    Font.Charset = DEFAULT_CHARSET 
    Font.Color = clWindowText 
    Font.Height = -16 
    Font.Name = 'MS Sans Serif 
    Font.Style = [fsBold] 
    ParentFont = False end object laTotal: TLabel 
    Left = 216 
    Top =  144 
    Width =89 
    Height = 20 
    Alignment = taRightJustify 
    AutoSize = False 
    Caption = 'laTotal' 
    Font.Charset = DEFAULT_CHARSET 
    Font.Color = clWindowText 
    Font.Height = -16 
    Font.Name = 'MS Sans Serif 
    Font.Style = [fsBold] 
    ParentFont = False end object paCHECKOUT: TPanel 
    Left = 40 
    Top =168 
    Width = 361 
    Height = 161 
    Caption = 'Your order has been received.  Thank you!' 
    TabOrder = 5 
    Visible = False end object dbgCART: TDBGrid 
    Left = 40 
    Top =16 
    Width = 353 
    Height = 115 
    DataSource = DataSourcel 
    Options = [dgTitles, dglndicator, dgColumnResize, dgColLines, dgRowLines, 
dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit] 
    Readonly = True 
    TabOrder = 0 
    TitleFont.Charset = DEFAULT__CHARSET 
    TitleFont.Color = clWindowText 
    TitleFont.Height = -11 
    TitleFont.Name = 'MS Sans Serif 
    TitleFont.Style = [] 
    Columns = < item 

    608	Часть IV. Темы повышенной сложности 

    FieldName = 'DESCRIPTION' Readonly = False Width = 187 end item 
    FieldName = 'PRICE' Readonly = False end> end object dbglTEM: TDBGrid 
    Left = 40 
    Top = 181 
    Width = 353 
    Height = 145 
    DataSource = DataSource2 
    Options = [dgTitles, dglndicator, dgColumnResize, dgColLines, dgRowLines, 
dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit] 
    Readonly = True 
    TabOrder = 1 
    TitleFont.Charset = DEFAULT_CHARSET 
    TitleFont.Color = clWindowText 
    TitleFont.Height = -11 
    TitleFont.Name = 'MS Sans Serif 
    TitleFont.Style = [] end object btAdd: TButton 
    Left = 136 
    Top = 331 
    Width = 145 
    Height = 25 
    Caption = 'Add Item to Shopping Cart' 
    TabOrder = 2 
    OnClick = btAddClick end object btCheckOut: TButton 
    Left = 318 
    Top = 331 
    Width = 75 
    Height = 25 
    Caption = 'Check Out' 
    TabOrder = 3 
    OnClick = btCheckOutClick end object dbnCART: TDBNavigator 
    Left = 40 
    Top = 131 
    Width = 100 
    Height = 22 
    DataSource = DataSourcel 
    VisibleButtons = [nbFirst, nbPrior, nbNext, nbLast, nbDelete] 
    ParentShowHint = False 
    ShowHint = True 
    TabOrder = 4 
    OnClick = dbnCARTClick end object csITEM: TClientDataSet 
    Active = True 
    ProviderName = 'talTEM' 
    Readonly = True 
    RemoteServer = RemoteServerl 
    Left = 504 
    Top =48 
    object csITEMITEMNO: TIntegerField FieldName = 'ITEMNO' Required = True 

    Глава 28. Приложения для работы с Internet	609 

    end 
    object csITEMDESCRIPTION: TStringField 
    FieldName = 'DESCRIPTION' 
    Required = True 
    Size = 30 end object csITEMPRICE: TStringField 
    FieldName = 'PRICE' 
    Required = True 
    Size = 2 end end 
    object RemoteServerl: TRemoteServer Connected = True 
    ServerGUID = ' {F7486762-4E2C-11D1-81BD-444553540000} Left = 464 Top = 8 end 
    object DataSourcel: TDataSource DataSet = csCART Left = 504 Top = 216 end 
    object DataSource2: TDataSource DataSet = csITEM Left = 504 Top = 256 end 
    .object csCART: TClientDataSet Active = True ProviderName = 'taCART' 
RemoteServer = RemoteServerl AfterOpen = csCARTAfterOpen Left = 504 Top = 8 
object csCARTCUSTOMERNAME: TStringField 
    FieldName = 'CUSTOMERNAME' 
    Size = 30 end object csCARTITEMNO: TIntegerField 
    FieldName = 'ITEMNO' end object csCARTCARTDATE: TDateTimeField 
    FieldName = 'CARTDATE' end object csCARTPRICE: TBCDField 
    FieldName = 'PRICE' 
    Size = 2 end object csCARTDESCRIPTION: TStringField 
    DisplayWidth = 36 
    FieldName = 'DESCRIPTION' 
    LookupDataSet = csITEM 
    LookupKeyFields = 'ITEMNO' 
    LookupResultField = 'DESCRIPTION' 
    KeyFields = 'ITEMNO' 
    Lookup = True end end 
    object csUSERNAME: TClientDataSet Active = True 
    ProviderName = 'quUSERNAME' RemoteServer = RemoteServerl Left = 504 Top = 96 

    610	Часть IV. Темы повышенной сложности 

    object csUSERNAMEUSERNAME: TStringField FieldName = 'USERNAME' Size = 31 
end end end 

    Резюме 

    Delphi предоставляет в ваше распоряжение набор мощных средств разработки 
приложений для Internet. В Delphi можно создавать любые типы приложений — от 
простого приложения типа броузера или программы работы с электронной почтой до 
сложных проектов, использующих элементы управления Active-Form и элементы 
программных интерфейсов ISAPI/NSAPI. Технология WebBroker Delphi позволяет 
избежать сложностей, которые обычно возникают при создании серверных 
приложений для Web, и сконцентрировать внимание на том, что вы хотите сделать, 
а не на том, как этого добиться. 

    Что дальше... 

    В главе 29, "Совершенствование приложения", подробно рассматривается 
процесс установки приложений Delphi, досконально изучается система 
InstallShield Express и обсуждаются вопросы, связанные непосредственно с 
совершенствованием, настройкой и подготовкой к эксплуатации приложений 
архитектуры клиент/сервер. 

    Глава 28. Приложения для работы с Internet	611 

