动态分配内存,用于存储使用 WinHttpReadData 下载的 HTML 源代码

Dynamically allocate memory for storage of HTML source downloaded with WinHttpReadData

本文关键字:下载 WinHttpReadData HTML 源代码 内存 用于 存储 动态分配      更新时间:2023-10-16

首先,字符串不能使用,这是一个要求。

我正在尝试实现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 WinHttpReadDatan");   
}
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] = '';            
}              
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] = '';               
}              
}           
// 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;
}

我像这样创建结构并调用函数:

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中。抱歉,如果这有点混乱/模糊,我试图尽可能地解释它。我做错了什么? 整天都卡在这个上面,任何帮助都非常感谢。

谢谢大家。

您必须在循环中调用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 WinHttpReadDatan");   
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;

即使是你认为正确的代码,也不是。您假设szOutBuffer以 null 结尾。阅读文档:该&dwDownloaded参数的存在是有原因的。

在"错误"的代码中,您当然有相同的错误。此外,您泄漏了旧szResponse(正是因为您没有使用字符串类)。

然后,通过以某种完全错误的方式移动字符串位,使情况变得更糟。似乎您将旧响应附加到空字符串szTemp(为什么?为什么?),将其复制回新分配的szResponse,然后附加(仍然大小不正确)szOutBuffer

最后,你在szResponse[]之外写一个

样式问题:您错误地假设strlen是免费的或至少是 O(1)。

这段代码是教科书上为什么人们应该使用std::string的例子。我强烈建议您不要修复它。使用字符串重写是唯一合理的操作。

您需要在循环中调用'WinHttpReadData,并将下载的数据内存到另一个缓冲区以保存所有数据,直到您检索到整个响应为止。 每次复制到缓冲区末尾时,请保留指向缓冲区末尾的指针。

像这样的东西(大大简化,只显示循环的基本结构):

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 = '';
// 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.

注意:这不是一个工作代码示例,可能包含错误甚至语法错误(我还没有测试甚至编译它)。 它只是为了展示循环的基本结构,以完成提问者试图完成的任务。