WinHTTP загружает нулевые байты или я неправильно копирую буфер результатов?

Недавно я портировал полностью работающую программу WinInet на WinHTTP. Вот функция, которую я написал, чтобы обернуть весь запрос GET в одну строку кода:

bool Get(Url url, std::vector<char>& data, ProgressCallbackFunction progressCallback = nullptr) throw()
{
    long cl = -1;
    DWORD clSize = sizeof(cl);
    DWORD readCount = 0;
    DWORD totalReadCount = 0;
    DWORD availableBytes = 0;
    std::vector<char> buf;

    if (_session != NULL)
        throw std::exception("Concurrent sessions are not supported");

    _session = ::WinHttpOpen(_userAgent.c_str(), WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, NULL);
    auto connection = ::WinHttpConnect(_session, url.HostName.c_str(), url.Port, 0);
    auto request = ::WinHttpOpenRequest(connection, TEXT("GET"), url.GetPathAndQuery().c_str(), NULL, NULL, NULL, WINHTTP_FLAG_REFRESH);

    if (request == NULL)
    {
        _lastError = ::GetLastError();
        ::WinHttpCloseHandle(_session);
        _session = NULL;
        return false;
    }

    auto sendRequest = ::WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, NULL, WINHTTP_NO_REQUEST_DATA, NULL, NULL, NULL);
    if (sendRequest == FALSE)
    {
        _lastError = ::GetLastError();
        ::WinHttpCloseHandle(request);
        ::WinHttpCloseHandle(_session);
        _session = NULL;
        return false;
    }

    if (::WinHttpReceiveResponse(request, NULL))
    {
        if (progressCallback != nullptr && progressCallback != NULL)
        {
            if (!::WinHttpQueryHeaders(request, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, reinterpret_cast<LPVOID>(&cl), &clSize, 0))
            {
                cl = -1;    
            }
        }

        while (::WinHttpQueryDataAvailable(request, &availableBytes))
        {
            if (availableBytes)
            {
                buf.resize(availableBytes + 1);
                auto hasRead = ::WinHttpReadData(request, &buf[0], availableBytes, &readCount);
                totalReadCount += readCount;
                data.insert(data.end(), buf.begin(), buf.begin() + readCount);
                buf.clear();

                if (progressCallback != nullptr && progressCallback != NULL)
                {
                    progressCallback(totalReadCount, cl, getProgress(totalReadCount, cl));
                }
            }
            else
                break;
        }
    }
    else
    {
        _lastError = ::GetLastError();
        ::WinHttpCloseHandle(request);
        ::WinHttpCloseHandle(_session);
        _session = NULL;
        return false;
    }

    ::WinHttpCloseHandle(request);
    ::WinHttpCloseHandle(_session);
    _session = NULL;
    return true;
}

Код работает в том смысле, что он загружает запрошенный URL. Проблема возникает, когда сервер не возвращает заголовок Content-Length (что происходит в большинстве случаев). Код по-прежнему будет загружать все данные, но при преобразовании в строку будут встроены нулевые байты.

Приведенный выше код называется так:

Url url(TEXT("http://msdn.microsoft.com/en-us/site/aa384376"));
Client wc;
std::vector<char> results;
wc.Get(url, results);
StdString html(results.begin(), results.end());
StdOut << html << endl;

StdString — это typedef std::basic_string‹TCHAR›, а StdOut — это макрос, который использует cout или wcout в зависимости от того, определен ли UNICODE.

Из-за встроенных нулей не весь ответ отображается на консоли. Вывод, отображаемый при запуске кода с отключенной отладкой, можно просмотреть здесь (обратите внимание, что разрывы строк находятся там, где текст завернут в мою консоль). Первый нуль виден сразу после "__in" в самом конце и происходит прямо там, где отображается вывод "Нажмите любую клавишу для продолжения...". Вот скрин вывода:

Консольный вывод

Вот кепка экрана текстового визуализатора значения переменной html, показывающая, где именно появляются нули по отношению к тому, что можно просмотреть:

Визуализатор текста для html

Я где-то плохо копирую или есть какой-то нюанс WinHTTP, о котором я не знаю?


person jvstech    schedule 24.01.2012    source источник
comment
Можете ли вы привести пример, который воспроизводит поведение? Я скопировал ваш код и попробовал эту страницу msdn, и не было встроенных нулей.   -  person Luke    schedule 25.01.2012
comment
Отредактировано для включения следующих ссылок: pastebin.com/aEH9GzDE i.imgur.com/vRmNL.png i.imgur.com/eu8rH.png   -  person jvstech    schedule 25.01.2012


Ответы (1)


При дальнейшем просмотре выходных данных они не являются нулевыми. Это символы Юникода, которые консоль не может отобразить, потому что они хранятся неправильно (и, следовательно, неправильно преобразуются). Мне удалось решить проблему в методе Get (и в вызывающем коде), изменив

std::vector<char>

to

std::vector<unsigned char>

и теперь все хорошо.

person jvstech    schedule 25.01.2012