Конференция "Сети" » 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 запроса и дальше парсим как нужно. =)
  • Anatoly Podgoretsky © (17.07.11 10:45) [20]

    > UTF8 использовал из-а того, что тело запроса должно быть
    > в этой кодировке. Да и мало ли как объявят в следующей версии
    > Delphi этот строковый тип

    А объявят, точнее уже объявили, UTF8String и не просто объявили, но и поддержку сделали.
  • DVM © (17.07.11 11:25) [21]

    > Maksim V. ©   (17.07.11 01:50) [19]


    > UTF8 использовал из-а того, что тело запроса должно быть
    > в этой кодировке.

    Хорошо, пусть так, но смотри что получается у тебя:


    > lstrlen(LPTSTR(pszSoap))


    pszSoap содержать может теоретически как однобайтовые символы так и двухбайтовые, в двухбайтовых не исключено, что один из байтов окажется нулем, что вычислит в этом случае lstrlen ? Неправильно она вычислит. Как дальше пойдет работа программы можно только гадать.
  • han_malign (20.07.11 09:34) [22]

          while( InternetQueryDataAvailable(hResourceHandle, dwBytesToWrite, 0, 0) )do begin
          ......
          end;



    "This function returns the number of bytes of data that are available to be read immediately by a subsequent call to InternetReadFile. If there is currently no data available and the end of the file has not been reached, the request waits until data becomes available. The amount of data remaining will not be recalculated until all available data indicated by the call to InternetQueryDataAvailable is read."


    > как однобайтовые символы так и двухбайтовые, в двухбайтовых
    > не исключено, что один из байтов окажется нулем

    - в UTF8 - к многобайтовой последовательности(и только к ней) относятся только октеты с установленным старшим битом... То есть символ #0 - всегда однобайтовый и терминирующий...

    а вот это
    > Result := Result + Utf8ToAnsi(pszText);

    - криминал, так как если кусок pszText начинается с середины многабайтового символа, или заканчивается незавершенной последовательностью - будет ой...
  • DVM © (20.07.11 22:16) [23]

    > han_malign   (20.07.11 09:34) [22]


    > - в UTF8 - к многобайтовой последовательности(и только к
    > ней) относятся только октеты с установленным старшим битом.
    > .. То есть символ #0 - всегда однобайтовый и терминирующий.
    > ..

    Да, все верно, я уже после как написал, понял, что ноль в средине не встретится именно в UTF8. Но все равно, считаю, что неправильно применять lstrlen такой строке. Неправильно она вычислит длину строки в символах, если там будут двухбайтовые символы. Пока там однобайтовые символы - все работает, но до поры.
  • Плохиш © (21.07.11 00:09) [24]

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

    > Плохиш
    > Опять же ворох всл барахла.

    Меня прикалывают психически неуравновешенные изобретатели велосипедов. Они постоянно пытаются присобачить квадратные колёса.
  • Maksim V. © (22.07.11 01:51) [25]

    > han_malign   (20.07.11 09:34) [22]
    > DVM ©   (20.07.11 22:16) [23]

    Поменял lstrlen на Length, теперь размер содержимого и размер строки совпадает. Так все-таки как цикл закачки составить? Сделать по аналогии как у Rouse_ с двумя массивами, расширяя один из них и копируя в него и под конец накопленные байты перевести в строку? Ну или выделять память под Pointer и через lstrcat докопировать или как вообще, я запутался...


    > Плохиш ©   (21.07.11 00:09) [24]

    По делу есть что сказать?
  • Плохиш © (22.07.11 09:53) [26]

    > Maksim V. ©   (22.07.11 01:51) [25]
    >
    > > Плохиш ©   (21.07.11 00:09) [24]
    >
    > По делу есть что сказать?
    >

    Смотри [7].
  • DVM © (22.07.11 22:59) [27]

    > Maksim V. ©   (22.07.11 01:51) [25]


    > Поменял lstrlen на Length, теперь размер содержимого и размер
    > строки совпадает.

    Я не уверен, что Length для UTF8 возвратит количество символов в строке.


    > Так все-таки как цикл закачки составить? Сделать по аналогии
    > как у Rouse_ с двумя массивами, расширяя один из них и копируя
    > в него и под конец накопленные байты перевести в строку?
    >

    да


    > Ну или выделять память под Pointer и через lstrcat докопировать
    > или как вообще, я запутался...

    нет
  • Maksim V. © (23.07.11 10:21) [28]
    Удалено модератором
 
Конференция "Сети" » SOAP запрос через WinInet [D7, WinXP]
Есть новые Нет новых   [134435   +18][b:0][p:0.005]