Динамически распределять память для хранения исходного кода HTML, загруженного с помощью WinHttpReadData

0

Прежде всего, строка не может быть использована, это требование.

Я пытаюсь реализовать 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. Извините, если это немного грязно/неопределенно, я попытался объяснить это как можно лучше. Что я делаю не так? Застряли на этом весь день, любая помощь высоко ценится.

Спасибо, парни.

  • 0
    Я всегда с подозрением отношусь к «требованиям», таким как «строка не может быть использована». Это достаточная причина, чтобы закрыть любые отчеты об ошибках, связанных с памятью, как «By Design».
Теги:
memory-management
winhttp

3 ответа

1
Лучший ответ

Вы должны вызвать 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;
  • 0
    Так что про, это работало как очарование, теперь я лучше понимаю распределение памяти. Я также обнаружил еще одну раздражающую вещь, которая привела меня к созданию этого поста (возможно, скрытое благословение). Первоначально я использовал OutputDebugString () для вывода http_get.ucResponse, этот вывод был усечен, я думал, что это ошибка в моем коде, что привело меня к созданию этого поста. Я заметил, что даже с вашим кодом он делал это. Поэтому я записал содержимое http_get.ucResponse в файл, boom, все данные были там. Таким образом, OutputDebugString усекает http_get.ucResponse. Спасибо вам большое!
  • 0
    Кроме того, мне любопытно, почему вы использовали u_char вместо char?
Показать ещё 6 комментариев
1

Вы должны вызвать "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.

Примечание. Это не предназначено для примера рабочего кода и может содержать ошибки или даже синтаксические ошибки (я не тестировал и даже не скомпилировал его). Он предназначен только для того, чтобы показать основную структуру цикла, чтобы выполнить то, что пытается выполнить ассер.

  • 0
    Спасибо, что нашли время, чтобы ответить и помочь мне. Я знал, что должен был зацикливаться, пока не осталось никаких данных, это было отчасти очевидно. Разве не плохо использовать malloc / realloc в c ++ (я думаю, тот же аргумент может применяться к неиспользованию строки)? Голосование за усилия.
  • 0
    Извините, я предполагал C, а не C ++ (не заметил тега c ++ в вашем вопросе). Да, в C ++ используйте new и delete вместо malloc () и free ().
Показать ещё 1 комментарий
1

Даже код, который вы считаете правильным, не соответствует действительности. Вы предполагаете, что szOutBuffer имеет нулевое завершение. Прочтите документацию: параметр &dwDownloaded существует по какой-либо причине.

В "неправильном" коде у вас, конечно, такая же ошибка. Кроме того, вы протекаете старый szResponse (именно потому, что вы не используете класс строк).

Затем вы делаете ситуацию хуже, перемещая строковые биты в некотором совершенно неправильном порядке. Кажется, вы добавите старый ответ на пустую строку szTemp (почему? Почему?), Скопируйте его обратно во вновь выделенных szResponse, а затем добавить ( до сих пор неправильно размера) szOutBuffer.

Наконец, вы пишете \0 вне szResponse[]

Проблема стиля: вы ошибочно strlen что strlen является бесплатным или по крайней мере O (1).

Этот код является примером учебника, почему люди должны использовать std::string. Я бы настоятельно советовал вам НЕ исправить это. Переписывание с использованием строк является единственным разумным действием.

  • 0
    Я бы не сказал, что перезапись с использованием std :: string - единственное разумное действие. Люди работали со строками в C задолго до того, как C ++ и std :: string даже существовали ...
  • 0
    Спасибо за совет и нашли время, чтобы ответить. Вся идея этого упражнения состояла в том, чтобы я научился трудным образом распределять память / строки, как только я это понимаю, я могу использовать строку. Я чувствую, что эта строка добавляет вздутие живота. Голосование за усилия.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню