我最近将一个可以正常运行的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 ,而StdOut是使用cout或wcout的宏,具体取决于是否定义了UNICODE。
由于嵌入了null,因此并非所有响应都显示在控制台上。当我关闭can be viewed here调试运行代码时显示的输出(请注意,换行符只是在控制台中包裹文本的位置)。第一个空值恰好在结尾处的“ __in”后面,并恰好在显示“按任意键继续...”输出的位置。这是输出的屏幕截图:
这是html变量值的文本可视化器屏幕截图,它准确显示了空值相对于可见内容的显示位置:
我是在某个地方进行一些错误的复制还是我不知道的WinHTTP的细微差别?
最佳答案
在进一步检查输出时,这些不为空。它们是控制台无法显示的Unicode字符,因为它们存储不正确(因此转换不正确)。我可以通过更改Get方法(和调用代码)解决问题
std::vector<char>
至
std::vector<unsigned char>
现在一切都很好。
关于c++ - WinHTTP是下载空字节还是我错误地复制了结果缓冲区?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8988000/