Создание приложений с базами данных для Интернета и интрасетей: практическое руководство (С) Александр Вячеславович Фролов, Григорий Вячеславович Фролов, 2000 8. Создание серверных элементов управления ActiveX 8. Создание серверных элементов управления ActiveX Первый проект элемента ActiveX Редактирование исходного текста свойства Автоматическая обработка кредитных карточек Библиотека для имитации интерфейса Тестовая программа для вызова имитатора интерфейса Вызов элемента управления CreditCard Отправка почтового сообщения из сценария ASP В этой главе мы кратко рассмотрим методику создания собственных элементов управления ActiveX для расширения объектной модели ASP. Мы будем назвать их серверными элементами управления ActiveX. В отличие от клиентских элементов управления ActiveX, загружаемых браузером для работы на компьютере посетителя, серверные элементы управления исполняются на сервере Web. Они не вызывают потенциальных проблем с безопасностью данных, возникающих при работе с клиентскими элементами ActiveX, а также проблем с совместимостью браузеров, характерных для технологии ActiveX. Зачем Вам создавать собственный серверный элемент ActiveX? Серверные сценарии, размещенные в страницах ASP, ограничены в своих возможностях рамками объектной модели ASP. Хотя, как Вы уже убедились, эта модель предоставляет прекрасные возможности для проектирования интерфейса пользователя и для обращения к базам данных, она имеет ограничения в плане вызова функций программного интерфейса Win32, а также функций, расположенных в библиотеках динамической загрузки DLL. Другой недостаток, возникающий из-за применения в серверных сценариях ASP интерпретируемых языков программирования JScript и VBScript — невысокая производительность. При формировании элементов пользовательского интерфейса этот недостаток не существенен, однако, если Ваше приложение предназначено для выполнения интенсивной обработки больших объемов данных, вопросы производительности становятся важны. Еще одна проблема, связанная с использованием серверных сценариев, имеет отношение к защите интеллектуальной собственности разработчика. Дело в том, что исходные тексты серверных и клиентских сценариев могут быть легко проанализированы администратором сервера Web, что не всегда желательно. Реализация бизнес-приложения в виде исполнимых модулей значительно затруднит работу злоумышленника, решившего «вскрыть» алгоритмы работы Вашего приложения. Конечно, все эти проблемы легко решаются при использовании таких расширений сервера Web, как программы CGI или ISAPI, рассмотренные в предыдущей главе. Однако этот подход тоже имеет свои недостатки. Наиболее существенные из них — это невозможность тесного взаимодействия расширений сервера Web с серверными сценариями ASP (удобными для создания пользовательского интерфейса и обращения к базам данных), а также сложность отладки. Намного проще создавать собственные расширения объектной модели в виде серверных элементов ActiveX. Эта технология допускает тесную интеграцию с серверными сценариями ASP. В случае реализации серверных элементов ActiveX на языке программирования С++ обеспечивается возможность вызова любых функций Win32 и функций из произвольных библиотек DLL. При этом обеспечивается высокая производительность работы созданных таким образом объектов. Следует заметить, что серверные элементы ActiveX можно создавать и с применением таких языков программирования, как Visual Basic или Java, однако это не всегда целесообразно. Язык Visual Basic удобен для разработки элементов ActiveX, но его применение вызывает проблемы с производительностью, так как Visual Basic относится к интерпретируемым языкам. Приложения Java также не отличаются высокой скоростью работы и не всегда способны обращаться напрямую к интерфейсам операционной системы. Поэтому в нашей книге мы расскажем лишь о приемах создания элементов ActiveX, созданных с применением С++. Причем для облегчения работы мы будем использовать библиотеку шаблонов Active Template Library (ATL). Хотя она предоставляет мощные средства для создания приложений COM, к которым относятся элементы ActiveX. Первый проект элемента ActiveX В этом разделе мы опишем поэтапную процедуру создания простейшего серверного элемента управления ActiveX с применением Microsoft Visual C++ версии 6.0 и библиотеки шаблонов ATL. Итак, начнем. Запустите Microsoft Visual C++ и выберите из меню File строку New. На экране появится диалоговая панель New, открытая на вкладке Projects (рис. 8-1). Рис. 8-1. Диалоговая панель New В списке типов проектов, расположенном в левой части этой панели, выберите строку ATL COM AppWizard. Далее укажите в поле Location путь к каталогу, в котором будет создан новый проект. В поле Project name задайте имя проекта. Если Вы выбрали какое-нибудь нестандартное имя для проекта, мы советуем добавить к нему строку Mod или Module, чтобы отличать имя модуля от имени файла, созданного для хранения исходного текста класса создаваемого объекта. Заполнив описанным образом поля панели New, щелкните кнопку OK. Запустится мастер проектов, первая панель которого показана на рис. 8-2. Рис. 8-2. Первая панель мастера проектов для элементов ActiveX Оставьте отмеченным переключатель Dynamic Link Library (DLL) и отметьте переключатель Allow merging of proxy/stub code. Далее щелкните кнопку Finish. Сразу после этого мастер проектов создаст в указанном Вами каталоге файлы исходных текстов, а затем выведет на экран панель со списком основных созданных файлов (рис. 8-3). Рис. 8-3. Окончание работы мастера приложений В результате работы мастера проектов создается проект библиотеки DLL, в которой пока не определено ни одного объекта. Выберите в меню Insert системы проектирования Microsoft Visual C++ строку New ATL Object. Откроется первая панели мастера объектов ATL ( рис. 8-4). Рис. 8-4. Первая панель мастера объектов ATL Здесь выделите пиктограмму ActiveX Server Component, а затем щелкните кнопку Next. В результате на экране появится вторая панель мастера объектов ATL с тремя вкладками (рис. 8-5). Рис. 8-5. Вкладка Names в панели мастера объектов ATL В поле Short Name введите имя объекта как BookStoreLogin. В результате в остальных полях этой вкладки появятся имена, выбранные для различных объектов по умолчанию. Хотя Вы можете оставить все как есть, мы рекомендуем отредактировать поля Type и ProgID. В поле Type нужно ввести текстовое описание создаваемого объекта, удобное для поиска в списке объектов, отображаемых программой OLE View, входящей в комплект Microsoft Visual C++. Например, можно указать в начале этого поля название Вашей фирмы, а в конце — название объекта. В поле ProgID следует ввести два идентификатора, разделенных точкой. В качестве первого можете набрать название фирмы (латинскими символами), а в качестве второго — название объекта. Мы вводим в этом поле строку «BookStore.BookStoreLogin». Теперь откройте вкладку Attributes (рис. 8-6). Рис. 8-6. Вкладка Attributes в панели мастера объектов ATL Здесь оставьте все элементы управления в том состоянии, в котором они показаны на рисунке. Группа переключателей Threading Model позволяет выбрать одну из четырех моделей для работы с потоками. Не вдаваясь сейчас в тонкости отличия моделей, скажем, что в большинстве случаев следует выбирать модель Apartment, как показано на рис. 8-6. Если Ваш объект не создает событий, то переключатель Support Connection Points следует оставить без отметки. Однако если в будущем Вы планируете использовать события, этот переключатель необходимо отметить. Остальные элементы управления оставьте в состоянии, показанном на рисунке. Теперь откройте вкладку ASP (рис. 8-7). Рис. 8-7. Вкладка ASP в панели мастера объектов ATL Если Вы отметить переключатель OnStartPage/OnEndPage, показанный на этом рисунке, создаваемый Вами элемент управления получит способность обращаться к объектам, свойствам и методам ASP. Это могут быть, например, переменные сеанса или приложения, параметры заголовка запроса и другие объекты, доступные серверным сценариям, расположенным на странице ASP. Заметим, однако, что если все, что Вам нужно для связи серверного сценария и элемента управления ActiveX, это передача параметров, то достаточно определить соответствующие методы и свойства. Именно так мы и поступим в наших примерах. Итак, мы установили все параметры на вкладках мастера объектов ATL. Теперь щелкните кнопку OK для запуска генерации исходных текстов. Через непродолжительное время исходные тексты шаблона Вашего элемента управления ActiveX будут построены. А нам надо определить собственные методы и свойства. Сначала мы определим свойство с именем CheckResult, получающее в качестве параметров две входные текстовые строки и возвращающее выходную текстовую строку, созданную слиянием входных строк. Откройте вкладку ClassView в главном окне Microsoft Visual C++ (рис. 8-8). Рис. 8-8. Добавление нового свойства Раскройте папку интерфейса IBookStoreLogin, а затем щелкните правой клавишей мыши строку IbookStoreLogin. Затем выберите из контекстного меню строку Add Property, как это показано на рис. 8-8 (строка Add Method позволяет добавить новый метод). На экране появится панель Add Property to Interface, показанная на рис. 8-9. Рис. 8-9. Панель, предназначенная для добавления нового метода Так как наше свойство предназначено для хранения строк, выберите тип BSTR в списке Property Type, определяющем тип свойства. В поле Property Name Вы должны ввести имя свойства. В нашем случае это имя CheckResult. В поле Parameters необходимо указать параметры метода, разделив их запятой. Укажите здесь параметры bsName и bsPassword типа BSTR*. Первый параметр представляет собой указатель на строку типа BSTR с именем пользователя, а второй — указатель на строку BSTR с паролем пользователя. Как Вы, наверное, знаете, для каждого свойства можно определить две функции, первая из которых предназначена для чтения содержимого свойства, а вторая — для записи в свойство нового значения. Чтобы мастер создания свойства добавил исходный текст и описание соответствующей функции в проект, необходимо отметить переключатели Get Function и Put Function. В нашем примере мы создаем только одно свойство, предназначенное для чтения, поэтому надо отметить только один переключатель Get Function. Сделав это, щелкните кнопку OK. В проект будет добавлен исходный текст метода CheckResult. Чтобы увидеть его исходный текст, раскройте папку интерфейса IBookStoreLogin, расположенную в папке класса CbookStoreLogin, и дважды щелкните название метода get_CheckResult. Вот что Вы увидите: STDMETHODIMP CBookStoreLogin::get_CheckResult( Редактирование исходного текста свойства Добавьте в определение метода следующие строки: USES_CONVERSION; Макрокоманда USES_CONVERSION используется для обозначения того факта, что наш метод будет применять перекодировку строк BSTR в формат обычных строк ASCII, закрытых двоичным нулем, причем для перекодировки будут применяться макрокоманды OLE2A и A2OLE. Первая из них предназначена для преобразования строк BSTR в строки ANSI, а вторая выполняет обратное действие. В начале своей работы добавленный фрагмент кода проверяет указатель pOutVal, передаваемый методу для записи значения свойства. Если он равен NULL, метод завершает свою работу с соответствующей ошибкой. Далее мы преобразуем входные параметры bsName и bsPassword в обычные текстовые строки ANSI, записывая указатели на результат преобразования соответственно в поля класса lpszName и lpszPassword. Эти поля типа LPSTR Вам надо добавить самостоятельно в класс CBookStoreLogin обычным образом. После преобразования наш метод копирует входные строки в буфер szBuf, разделяя их двоеточием. Для преобразования результата копирования в тип BSTR мы создаем указатель bstrTemp типа CComBSTR и записываем в него результат преобразования, выполненного макрокомандой A2OLE. Если оно выполнено с ошибкой, в указатель bstrTemp будет записано нулевое значение. В этом случае метод возвращает код ошибки, означающий отсутствие необходимого объема свободной памяти. Чтобы вернуть значение свойства, мы вызываем метод Detach, определенный в классе CComBSTR. На этом работа метода закончена. Добавив описанные выше строки в определение метода CheckResult, запустите проект на трансляцию. Созданный в виде библиотеки DLL элемент управления будет зарегистрирован и доступен для вызова. Если этот файл Вы предполагаете использовать на другом компьютере, его нужно зарегистрировать при помощи программы REGSVR32, передав ей путь к файлу DLL. Запустив программу REGSVR32 без параметров, Вы увидите на экране краткую инструкцию по ее использованию. Теперь мы подготовим страницу ASP, вызывающую созданный нами серверный элемент управления ActiveX. На самом деле мы создадим две страницы. Первая из них представляет собой обычный документ HTML с формой, в которой посетитель вводит свой идентификатор и пароль (рис. 8-10). Рис. 8-10. Форма для ввода идентификатора и пароля Если после ввода информации щелкнуть кнопку Вход, управление будет передано странице ASP, вызывающей наш элемент управления ActiveX. На этой странице отображается строка, сформированная элементом BookStoreLogin (рис. 8-11). Рис. 8-11. Результат работы серверного сценария Исходный текст документа HTML с формой представлен в листинге 8-1. Листинг 8-1 Вы найдете в файле ch08/BookStoreLoginMod/bslogin.html на прилагаемом к книге компакт-диске. Определенная в нем форма содержит два поля редактирования с именами USR и PWD: <FORM ACTION="BookStoreLogin.asp"
METHOD="post" TARGET="_top"> После щелчка кнопки Вход управление передается странице с именем BookStoreLogin.asp, указанным в параметре ACTION тега <FORM>. Исходный текст этой страницы представлен в листинге 8-2. Листинг 8-2 Вы найдете в файле ch08/BookStoreLoginMod/BookStoreLoginMod.asp на прилагаемом к книге компакт-диске. Первая строка в файле BookStoreLogin.asp задает язык серверного сценария JScript: <%@ LANGUAGE = "JScript" %> Далее в документе расположен фрагмент серверного сценария, показанный ниже: <% Здесь мы вначале извлекаем параметры с именами USR и PWD, переданные формой, и сохраняем эти параметры в переменных sUser и sPassword соответственно. Далее с помощью метода CreateObject встроенного объекта ASP с именем Server мы создаем объект с идентификатором BookStore.BookStoreLogin. Это тот самый идентификатор, который мы определяли при создании проекта в панели, показанной на рис. 8-5. Объект будет записан в переменную culogin, при помощи которой сценарий обращается к свойствам и методам объекта. Строкой ниже мы читаем свойство CheckResult, передавая ему в качестве параметров идентификатор посетителя и его пароль. Значение, прочитанное из свойства, сценарий записывает в переменную sResult. Далее эта переменная используется при формировании текста документа HTML, создаваемого сценарием: <p>Вы ввели (идентификатор:пароль) - <b><%= sResult %></b> Исходный текст метода CheckResult, а также двух других методов, созданных мастером проекта, находится в файле BookStoreLogin.cpp (листинг 8-3). Листинг 8-3 Вы найдете в файле ch08/BookStoreLoginMod/BookStoreLogin.cpp на прилагаемом к книге компакт-диске. Рассмотрим его содержимое. Помимо файла stdafx.h мастер проекта включает в файл BookStoreLogin.cpp файлы BookStoreLoginMod.h и BookStoreLogin.h: #include
"BookStoreLoginMod.h" Первый из них создается автоматически и содержит определения интерфейсов. Вам не нужно его редактировать, так как это сделает мастер проекта при добавлении новых методов и свойств. Второй файл содержит определение класса CBookStoreLogin, который представляет собой главный класс нашего объекта. Опять же, при добавлении в определение этого класса новых полей мастер проекта сделает все изменения в файле автоматически. Методы OnStartPage и OnEndPage генерируются автоматически, если при создании проекта отметить переключатель OnStartPage/OnEndPage на вкладке ASP (рис. 8-7). Когда посетитель запрашивает страницу ASP, управление получает метод OnStartPage. Ему передается указатель на интерфейс IUnknown, с помощью которого получит указатели на интерфейсы, нужные для связи нашего элемента со встроенными элементами ASP: STDMETHODIMP
CBookStoreLogin::OnStartPage (IUnknown* pUnk) Вначале метод OnStartPage получает указатель на интерфейс IscriptingContext: CComPtr<IScriptingContext>
spContext; Далее, пользуясь этим интерфейсом, метод извлекает указатели на объекты Request, Response, Server, Session и Application: hr =
spContext->get_Request(&m_piRequest); Метод OnEndPage освобождает полученные указатели при завершении обработки серверного сценария, расположенного на странице: m_piRequest.Release(); Кроме методов OnStartPage и OnEndPage в файле BookStoreLogin.cpp находится определение созданного нами метода get_CheckResult: STDMETHODIMP
CBookStoreLogin::get_CheckResult( Автоматическая обработка кредитных карточек Создавая Интернет-магазин, Вы должны продумать способ оплаты товара. Наиболее современный способ оплаты — с помощью кредитных карточек. Мы уже рассказывали о том, как организовать такую оплату с привлечением процессинговых компаний, выполняющих обработку кредиток через Интернет. Как Вам известно, такие компании предоставляют всем желающим интерфейс в виде библиотеки DLL или автономной программы. Модуль интерфейса необходимо разместить на сервере Web Вашего магазина, причем вызов функций этого модуля должен выполняться из программного обеспечения магазина. Если магазин создан с применением технологии ASP, нужно придумать способ вызова интерфейсного модуля из серверных сценариев, написанных на языках JScript или VBScript. В этом разделе мы расскажем о том, как создать серверный элемент управления ActiveX, вызывающий функции из библиотеки DLL, имитирующей часть интерфейса обработки кредитных карточек. Библиотека для имитации интерфейса Наш демонстрационный интерфейс обработки кредитных карточек состоит всего из одной функции с именем fnSendPayData. Ее задача — отправить информацию о платеже на сервер процессинговой компании. Напомним, что при создании реального магазина Вы получите интерфейсный модуль от процессинговой компании. Прототип функции fnSendPayData показан ниже: extern "C" __declspec(dllexport) int
fnSendPayData( Так как предполагается, что мы вызываем функцию из программы, написанной на С++, для отключения механизма «украшения» имени функции (name mangling) используется определение extern "C". Чтобы функция fnSendPayData попала в список экспортируемых функций, определим ее как __declspec(dllexport). Это позволит нам не создавать def-файл определения модуля библиотеки DLL. При вызове функции fnSendPayData необходимо передать шесть параметров. Через первые два параметра szAmount и szCurrency передается величина суммы и название валюты. И тот, и другой параметр задается в виде текстовой строки. Третий параметр dwMerchantID — идентификатор покупателя, назначенный ему при регистрации и зарегистрированный в процессинговой компании. По этому идентификатору компания сможет определить, кто выполняет платеж. Через последние три параметра интерфейсный модуль передает серверу магазина адреса URL-страниц, на которые попадает посетитель в зависимости от результата выполнения платежа. Если результат успешный, посетитель попадает на страницу, адрес которой задан параметром szSuccessURL, если нет — на страницу с адресом szErrorURL, а если при выполнении операции появилась дополнительная информация — на страницу с адресом szNoyificationURL. Функция fnSendPayData возвращает код выполнения операции. Если этот код равен нулю, операция выполнена успешно, а если нет, то это означает, что были ошибки. Рассмотрим действия, выполняемые функцией fnSendPayData. Ее исходный текст Вы найдете в листинге 8-4. Листинг 8-4 хранится в файле ch08/CreditCardInterface/CreditCardInterface.cpp на прилагаемом к книге компакт-диске. Прежде всего, эта функция проверяет идентификатор покупателя: if(dwMerchantID != 12345) Мы разрешаем выполнение платежа только для посетителя с идентификатором 12345. При ошибке возвращается значение 1. Процессинговая компания проверяет этот идентификатор по своей базе данных. Далее функция fnSendPayData убеждается, что параметры szAmount и szCurrency не содержат нулевых значений: if(strlen(szAmount) == 0 ||
strlen(szCurrency) == 0) В случае ошибки функция возвращает значение 2. Если же параметры указаны правильно, функция fnSendPayData записывает имена файлов по адресам, хранящимся в параметрах szSuccessURL, szErrorURL и szNoyificationURL: if(szSuccessURL != NULL) Далее функция возвращает нулевой значение в качестве признака успешного завершения своей работы. Помимо функции fnSendPayData в исходном тексте нашей библиотеки DLL определена стандартная функция DllMain: BOOL APIENTRY DllMain(HANDLE hModule, Она вызывается, когда процесс или поток обращается к библиотеке. Эта функция была создана мастером Microsoft Visual C++ и не выполняет никаких действий. Тестовая программа для вызова имитатора интерфейса Прежде чем вызывать только что описанный модуль из серверного сценария ASP, выполним его тестирование. Для этого мы подготовили небольшую консольную программу, исходный текст которой Вы найдете в листинге 8-5. Листинг 8-5 хранится в файле ch08/CreditCardInterfaceTest/CreditCardInterfaceTest.cpp на прилагаемом к книге компакт-диске. В области глобальных переменных этой программы мы подготовили определение типа DLLFN: typedef int (* DLLFN)(LPSTR szAmount, LPSTR szCurrency,
Как видите, этот тип представляет собой указатель на функцию fnSendPayData, описанную ранее. Тестовая программа вызывает функцию fnSendPayData, предварительно выполняя динамическую загрузку библиотеки DLL с именем CreditCardInterface.dll. Библиотека DLL загружается функцией LoadLibrary, как это показано ниже: HINSTANCE hCreditCardInterfaceDLL; Идентификатор загруженной библиотеки сохраняется в локальной переменной hCreditCardInterfaceDLL. Если загрузка прошла успешно, тестовая программа вызывает функцию fnSendPayData, предварительно получив на нее указатель с помощью функции GetProcAddress: DLLFN fn; Здесь мы передаем функции сумму 123, название валюты — «usd», идентификатор покупателя 12345. Адреса URL функция запишет в переменные szSuccessURL, szErrorURL и szNoyificationURL. Убедившись с помощью тестовой программы и отладчика, что интерфейсная библиотека DLL работает нормально, мы переходим к созданию серверного элемента управления ActiveX, доступного из страниц ASP и предназначенного для вызова функции fnSendPayData. В начале этой главы мы подробно рассмотрели процедуру создания серверного элемента управления ActiveX с применением библиотеки шаблонов ATL. Поэтому сейчас мы не будем вдаваться в детали. Нам нужно создать модуль CreditCardMod. Идентификатор элемента управления должен быть указан как «Frolov.CreditCard».Исходный текст главного модуля элемента управления, созданный мастером и дополненный нами, Вы найдете в листинге 8-6. Листинг 8-6 хранится в файле ch08/CreditCardMod/CreditCard.cpp на прилагаемом к книге компакт-диске. Рассмотрим определения свойств и методов, добавленных нами к объекту CreditCard. Для свойства Amount, предназначенного для хранения суммы денег, мы предусмотрели две функции с именами get_Amount и put_Amount: STDMETHODIMP CCreditCard::get_Amount(BSTR
*pVal) Обе эти функции обращаются к полю m_Amount типа CComBSTR: CComBSTR m_Amount; Мы добавили эту переменную в класс CreditCard, пользуясь мастером класса. Функция put_Amount записывает в поле m_Amount значение, полученное через параметр newVal. Что же касается функции get_Amount, то она извлекает значение из поля m_Amount и возвращает его, вызывая метод Copy. Аналогичным образом устроены функции для работы со свойствами Currency, SuccessURL, ErrorURL и NotificationURL. Для них в классе CreditCard мы определили следующие поля: CComBSTR m_ErrorURL; Функции для работы со свойством MerchantID, хранящим численное значение, определены так: STDMETHODIMP
CCreditCard::get_MerchantID(long *pVal) Эти функции обращаются к полю m_MerchantID типа long. Теперь мы займемся методом SendPayData, предназначенным для вызова нашей библиотеки DLL, имитирующей интерфейс процессинговой компании. Так же как и в тестовой программе, мы определили тип DLLFN, необходимый для вызова функции fnSendPayData через указатель: typedef int (* DLLFN)(LPSTR szAmount, LPSTR szCurrency,
В самом начале метода SendPayData, вызывающего эту функцию, мы расположили макрокоманду USES_CONVERSION, необходимую для работы с макрокомандами перекодировки OLE2A и A2OLE: STDMETHODIMP
CCreditCard::SendPayData() В области локальных переменных метода SendPayData мы определили переменную для хранения идентификатора библиотека DLL hCreditCardInterfaceDLL, переменную для хранения указатель на функцию fnSendPayData с именем fn, три массива для хранения адресов URL с именами szSuccessURL, szErrorURL и szNotificationURL, а также рабочую переменную bstrTemp типа CComBSTR: HINSTANCE hCreditCardInterfaceDLL; В начале своей работы метод SendPayData загружает интерфейсную библиотеку DLL из файла CreditCardInterface.dll и получает указатель на функцию fnSendPayData: hCreditCardInterfaceDLL =
LoadLibrary("CreditCardInterface.dll"); Если библиотека загрузилась, а указатель получен без ошибок и не равен NULL, метод преобразует значения из полей m_Amount и m_Currency в текстовые строки ANSI: pszAmount = OLE2A(m_Amount); Соответствующие указатели pszAmount и pszCurrency Вы должны определить в классе CreditCard: char* pszCurrency; Теперь можно вызывать функцию fnSendPayData: m_Result = fn(pszAmount, pszCurrency,
m_MerchantID, Ее код возврата мы записываем в поле m_Result, определенное в классе CreditCard как long. Если параметры для функции указаны без ошибок, метод освобождает идентификатор библиотеки DLL, а затем преобразует полученные строки адресов URL, записывая их в соответствующие поля класса CreditCard: FreeLibrary(hCreditCardInterfaceDLL); При ошибке в поле m_Result записывается значение –1: m_Result = -1; Сценарий определяет результат вызова функции с помощью свойства Result, доступного только для чтения. Для этого свойства мы подготовили только одну функцию с именем get_Result: STDMETHODIMP
CCreditCard::get_Result(long *pVal) Вызов элемента управления CreditCard Для вызова элемента управления CreditCard мы подготовили документ HTML с формой, позволяющей задать идентификатор платежа, сумму и выбрать валюту (рис. 8-12). Рис. 8-12. Форма для вызова элемента управления CreditCard Полный исходный текст этого документа расположен в листинге 8-7. Листинг 8-7 Вы найдете в файле ch08/CreditCardMod/pay.html на прилагаемом к книге компакт-диске. Этот документ содержит форму, ссылающуюся на страницу ASP с именем dopay.asp: <form method="POST" action="dopay.asp"> В форме определены поля ввода идентификатора MerchID, суммы платежа Amount, а также поле кода валюты Currency: <tr> Исходный текст сценария, выполняющего вызов элемента управления CreditCard, представлен в листинге 8-8. Листинг 8-8 Вы найдете в файле ch08/CreditCardMod/dopay.asp на прилагаемом к книге компакт-диске. В начале своей работы сценарий создает объект CreditCard, указывая его идентификатор: var cc = Server.CreateObject("Frolov.CreditCard"); Ссылка на объект записывается в переменную cc, с помощью которой мы будем обращаться к свойствам и методам объекта. Первым делом необходимо извлечь из полей формы информацию о платеже. Мы это делаем с помощью объекта Request: cc.Amount =
Request("Amount")(1); Так как в форме может быть указан пустой идентификатор, мы обрабатываем такую ситуацию как ошибочную, записывая при этом в свойство MerchantID нулевое значение. Далее сценарий вызывает метод SendPayData: cc.SendPayData(); Результаты работы метода SendPayData извлекаются из свойств элемента управления и сохраняются в соответствующих локальных переменных: var Result = cc.Result; Далее, если метод выполнен без ошибок (то есть если в свойстве cc.Result находится нулевое значение), наш сценарий отображает содержимое всех свойств объекта: if(Result == 0) В противном случае в окне браузера отобразится лишь код завершения. Отправка почтового сообщения из сценария ASP Как правило, в Интернет-магазинах, созданных за рубежом, оплата товаров производится с помощью кредитных карточек. Что же касается отечественных Интернет-магазинов, то в них преобладают другие формы: предварительный сбор заказов и оплата наличными или через сберегательный банк. При этом торгующий сервер Web выступает в роли автоматизированной системы сбора заказов. Такие заказы обычно отправляются в магазин в виде электронных почтовых сообщений и затем обрабатываются сотрудниками магазина вручную. Для подтверждения покупки сделавшему заказ посетителю сервера перезванивают по телефону или запрашивают подтверждение по электронной почте. Это не слишком удобно, но в более или менее надежно. В качестве примера можно привести маленький магазин, расположенный на сервере авторов этой книги по адресу http://www.glasnet.ru/~frolov, а также такие крупные отечественные книжные магазины, подобные Bolero (http://www.bolero.ru). Для практической реализации системы автоматизированного сбора предварительных заявок обычно используются программы CGI типа FormMail. Эти программы принимают данные из полей формы и отправляют их почтовому серверу при помощи протокола SMTP. А сейчас речь пойдет о применении серверного элемента управления ActiveX с названием MTASend, разработанного Максимом Синевым из компании Spektrum Web Development (http://www.spektrum.org.ru) и предназначенным для отправки почты. Так как этот элемент реализует интерфейс автоматизации, его можно вызывать из клиентских и серверных сценариев, а также из любых программ, способных работать с объектами COM. Как Вы знаете, простой протокол передачи почты Simple Mail Transfer Protocol (SMTP) основан на передаче текстовых строк через порт с номером 25. Как правило, провайдеры требуют, чтобы для передачи почты было установлено прямое соединение между сервером или рабочей станцией, отправляющей почту, и почтовым сервером провайдера (то есть они должны находиться в сети с одним адресом IP). Прежде чем познакомить Вас с исходными текстами серверного элемента управления MTASend, мы попытаемся отправить почтовое сообщение с помощью программы telnet, входящей в комплект операционной системы Windows. Итак, подключитесь к своему провайдеру и запустите программу telnet. Выберите из меню Connect строку Remote system. Укажите в поле Host name адрес почтового сервера своего провайдера, а в поле Port — значение 25. Щелкните кнопку Connect для подключения к почтовому серверу провайдера. Если адрес сервера указан правильно, в окне программы появится сообщение от почтового сервера. Пример такого сообщения для сервера smtp.galsnet.ru показан ниже: 220-hawk.glas.apc.org Smail-3.2.0.109 (#4 1999-Nov-10) ready at Sun, 26 Dec 1999 Для другого сервера внешний вид приглашения может отличаться от приведенного выше, однако текст должен начинаться с цифры 220. Теперь отправьте серверу команду подключения HELO: HELO smtp.glasnet.ru Сервер должен ответить сообщением с кодом 250: 250 hawk.glas.apc.org Hello smtp.glasnet.ru (smtp.glasnet.ru from address [195.178.200.74]). Продолжим диалог с сервером.: командой MAIL FROM укажите адрес отправителя (тое есть свой адрес): MAIL FROM: frolov@glasnet.ru В ответ Вы должны получить сообщение с кодом 250: 250 2.1.0 frolov@glasnet.ru Sender Okay. Теперь введите адрес получателя. Мы указали здесь свой собственный адрес, так как отправляем тестовое сообщение сами себе: RCPT TO: frolov@glasnet.ru Вам придет подтверждение следующего вида: 250 2.1.0 'frolov@glasnet.ru' Recipient Okay. Далее мы должны ввести текст сообщения. Выдайте серверу команду DATA: DATA Вы получите приглашение для ввода текста сообщения с кодом 354: 354 Enter mail, end with "." on a line by itself... Введите тему сообщения командой SUBJECT: SUBJECT:Test Тема указывается в одной строке, непосредственно после команды. закончите ввод строкой, состоящей из одной точки в первой позиции: This is test Сообщение будет отправлено, а Вы получите извещение об этом в следующем виде: 250 2.6.0 Mail accepted, queue ID m122Hir-001oz7n. Теперь Вы можете разорвать соединение с почтовым сервером и завершить работу программы telnet. Запустив свою почтовую программу, убедитесь, что Вы получили новое сообщение. Мы покажем, как использовать элемент управления MTASend для отправки почтовых сообщений из формы, расположенной на Вашем сервере Web. Форма показана на рис. 8-13. Рис. 8-13. Форма для отправки почты При ее заполнении нужно указать адрес отправителя и получателя, тему письма Subject, а также адрес почтового сервера Вашего провайдера. Для отправки сообщения щелкните кнопку Отправить. Если электронное письмо отправлено без ошибок, в окне браузера появится сообщение, показанное на рис. 8-14. Рис. 8-14. Сообщение об успешной отправке почты Полный исходный текст формы отправки сообщения показан в листинге 8-9. Листинг 8-9 Вы найдете в файле ch08/MTASend/sendmail.html на прилагаемом к книге компакт-диске. Форма ссылается на страницу ASP с именем mta.asp, которая и выполняет обращение к элементу управления MTASend: <form method="POST" action="mta.asp"> В форме также определены поля с именами To, From, Subject, SMTPServer и Message: <tr><td
width="171">Кому:</td><td width="297"> Исходный текст страницы mta.asp Вы найдете в листинге 8-10. Листинг 8-10 Вы найдете в файле ch08/MTASend/mta.asp на прилагаемом к книге компакт-диске. В самом начале этого листинга находится тег <OBJECT>, закрытый символами комментария: <!-- <OBJECT RUNAT=SERVER ID=MTAMail CLASSID="CLSID:F477288F-ABB2-11D3-9DE9-204C4F4F5020"></OBJECT> --> Вы можете использовать его вместо метода CreateObject для создания объекта MailMTA, средствами которого выполняется передача почты: var MTAMail = Server.CreateObject("MailATL.MTA"); После того как сценарий создает объект MailMTA и записывает его в переменную MTAMail, он получает данные из полей формы и помещает их в соответствующие свойства: MTAMail.To=Request("To")(1); В свойство SMTPPort мы записываем значение 25, так этот порт используется стандартными почтовыми серверами. Подготовив свойства, сценарий вызывает метод Send, выполняющий отправку почты: MTAMail.Send(); Результат выполнения операции отправки почты сохраняется в свойстве Status, и наш сценарий может его проверить. Если почта отправлена без ошибок, сценарий записывает в создаваемый документ HTML соответствующее сообщение: if(MTAMail.Status == 1) В противном случае в текст сообщения об ошибке включается код завершения из свойства Status, а также уточняющий код ошибки из свойства StatusEx: else { Теперь мы кратко рассмотрим исходные тексты элемента управления MTASend. Прежде всего, обратите внимание на файл MailATL.cpp (листинг 8-11). Листинг 8-11 Вы найдете в файле ch08/MTASend/MailATL.cpp на прилагаемом к книге компакт-диске. В нем нам интересны функции DllRegisterServer и DllUnregisterServer, предназначенные соответственно для регистрации и для отмены регистрации элемента управления: STDAPI DllRegisterServer(void) В них мы вызываем функции WSAStartup и WSACleanup, предназначенные для инициализации сокетов и для освобождения ресурсов приложения, полученных для работы с сокетами. Определение методов Вы найдете в файле MTA.cpp (листинг 8-12). Листинг 8-12 хранится в файле ch08/MTASend/MTA.cpp на прилагаемом к книге компакт-диске. Для сокращения объема исходных текстов, определяющих функционирование сходных методов, в этом файле определена макрокоманда MTAStringProperty: #define MTAStringProperty(v) \ Она определяет два метода, один из которых извлекает значение свойства из соответствующего поля класса, а другой — сохраняет значение в поле класса. Вот как просто выглядит определение нескольких свойств, сделанное с помощью этой макрокоманды: MTAStringProperty(From) В классе MTA определен конструктор, выполняющий начальную инициализацию свойств значениями, взятыми из ресурсов элемента управления: MTA::MTA() В файле MTA.cpp Вы также найдете определение свойств SMTPPort, Status и StatusEx. Файл send.cpp (листинг 8-13) содержит определение функций и методов, применяемых для установки соединения с почтовым сервером и для отправки сообщения. Листинг 8-13 хранится в файле ch08/MTASend/send.cpp на прилагаемом к книге компакт-диске. Рассмотрим метод Send, посылающий сообщение: STDMETHODIMP MTA::Send() Прежде чем приступить к работе, этот метод устанавливает код завершения, равный 1, в поле m_Status. Этот код впоследствии можно извлечь из свойства Status. Далее метод Send устанавливает соединение с почтовым сервером, вызывая метод Connect. Если эта процедура выполнена успешно, метод Send вызывает метод Transfer, выполняющий передачу данных. Далее программа отключается от почтового сервера при помощи метода Disconnect. В случае возникновения каких-либо ошибок устанавливается новое состояние в поле m_Status. Исходный текст метода Connect мы рассмотрим ниже. Получив управление, метод Connect инициализирует структуру srv_addr типа sockaddr_in, записывая в нее адрес почтового сервера: m_StatusEx=0; Далее мы получаем адрес IP почтового сервера и сохраняем его в переменной he: if(srv_addr.sin_addr.s_addr==INADDR_NONE)
Полученный адрес копируется в поле s_addr структуры srv_addr.sin_addr. На следующем этапе метод Connect получает сокет для связи с почтовым сервером: srv_addr.sin_port=htons(Port); Этот сокет затем привязывается функцией bind к адресу IP почтового сервера: cli_addr.sin_family=AF_INET; Далее выполняется соединение: if(rc &&
connect(m_Socket,(LPSOCKADDR)&srv_addr,sizeof(srv_addr)) Теперь мы займемся методом Transfer, выполняющим обмен данными с почтовым сервером по каналу, созданному методом Connect. В начале с помощью макрокоманды WAIT этот метод дожидается получения от почтового сервера сообщения о готовности: WAIT(220); Когда сообщение, имеющее код 220, приходит, мы посылаем почтовому серверу команду HELO, указав ей в качестве параметра адрес почтового сервера: WriteLn("HELO "+m_SMTPServer); Для отправки почтовому серверу текстовой строки мы вызываем функцию WriteLn, определенную в нашей программе. Дождавшись подтверждения с кодом 250, мы сообщаем серверу адрес отправителя: CHECK(250); Далее метод снова ожидает подтверждения и посылает почтовому серверу аналогичным образом адрес получателя и содержимое письма, включая все стандартные заголовки. Отправка заканчивается пустой строкой и строкой, содержащей одну точку в первой позиции: WriteLn(""); Далее наша программа отключается от почтового сервера: WriteLn("QUIT"); Работа методов WriteLn и ReadLn. с помощью которых наш элемент управления общается с почтовым сервером, основана на применении функций send и recv. Эти функции предназначены для обмена данными через потоковые сокеты. Метод Disconnect, выполняющий отключение от почтового сервера, закрывает сокет функцией closesocket: void MTA::Disconnect() |