-
Не удается полностью загрузить 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; Подскажите где ошибка, явно сделал все мыслимые проверки уже где только можно, не знаю где косяк в реализации.
-
я не большой знаток WinInetApi, но думается мне, что InternetReadFile надо вызывать в цикле, пока не будет загружен весь объем.
-
Да тут же есть repeat / until чем не цикл? :(
-
а зачем используется такой суровый и доисторический способ ?
-
Ну и ка правильнее? Напишите рабочий код пожалуйста. А то советчиков сейчас понабежит куча а вот так надо нет вот так. Подозреваю что вы имели ввиду оператор 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;
-
> Maksim V. ©
> Да тут же есть repeat / until чем не цикл?
не заметил, сорри
> pszText := Copy(LPCSTR(pByteData), 0, dwBytesRead); > > Result := Result + Utf8ToAnsi(pszText);
странная конструкция какая то имхо
-
> Maksim V. ©
SOAPHTTPTrans.PAS из поставки delphi тебя не устроит? Там все то же что ты делаешь есть.
вот в
procedure THTTPReqResp.Receive(Context: Integer; Resp: TStream; IsGet: Boolean);
аналог твоего кода
-
> SOAP запрос через WinInet [D7, WinXP]
В D7 есть встроенное средство для создания таких клиентов.
-
У меня на одном апи приложение, мне не нужны всякие реализации на всл. 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, но так и не смогли помочь.
-
> Maksim V. © (16.07.11 17:54) [8]
> В общем такая же хрень выходит, загружается столько же не > до конца.
Ты этот вывод делаешь на основе чего? На основе содержимого строки или на основе суммы считанных байт?
-
> Ты этот вывод делаешь на основе чего? На основе содержимого > строки или на основе суммы считанных байт?
На основе содержимого строки. Ну завел DWORD переменную, делаю инкремент вслучае bRet and (dwBytesRead > 0) от InternetReadFile. Значит ситуация следующая.
Под XP x64 считано 9397 байт длина строки 9337 байт Под 7 x64 считано 9659 байт длина строки 9580 байт
Под 7 все четко до конца. Как код переделать? В каком направлении копать?
-
> Maksim V. © (16.07.11 19:29) [10]
9397 байт и 9659 байт - это реально считано? Это все данные? Это совпадает со значением Content-Length в заголовке ответа сервера (посмотреть заголовок можно с пом WireShark например)?
> длина строки 9337 байт
> длина строки 9580 байт
Как ты вычислил длину строки UTF8 в байтах под D7? Дело в том, что в UTF8 каждый символ может занимать переменной число байт.
Я к чему клоню. Мне с самого начала не понравилось, как ты небрежно обращаешься со строками. Приведения вот такого вида Utf8String(Buff) мне очень не нравятся.
-
> 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 Охренеть...
-
Windows 2000 = 9337 / 9659 / 9337 XP x86 = 9337 / 9659 / 9397 Seven x86 = 9337 / 9659 / 9397
-
> Maksim V. © (16.07.11 21:31) [13]
9337 - равно Content-Length? Если да, то получается проблемы у тебя есть только с W7-64 ?
Не вижу, что такое size тебя, где вычисляется. С размером строки пока можешь не заморачиваться, давай выясним ты все данные принял или нет, если нет то почему. В строку потом грамотно переведем.
-
> DVM © (16.07.11 21:54) [14]
Content-Length здесь 9659. Узнаем через HttpQueryInfo, она возвращает в переменную size значение. Благо сервер отдает эти сведения в заголовок. Нет, получается что без проблем качается файл именно только под х64 семеркой. Во всех остальных системах недокачка происходит. Получается что недокачивается 262 байта.
-
> Maksim V. © (16.07.11 22:23) [15]
> Нет, получается что без проблем качается файл именно только > под х64 семеркой.
Ну уже результат. Так.
1) Ты смотрел, что за поля в заголовке запроса и ответа под разными системами присутствуют. Они идентичны?
2) Сервер вообще нормально отдает контент браузерам например, а если через Indy попробовать - полностью загружается?
3) Давай весь код сюда, выдерни минимально рабочий кусок, я проверю у себя. SOAP сервер в интернет находится? У его адрес какой и какой URL?
-
DVM Да вроде заголовки одинаковые отдавать должен, мне кажется не в них дело, но проверю. Что отдает браузерам не знаю, это биллинговая система провайдера, я просто посмотрел реализацию SOAP запросов, там через сайт ихний с этим сервером взаимодействуют они. Да вот с кодом проблема, чтобы получить этот XML файл, необходима авторизация через несколько SOAP функций. Я бы уже давно скинул бы весь код для проверки. Неохото логин и пароль светить. Если только вам на почту как-то. Просто тоже поймите меня правильно. Через Инди вообще не пробовал, я всегда стараюсь на одном апи свои программы делать, так что толком с всл и не умею работать.
-
> 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 на худой конец? Это не связано с недокачкой но тем не менее.
-
> 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 запроса и дальше парсим как нужно. =)
|