Прежде всего, строка не может быть использована, это требование.
Я пытаюсь реализовать Winhttp для загрузки контента из HTTP. Я использовал пример, предоставленный в MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa384270(v=vs.85).aspx).
Как я уверен, некоторые из вас знают, что WinHttpReadData() считывает данные во временном буфере, записывая существующие данные до тех пор, пока запрос не будет завершен. Это нормально, если вы просто хотите распечатывать буфер каждый раз, однако мне нужно сохранить весь ответ в буфер для использования позже.
Для этого я создал структуру, содержащую все "вещи", которые мне нужно выполнить, я передаю эту структуру ссылкой на функцию, которая выполняет запрос. Структура выглядит следующим образом:
struct HttpG
{
wchar_t* wszUserAgent;
wchar_t* wszCookie;
wchar_t* wszHost;
wchar_t* wszPath;
char* szResponse;
};
Функция, которая выполняет запрос, определяется следующим образом:
int HttpGet(HttpG &http_get);
Все идет нормально....
Теперь проблема возникает, когда я пытаюсь распределить память динамически для http_get.szResponse. Не все данные считываются. Я не буду публиковать весь код примера из MSDN, но я выложу часть кода, которая дает мне эту проблему. Если вы посмотрите на ссылку MSDN выше, вы увидите, какую часть кода я имею в виду. Это основной цикл, который загружает данные.
// Read the Data.
ZeroMemory(szOutBuffer, dwSize + 1);
if(!WinHttpReadData(hRequest, (LPVOID)szOutBuffer, dwSize, &dwDownloaded))
{
OutputDebugStr("Error in WinHttpReadData\n");
}
else
{
// Read data here
if(http_get.szResponse == NULL)
{
// This part seems to work as needed
http_get.szResponse = new char[dwSize + 1];
ZeroMemory(http_get.szResponse, dwSize + 1);
strcpy(http_get.szResponse, szOutBuffer);
http_get.szResponse[dwSize + 1] = '\0';
}
else
{
// Im sure the problems is here, full source
// is not getting put into http_get.szResponse.
// Create temp buffer
szTemp = new char[strlen(http_get.szResponse) + 1];
ZeroMemory(szTemp, strlen(http_get.szResponse) + 1);
strcat(szTemp, http_get.szResponse);
// Resize origonal buffer to hold new data
http_get.szResponse = new char[strlen(szTemp) + dwSize + 1];
ZeroMemory(http_get.szResponse, strlen(szTemp) + dwSize + 1);
strcpy(http_get.szResponse, szTemp);
strcat(http_get.szResponse, szOutBuffer);
http_get.szResponse[strlen(szTemp) + dwSize + 1] = '\0';
}
}
// Free the memory allocated to the buffer.
delete[] szTemp;
delete[] szOutBuffer;
// This condition should never be reached since WinHttpQueryDataAvailable
// reported that there are bits to read.
if(!dwDownloaded)
{
break;
}
Я создаю функцию struct и call следующим образом:
HttpG http_get;
http_get.wszHost = L"au.yahoo.com";
http_get.wszPath = L"/?p=us";
http_get.wszUserAgent = L"Blah blah blah";
http_get.szResponse = NULL;
HttpGet(http_get);
Поэтому по существу в конце запроса я хочу, чтобы все данные находились внутри http_get.szResponse. Извините, если это немного грязно/неопределенно, я попытался объяснить это как можно лучше. Что я делаю не так? Застряли на этом весь день, любая помощь высоко ценится.
Спасибо, парни.
Вы должны вызвать WinHttpReadData()
в цикле, пока не будет больше данных для чтения, и вам необходимо динамически (повторно) распределить буфер ответа на каждой итерации этого цикла. Если требования запрещают вам использовать std::string
они, вероятно, также запретят вам использовать std::vector
, поэтому вам придется прибегнуть к ручному управлению памятью, например:
struct HttpG
{
wchar_t* wszUserAgent;
wchar_t* wszCookie;
wchar_t* wszHost;
wchar_t* wszPath;
u_char* ucResponse;
int ucResponseSize;
};
u_char ucBuffer[1024], *ucTemp;
DWORD dwDownloaded;
do
{
if (!WinHttpReadData(hRequest, ucBuffer, sizeof(ucBuffer), &dwDownloaded))
{
OutputDebugStr("Error in WinHttpReadData\n");
break;
}
if (dwDownloaded == 0)
break;
if (http_get.ucResponse == NULL)
{
http_get.ucResponse = new u_char[dwDownloaded];
memcpy(http_get.ucResponse, ucBuffer, dwDownloaded);
http_get.ucResponseSize = dwDownloaded;
}
else
{
ucTemp = new u_char[http_get.ucResponseSize + dwDownloaded];
memcpy(ucTemp, http_get.ucResponse, http_get.ucResponseSize);
memcpy(&ucTemp[http_get.ucResponseSize], ucBuffer, dwDownloaded);
delete[] http_get.ucResponse;
http_get.ucResponse = ucTemp;
http_get.ucResponseSize += dwDownloaded;
}
}
while (true);
HttpG http_get;
http_get.wszHost = L"au.yahoo.com";
http_get.wszPath = L"/?p=us";
http_get.wszUserAgent = L"Blah blah blah";
http_get.ucResponse = NULL;
http_get.ucResponseSize = 0;
HttpGet(http_get);
// use ucResponse up to ucResponseSize bytes as needed...
delete[] http_get.ucResponse;
Вы должны вызвать "WinHttpReadData" в цикле и memcpy загруженные данные в другой буфер, чтобы сохранить все это, пока вы не получите весь ответ. Храните указатель в конце буфера каждый раз, когда вы копируете его.
Что-то вроде этого (очень упрощенное, показывающее только основную структуру цикла):
char *myBuffer = malloc(bufSize);
char *bufPtr = myBuffer;
int totalBytes = 0;
while (!done)
{
if (WinHttpReadData(hRequest, (LPVOID)outBuffer, dwSize, &dwDownloaded))
{
// if nothing left to download, we're done
if (dwDownloaded == 0)
done = true;
else
{
// Might need to realloc() myBuffer here if you're going to pass the end of it
if (myBuffer + totalBytes + dwDownloaded > bufSize);
myBuffer = (char *)realloc(myBuffer, totalBytes + dwDownloaded);
memcpy(bufPtr, outBuffer, dwDownloaded);
bufPtr += dwDownloaded;
totalBytes += dwDownloaded;
}
}
}
// Null terminate it so you can treat it like a C string.
*bufPtr = '\0';
// Now myBuffer contains the entire downloaded response as a null-terminated string. Do whatever you want with it.
// Don't forget to free(myBuffer) when you're done with it.
Примечание. Это не предназначено для примера рабочего кода и может содержать ошибки или даже синтаксические ошибки (я не тестировал и даже не скомпилировал его). Он предназначен только для того, чтобы показать основную структуру цикла, чтобы выполнить то, что пытается выполнить ассер.
Даже код, который вы считаете правильным, не соответствует действительности. Вы предполагаете, что szOutBuffer
имеет нулевое завершение. Прочтите документацию: параметр &dwDownloaded
существует по какой-либо причине.
В "неправильном" коде у вас, конечно, такая же ошибка. Кроме того, вы протекаете старый szResponse
(именно потому, что вы не используете класс строк).
Затем вы делаете ситуацию хуже, перемещая строковые биты в некотором совершенно неправильном порядке. Кажется, вы добавите старый ответ на пустую строку szTemp
(почему? Почему?), Скопируйте его обратно во вновь выделенных szResponse
, а затем добавить ( до сих пор неправильно размера) szOutBuffer
.
Наконец, вы пишете \0
вне szResponse[]
Проблема стиля: вы ошибочно strlen
что strlen
является бесплатным или по крайней мере O (1).
Этот код является примером учебника, почему люди должны использовать std::string
. Я бы настоятельно советовал вам НЕ исправить это. Переписывание с использованием строк является единственным разумным действием.