2012-01-24 5 views
1

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

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 является ЬурейеЕ станд :: basic_string <TCHAR> и STDOUT макрос, который использует COUT или wcout в зависимости от того, если UNICODE определен.

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

Console output

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

Text visualizer for html

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

+0

Можете ли вы привести пример, который воспроизводит поведение? Я скопировал ваш код и попробовал эту страницу msdn, и не было встроенных нулей. – Luke

+0

Отредактировано для включения следующих ссылок: http://pastebin.com/aEH9GzDE http://i.imgur.com/vRmNL.png http://i.imgur.com/eu8rH.png – jvstech

ответ

0

При последующем рассмотрении выходных данных это не nulls. Они являются символами юникода, которые консоль не может отображать, потому что они некорректно хранятся (и, соответственно, преобразуются неправильно). Я был в состоянии решить эту проблему в Получ (и в коде вызова) путем изменения

std::vector<char> 

в

std::vector<unsigned char> 

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