Конференция "Сети" » SOAP запрос через WinInet [D7, WinXP]
 
  • Maksim V. © (14.07.11 20:59) [0]
    Не удается полностью загрузить xml файл (вероятно более 8 кб), полученный после отправки SOAP запроса на сервер.

    function SendSoapRequest(hResourceHandle: HINTERNET; pszSoap: Utf8String): AnsiString;
    var
     dwStatus      : DWORD;
     dwStatusSize  : DWORD;
     dwReserved    : DWORD;
     bRet          : Boolean;
     pszText       : Utf8String;
     dwBytesToWrite: DWORD;
     dwBytesRead   : DWORD;
     pByteData     : TByteArr;
    begin

     Result := '';

     bRet := HttpSendRequest(hResourceHandle, nil, 0, LPCSTR(pszSoap), lstrlen(LPCSTR(pszSoap)));
     if bRet then
     begin

       dwStatus := 0;
       dwStatusSize := SizeOf(dwStatus);
       dwReserved := 0;
       bRet := HttpQueryInfo(
         hResourceHandle,
         HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE,
         @dwStatus,
         dwStatusSize,
         dwReserved
       );

       if (bRet and (dwStatus = HTTP_STATUS_OK)) then
       begin

         bRet := InternetQueryDataAvailable(hResourceHandle, dwBytesToWrite, 0, 0);
         if (bRet and (dwBytesToWrite > 0)) then
         begin

           SetLength(pByteData, dwBytesToWrite);

           repeat

             bRet := InternetReadFile(hResourceHandle, @pByteData[0], dwBytesToWrite, dwBytesRead);
             if (bRet and (dwBytesRead > 0)) then
             begin

               pszText := Copy(LPCSTR(pByteData), 0, dwBytesRead);
               Result := Result + Utf8ToAnsi(pszText);

             end;

           until
             dwBytesRead = 0;

           SetLength(pByteData, 0);

         end;

       end;

     end;

    end;



    Подскажите где ошибка, явно сделал все мыслимые проверки уже где только можно, не знаю где косяк в реализации.
  • DVM © (14.07.11 21:40) [1]
    я не большой знаток WinInetApi, но думается мне, что InternetReadFile надо вызывать в цикле, пока не будет загружен весь объем.
  • Maksim V. © (14.07.11 22:02) [2]
    Да тут же есть repeat / until чем не цикл? :(
  • Медвежонок Пятачок © (14.07.11 22:31) [3]
    а зачем используется такой суровый и доисторический способ ?
  • Maksim V. © (14.07.11 23:43) [4]
    Ну и ка правильнее? Напишите рабочий код пожалуйста. А то советчиков сейчас понабежит куча а вот так надо нет вот так. Подозреваю что вы имели ввиду оператор while. Код все равно такой же некорректно рабочий.
           while (dwBytesRead > 0) do
           begin

             bRet := InternetReadFile(hResourceHandle, @pByteData[0], dwBytesToWrite, dwBytesRead);
             if (bRet and (dwBytesRead > 0)) then
             begin

               pszText := Copy(LPCSTR(pByteData), 0, dwBytesRead);
               Result := Result + Utf8ToAnsi(pszText);

             end;

           end;

  • DVM © (15.07.11 00:18) [5]

    > Maksim V. ©


    > Да тут же есть repeat / until чем не цикл?

    не заметил, сорри


    >            pszText := Copy(LPCSTR(pByteData), 0, dwBytesRead);
    >
    >            Result := Result + Utf8ToAnsi(pszText);

    странная конструкция какая то имхо
  • DVM © (15.07.11 00:31) [6]

    > Maksim V. ©

    SOAPHTTPTrans.PAS из поставки delphi тебя не устроит? Там все то же что ты делаешь есть.

    вот в

    procedure  THTTPReqResp.Receive(Context: Integer; Resp: TStream; IsGet: Boolean);

    аналог твоего кода
  • Плохиш © (15.07.11 09:33) [7]

    > SOAP запрос через WinInet [D7, WinXP]

    В D7 есть встроенное средство для создания таких клиентов.
  • Maksim V. © (16.07.11 17:54) [8]
    У меня на одном апи приложение, мне не нужны всякие реализации на всл.
    DVM
    Я смотрел уже код, там то же самое примерно.
    Плохиш
    Опять же ворох всл барахла.

    Переделал код от Rouse_ на свой лад.

     Buff, IntermediateBuffer: Array of Byte;
     ContentLength, BytesRead: DWORD;
         SetLength(IntermediateBuffer, 8192);
         ContentLength := 0;
         BytesRead := 0;

         repeat
           InternetReadFile(hResourceHandle, @IntermediateBuffer[0], 1024, BytesRead);
           if (BytesRead > 0) then
               begin
                 SetLength(Buff, ContentLength + BytesRead);
                 Move(IntermediateBuffer[0], Buff[ContentLength], BytesRead);
                 Inc(ContentLength, BytesRead);
                 Result := Result + Utf8ToAnsi(Utf8String(Buff));
               end
         until
           BytesRead = 0;



    В общем такая же хрень выходит, загружается столько же не до конца.

    Изначально вообще был такой код.

    function SendSoapRequest(hResourceHandle: HINTERNET; pszSoap: Utf8String): AnsiString;
    var
     dwStatus      : DWORD;
     dwStatusSize  : DWORD;
     dwReserved    : DWORD;
     bRet          : Boolean;
     pszText       : Utf8String;
     dwBytesToWrite: DWORD;
     dwBytesRead   : DWORD;
    begin
     Result := '';
     bRet := HttpSendRequest(hResourceHandle, nil, 0, LPTSTR(pszSoap), lstrlen(LPTSTR(pszSoap)));
     if bRet then
     begin
       dwStatus := 0;
       dwStatusSize := SizeOf(dwStatus);
       dwReserved := 0;
       bRet := HttpQueryInfo(
         hResourceHandle,
         HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE,
         @dwStatus,
         dwStatusSize,
         dwReserved
       );
       if (bRet and (dwStatus = HTTP_STATUS_OK)) then
       begin
         bRet := InternetQueryDataAvailable(hResourceHandle, dwBytesToWrite, 0, 0);
         if (bRet and (dwBytesToWrite > 0)) then
         begin
           SetLength(pszText, dwBytesToWrite);
           repeat
             bRet := InternetReadFile(hResourceHandle, LPTSTR(pszText), dwBytesToWrite, dwBytesRead);
             if (bRet and (dwBytesRead > 0)) then
             begin
               SetLength(pszText, dwBytesRead);
               Result := Result + Utf8ToAnsi(pszText);
             end;
           until
             dwBytesRead = 0;
         end;
       end;
     end;
    end;



    Так как делаю приложение под Windows 7, то проблем не было. Решил протестировать на виртуальных машинах с предыдущими Windows вплоть до 2000. Бах и программа вылетает с ошибклй что где-то память не может быть записана по такому-то адресу. Я подумал ну ладно хрен с ним мне главное добиться рабочести кода на семерке чем на этих, а дальше можно и улучшить после доработки. Компилирую уже на Windows XP x64 и такая же хрень. Вообще брал примерный код из другого своего проекта где все работает как часы. Запускаю под отладчиком и ошибка. Решил пересобрать проект с отладочными DCU файлами. Запускаю и вылезает вкладка GETMEM.INC с процедурой DeleteFree на строке n.prev := p; это получается косяк в системом модуле Delphi? Код отрабатывает только под Windows 7 а в других системах нарушение доступа. На другом форуме мне сказали что затирается нулевой символ в строке, где указывается ее длина при выполнении SetLength, но так и не смогли помочь.
  • DVM © (16.07.11 18:08) [9]

    > Maksim V. ©   (16.07.11 17:54) [8]


    > В общем такая же хрень выходит, загружается столько же не
    > до конца.

    Ты этот вывод делаешь на основе чего? На основе содержимого строки или на основе суммы считанных байт?
  • Maksim V. © (16.07.11 19:29) [10]

    > Ты этот вывод делаешь на основе чего? На основе содержимого
    > строки или на основе суммы считанных байт?

    На основе содержимого строки. Ну завел DWORD переменную, делаю инкремент вслучае bRet and (dwBytesRead > 0) от InternetReadFile. Значит ситуация следующая.

    Под XP x64 считано 9397 байт длина строки 9337 байт
    Под 7 x64 считано 9659 байт длина строки 9580 байт

    Под 7 все четко до конца. Как код переделать? В каком направлении копать?
  • DVM © (16.07.11 19:49) [11]

    > Maksim V. ©   (16.07.11 19:29) [10]

    9397 байт и 9659 байт - это реально считано? Это все данные? Это совпадает со значением Content-Length в заголовке ответа сервера (посмотреть заголовок можно с пом WireShark например)?


    > длина строки 9337 байт


    > длина строки 9580 байт

    Как ты вычислил длину строки UTF8 в байтах под D7? Дело в том, что в UTF8 каждый символ может занимать переменной число байт.

    Я к чему клоню. Мне с самого начала не понравилось, как ты небрежно обращаешься со строками. Приведения вот такого вида Utf8String(Buff) мне очень не нравятся.
  • Maksim V. © (16.07.11 20:28) [12]

    > DVM ©   (16.07.11 19:49) [11]


    Да я просто уже все перепробовал.
    Код такой:

    function SendSoapRequest(hResourceHandle: HINTERNET; pszSoap: Utf8String): AnsiString;
    var
     dwStatus      : DWORD;
     dwStatusSize  : DWORD;
     dwReserved    : DWORD;
     bRet          : Boolean;
     dwBytesToWrite: DWORD;
     dwBytesRead   : DWORD;
     pByteData     : TByteArr;
     text: AnsiString;
     size: DWORD;
     read: DWORD;
    begin
     Result := '';
     read := 0;
     size := 0;
     bRet := HttpSendRequest(hResourceHandle, nil, 0, LPTSTR(pszSoap), lstrlen(LPTSTR(pszSoap)));
     if bRet then
     begin
       dwStatus := 0;
       dwStatusSize := SizeOf(DWORD);
       dwReserved := 0;
       bRet := HttpQueryInfo(
         hResourceHandle,
         HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE,
         @dwStatus,
         dwStatusSize,
         dwReserved
       );
       if (bRet and (dwStatus = HTTP_STATUS_OK)) then
       begin

         dwStatusSize := SizeOf(DWORD);
         HttpQueryInfo(
           hResourceHandle,
           HTTP_QUERY_CONTENT_LENGTH or HTTP_QUERY_FLAG_NUMBER,
           @size,
           dwStatusSize,
           dwReserved
         );

         read := 0;
         bRet := InternetQueryDataAvailable(hResourceHandle, dwBytesToWrite, 0, 0);
         if (bRet and (dwBytesToWrite > 0)) then
         begin
           SetLength(pByteData, dwBytesToWrite);
           repeat
             ZeroMemory(pByteData, Length(pByteData));
             bRet := InternetReadFile(hResourceHandle, @pByteData[0], dwBytesToWrite, dwBytesRead);
             if (bRet and (dwBytesRead > 0)) then
             begin
               Inc(read, dwBytesRead);
               text := BytesToStr(pByteData);
               Result := Result + text;
             end;
           until
             dwBytesRead = 0;
         end;
       end;
     end;

     ShowMessage(
       Format('Загружено: %d Размер содержимого %d Размер строки %d', [read, size, Length(Result)])
     );

    end;



    Теперь без преобразования в UTF8 играюсь с ANSI результатом.
    XP x64 = 9397 / 9659 / 9337
    Seven x64 = 9659 / 9659 / 9659

    Охренеть...
  • Maksim V. © (16.07.11 21:31) [13]
    Windows 2000 = 9337 / 9659 / 9337
    XP x86 = 9337 / 9659 / 9397
    Seven x86 = 9337 / 9659 / 9397
  • DVM © (16.07.11 21:54) [14]

    > Maksim V. ©   (16.07.11 21:31) [13]

    9337 - равно Content-Length?
    Если да, то получается проблемы у тебя есть только с W7-64 ?

    Не вижу, что такое size тебя, где вычисляется. С размером строки пока можешь не заморачиваться, давай выясним ты все данные принял или нет, если нет то почему. В строку потом грамотно переведем.
  • Maksim V. © (16.07.11 22:23) [15]

    > DVM ©   (16.07.11 21:54) [14]


    Content-Length здесь 9659. Узнаем через HttpQueryInfo, она возвращает в переменную size значение. Благо сервер отдает эти сведения в заголовок. Нет, получается что без проблем качается файл именно только под х64 семеркой. Во всех остальных системах недокачка происходит. Получается что недокачивается 262 байта.
  • DVM © (16.07.11 23:07) [16]

    > Maksim V. ©   (16.07.11 22:23) [15]


    >  Нет, получается что без проблем качается файл именно только
    > под х64 семеркой.

    Ну уже результат. Так.

    1) Ты смотрел, что за поля в заголовке запроса и ответа под разными системами присутствуют. Они идентичны?

    2) Сервер вообще нормально отдает контент браузерам например, а если через Indy попробовать - полностью загружается?

    3) Давай весь код сюда, выдерни минимально рабочий кусок, я проверю у себя. SOAP сервер в интернет находится? У его адрес какой и какой URL?
  • Maksim V. © (16.07.11 23:46) [17]
    DVM
    Да вроде заголовки одинаковые отдавать должен, мне кажется не в них дело, но проверю.
    Что отдает браузерам не знаю, это биллинговая система провайдера, я просто посмотрел реализацию SOAP запросов, там через сайт ихний с этим сервером взаимодействуют они.
    Да вот с кодом проблема, чтобы получить этот XML файл, необходима авторизация через несколько SOAP функций. Я бы уже давно скинул бы весь код для проверки. Неохото логин и пароль светить. Если только вам на почту как-то. Просто тоже поймите меня правильно.
    Через Инди вообще не пробовал, я всегда стараюсь на одном апи свои программы делать, так что толком с всл и не умею работать.
  • DVM © (17.07.11 00:37) [18]

    > Maksim V. ©   (16.07.11 23:46) [17]

    Не, логин пароль мне не надо присылать.

    Натрави этот код на любой другой URL (необязательно SOAP вообще) он нормально все что надо подгружает на разных системах?

    Я вот еще смотрю на твой код, уж не знаю, почему ты привязался к этому UTF8 (Lazarus что ли используется?), но к примеру, так:

    LPTSTR(pszSoap), lstrlen(LPTSTR(pszSoap))



    Делать не следует.

    Есть же WideCharToMultiByte и MultiByteToWideChar вот ими и надо оперировать иначе, чревато проблемами.

    UTF8String это все же не WideString и не String (хотя в D7 и UTF8String = type string). Оно может работать, а может и нет при такой записи.

    Что за нужда заставила объявлять pszSoap: Utf8String, а не скажем String или WideString на худой конец?

    Это не связано с недокачкой но тем не менее.
  • Maksim V. © (17.07.11 01:50) [19]

    > DVM ©   (17.07.11 00:37) [18]


    UTF8 использовал из-а того, что тело запроса должно быть в этой кодировке. Да и мало ли как объявят в следующей версии Delphi этот строковый тип, совместимость никогда не помешает, вон стринги то стали юникодовые что путает программистов некоторых. Ну и сам текст в приходящем XML файле естественно в этой кодировке содержится, поэтому чтобы без остальных косяков было бы, оставил так, мало ли как карта ляжет. Да и на первый раз для тестирвоания быстренько состряпал всл программу, а она в 7 Delphi неюникодовая, вот и использовал анс аналоги функций. Потом уже планирую в релизе отточенном юникод функции использовать со всякими Utf8Encode/Utf8Decode итд.

    function CreateSoapRequest(func: AnsiString; args, keys: Array of AnsiString): Utf8String;
    var
     dwItem : Integer;
     pszTemp: AnsiString;
     pszArg : AnsiString;
    begin
     Result :=
       '<?xml version=\"1.0\" encoding=\"UTF-8\"?>' + sLineBreak +
       '<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">' + sLineBreak +
       '  <soap12:Body>' + sLineBreak +
       '    <%func% xmlns=\"AIST-CARDS-USER-SERVICE\">' + sLineBreak +
       '      %args%' + sLineBreak +
       '    </%func%>' + sLineBreak +
       '  </soap12:Body>' + sLineBreak +
       '</soap12:Envelope>';
     Result := StringReplace(Result, '%func%', func, [rfReplaceAll, rfIgnoreCase]);
     for dwItem := Low(args) to High(args) do
     begin
       pszTemp := StringReplace('      <%arg%>%key%</%arg%>', '%arg%', args[dwItem], [rfReplaceAll, rfIgnoreCase]);
       pszTemp := StringReplace(pszTemp, '%key%', keys[dwItem], [rfReplaceAll, rfIgnoreCase]);
       pszArg := pszArg + pszTemp + sLineBreak;
     end;
     pszTemp := StringReplace(Result, '%args%', Trim(pszArg), [rfReplaceAll, rfIgnoreCase]);
     Result := AnsiToUtf8(pszTemp);
    end;



    Перед посылкой запросов также указываю в заголовке:

           pszText := 'Content-Type: application/soap+xml; charset=utf-8' + sLineBreak;
           HttpAddRequestHeaders(hResourceHandle, LPTSTR(pszText), lstrlen(LPTSTR(pszText)), HTTP_ADDREQ_FLAG_ADD);



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

           pszSoap := CreateSoapRequest('ФУНКЦИЯ', ['ИМЯ'], [Edit1.Text]);
           pszText := Format('Content-Length: %d', [lstrlen(LPTSTR(pszSoap))]) + sLineBreak;
           HttpAddRequestHeaders(hResourceHandle, LPTSTR(pszText), lstrlen(LPTSTR(pszText)), HTTP_ADDREQ_FLAG_ADD or HTTP_ADDREQ_FLAG_REPLACE);
           pszTemp := SendSoapRequest(hResourceHandle, pszSoap);



    Соответственно в pszTemp имеем результат нашего SOAP запроса и дальше парсим как нужно. =)
 
Конференция "Сети" » SOAP запрос через WinInet [D7, WinXP]
Есть новые Нет новых   [134435   +16][b:0][p:0.005]