Показаны сообщения с ярлыком Ошибки. Показать все сообщения
Показаны сообщения с ярлыком Ошибки. Показать все сообщения

суббота, 11 апреля 2009 г.

Простой пример клиента для WebService

Одному моему коллеге понадобилось написать небольшого клиента для сервера контента системы электронного документооборота. Собственно сам сервер контента реализован в виде WEB-сервиса, предоставляющего некоторые функции для доступа к документам. Что он только не пробовал и ClientSocket и TCPConnection и HTTPRIO. Но на самом деле все достаточно просто, и не нужно изобретать велосипед :)

Сразу же оговорюсь , что данный пост не претендует на полноту изложения данной темы, здесь я просто хочу показать в каком направлении следует «копать» и покажу как обойти один подводный камень, на который можно наткнуться при создании клиента для WEB-сервисов.

Итак, приступим … У нас есть некоторый WEB-сервис, предоставляющий некоторые функции. Пусть это будет некоторый ASP.NET WebService запущенный на соседней машине с именем Athlon, URI данного WEB-сервиса выглядит следующим образом: http://athlon/test/service.asmx . Набрав данный URI в браузере получаем тестовую страничку, сгенерированную IIS для проверки работоспособности WEB-сервиса:


Из тестовой страницы видно, что WEB-сервис поддерживает одну единственную процедуру с именем HelloWorld. Попробуем ее вызвать из клиентского приложения на Delphi. Естественно создадим новый проект, сохраним куда-нибудь по-удобнее, и займемся самым интересным – автоматической генерацией кода для доступа к WEB-сервису. Для этого в среде Delphi есть специальный мастер который вызвается следующей командой: File -> New -> Other … -> WebServices -> WSDL Importer:



В мастер вводится один единственный параметр – это URI нашего WEB-сервиса:



Но при попытке перейти на следующий шаг мастера появится сообщение об ошибке, примерно следующего содержания, толком ничего не говорящего:



Давайте разберемся, что это такое и почему это происходит. Еще раз наберем URI WEB-сервиса в строке адреса браузера – на экране отобразится тестовая страничка. Тоже самое происходит когда по данному URI обращается мастер – в соответствии с протоколом SOAP он ждет XML описания WEB-сервиса, а ему вместо этого выдают обычную html-страницу. Именно по этому он вываливается с ошибкой невразумительного содержания, поскольку пытается разобрать html и ему это не удается и он сообщает на каких именно тэгах разметки он вы вывалился. Чтобы этого не происходило, мы должны явно указать WEB-сервису, что мы ждем от него WSDL описание сервиса. Делается это очень просто, просто добавляется параметр ?WSDL в URI WEB-сервиса:


http://athlon/test/service.asmx?WSDL



Теперь мастер получает именно те данные, которые ожидает и генерация кода происходит без ошибок:



В результате сгенерировался довольно большой код, ключевой фрагмент которого выглядит так:


  ServiceSoap = interface(IInvokable)
['{77573149-9C57-FA51-F11F-EFD527C91BD9}']
function HelloWorld: string; stdcall;
end;

function GetServiceSoap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): ServiceSoap;

Здесь объявляется интерфейс нашего WEB-сервиса и функция для получения этого интерфейса, именно с ними мы и будем дальше работать. Просто для примера вызовем в обаботчике события OnClick для кнопки процедуру и выведем сообщение с результатом


procedure TForm1.Button1Click(Sender: TObject);
var
ws: ServiceSoap;
begin
// получаем интерфейс
ws := GetServiceSoap();
// вызываем процедуру сервиса
with ws do
ShowMessage(HelloWorld);
end;

Вот собственно и все - клиент для WEB-сервиса готов. На его создание потрачено совсем немного времени и написано совсем небольшое количество кода.


четверг, 8 января 2009 г.

Перевод проекта на Delphi 2009 – первые грабли

Сегодня выдалось немного свободного времени и решил я поковырять свой проект на предмет совместимости с Delphi 2009. В одной из моих предыдущих заметок я уже говорил, что проект отказался компилироваться. Начнем разбираться почему.

Первым делом компилятор споткнулся о «мертвую» (нигде не использующуюся) процедуру RunDosAndWait которая запускала консольное приложение фоном и ждала его выполнения. Честно говоря, я намеренно не стал убирать эту процедуру и берег ее для такого случая, как она мне сегодня пригодится – речь пойдет немного позже.

Итак, не работает функция CreateProcess(nil, PChar(CommandLine), nil, nil, true, 0, nil, nil, StartupInfo, ProcessInfo). Попробовал поискать в инете, толком решения проблемы не нашел. На одном из форумов тоже сталкивались с этой проблемой, но решения предложено не было.

Пробуем просто создать процесс:

Получаем следующую картинку:



Поскольку новая Delphi 2009 это один большой Юникод, то думаю следует поискать причину в этом. Попробуем разобраться почему код отлично работавший в предыдущих версиях резко перестал работать. В предыдущих версиях (не Юникод) при вызове функции CreateProcess фактически вызывалась ANSI-версия этой функции – CreateProcessA, в новой Юникод-версии фактически вызывается CreateProcessW и по всей видимости в этом то и кроется основная проблема. А так как CreateProcess это самая что ни на есть WinAPI функция, попробуем поискать ответы на наши вопросы в MSDN.

Вот собственно, что и следовало доказать. Функции CreateProcessA и CreaterocessW ведут себя по-разному:

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

Что в вольном переводе означает, что эта функция может изменить содержание строки и поэтому параметр не может быть константой или строкой символов. Напрашивается самый простой вариант:


Но в этом случае программа так же откажется работать. Дело в том, что оптимизирующий компилятор Delphi вместо нашей переменной все равно воткнет константу, поскольку он видит, что значение нигде по коду не меняется. Чтобы проверить правильность наших выводов относительно функции CreateProcess слегка изменим код, чтобы обновить оптимизатор:


Теперь программа исполняется так как положено. Единственное, что мне необходимо было сделать, чтобы обойти этот баг – это изменить описание функции. Вместо:

function RunDosAndWait(const CommandLine: string; OutSrings: TStrings): boolean;

написать

function RunDosAndWait(var CommandLine: string; OutSrings: TStrings): boolean;.

Все - с этим разобрались, но в этой же процедуре осталась ошибка с процедурой OemToChar, переводящей кириллический текст из консоли в удобочитаемый вид. Но об этом – в другой раз.

воскресенье, 28 декабря 2008 г.

И снова Delphi 2009

Ура! Наконец то мне удалось запустить Delphi 2009 и избавиться от проблем о которых я писал ранее. Решил проблему кардинально - поставил Microsoft Virtual PC (кстати, был очень удивлен - не знал что она теперь полностью бесплатна) - поднял на ней Windows XP и на чистую систему Delphi 2009 установилась без проблем. А вот в чем была истинная причина тех непонятных сбоев мне выяснить так и не удалось, но и ладно, и в виртуальной машине можно работать, только вот колесо прокрутки что то глючит иногда :)

Но на этом проблемы с ней не закончились. Мой проект, над которым я работаю последние несколько лет - отказался компилироваться. Те ошибки которые касались моего кода удалось довольно быстро локализовать и исправить (они касались несовместимости типов), а вот основная проблема случилась с компонентами FIBPlus. На настоящий момент на сайте разработчиков нет версии компонентов под эту версию Delphi, что очень жаль - непростительно тормозят разработчики! На полумертвом форуме на официальном сайте как то обнаружил запись, что они должны были выйти еще в ноябре ... Что ж, видимо это дух времени - обещать релиз и не выполнять обещания ...

четверг, 27 ноября 2008 г.

Delphi 2009 спустя 3 месяца

Прошло уже почти 3 месяца с момента выхода Delphi 2009, а мне так и не довелось еще попробовать в ней поработать.

Неудачной оказалась попытка установки среды на компьютер с Windows Vista - при загрузке выскакивает сообщение об исключении в модуле coreide120.bpl. Небыло времени разбираться с причинами этой ошибки и я забросил это гиблое дело и снес ее, отметив для себя, что среда скорее всего конфликтует с ранее установленными версиями.

И вот появилось немного свободного времени и я решил разобраться в причинах ошибки. Сразу же не стал пробовать ставить ее на "живую" систему. Поставил на Windows XP - тот же результат, что и был ранее - ошибка в coreide120.bpl.

Далее среда продолжала загружаться и выдала самое мое любимое исключение, которое только можно получить в Delphi :)

Все, наработался кто-то на сегодня. Решил проверить на чистой системе -поставил диск с автоматическим установщиком Windows и ушел спать, решив продолжить свои попытки на следующий день.

На следующий день, как я и ожидал, Delphi 2009 совершенно спокойно поставилась на свежую систему, что еще больше меня убедило в мысли конфликта с ранее установленными версиями. Все снес и решил дальше разбираться с причинами проблемы. Сделал образ рабочей системы и приступил к "издевательствам" . Для начала снес все версии Delphi (их оказался целый зопарк - начиная от Turbo Delphi Explorer for Win32 заканчивая BDS 2006 и 2007 - как тут не заглючишь:))) и почистил реестр - не помогло, система упорно продолжает выдавать исключение и не загружаться. Видимо причина всетаки не в конфликтах ... Мне уже становится интересно :) Завтра продолжу свои исследования, поскольку сегодня опять уже 3 часа ночи.

понедельник, 27 октября 2008 г.

Подозрительный файл в проекте

Сегодня выдалась свободная минутка, и решил продолжить работу над одним из своих проектов. Сходу решил откомпилировать проект, чтобы вспомнить, на чем я остановился в прошлый раз. О-па! А проект-то не работает и IDE выдает следующую ошибку:

«Странно» - подумал я, припоминая, что когда то я уже боролся с этой проблемой … Первой шальной мыслью было, что перевод часов стал причиной сбоя. :) Но эту мысль я очень быстро откинул, однако сменил дату всех файлов проекта – так, на всякий случай.

Стал мыслить дальше, поскольку даже запуск приложения просто из-под Windows выдал ошибку – «Не удалось запустить приложение, поскольку его параллельная конфигурация неправильна». Открыл папку проекта и стал на нее смотреть, вскоре заметил один подозрительный файл, которого быть не должно! (он выделен на рисунке ниже)

Ума не приложу, откуда он нарисовался. Причем его содержимое было идентично файлу mntrng.config в котором у меня содержатся настройки приложения – поэтому система и не могла запустить приложение. Просто удалил файл – и все заработало в штатном режиме. Даже несколько раз перекомпилировал и собрал проект – файл пока больше не появился, откуда он только взялся ….