WinHTTP загружает нулевые байты или я неправильно копирую буфер результатов? - PullRequest
1 голос
/ 24 января 2012

Я недавно портировал полностью работающую программу 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 is typedef std:: basic_string и StdOut - это макрос, который использует cout или wcout в зависимости от того, определен ли UNICODE.

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

Console output

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

Text visualizer for html

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

1 Ответ

0 голосов
/ 25 января 2012

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

std::vector<char>

до

std::vector<unsigned char>

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

...