使用 WinHTTP 下载 C 中的可执行文件 (.exe)
Downloading an executable (.exe) in C using WinHTTP
我正在尝试使用 WinHTTP 从 C 语言的 HTTP Web 服务器下载可执行文件。下面的代码与 HTML 文件完美配合,但是当我尝试下载可执行文件 (.exe) 时,它只会下载文件的一部分(并且每次下载的字节数都不同 运行 程序)。
代码(灵感来自this示例):
...
LPSTR retVal = (LPSTR) GlobalAlloc(GMEM_FIXED, 20000);
retVal[0] = 0;
LPSTR tempVal = 0;
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPCWSTR accept[2] = { L"application/octet-stream", NULL };
HINTERNET hSession = WinHttpOpen(userAgent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
hConnect = WinHttpConnect(hSession, domain, INTERNET_DEFAULT_HTTP_PORT, 0);
hRequest = WinHttpOpenRequest(hConnect, L "GET", pathL, NULL, WINHTTP_NO_REFERER, accept, NULL);
BOOL work = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
work = WinHttpReceiveResponse(hRequest, NULL);
if (work) {
do {
dwSize = 0;
WinHttpQueryDataAvailable(hRequest, & dwSize);
tempVal = (LPSTR) GlobalAlloc(GMEM_FIXED, dwSize + 1);
ZeroMemory(tempVal, dwSize + 1);
WinHttpReadData(hRequest, (LPVOID) tempVal, dwSize, & dwDownloaded);
StringCchCatA(retVal, 20000, tempVal);
GlobalFree(tempVal);
} while (dwSize > 0);
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return retVal;
这可能是什么原因,我该如何解决?我感谢每一条评论!
不要将 StringCchCatA 与可执行文件等二进制字符串一起使用。您需要使用 memcpy 或类似的东西,目标指向您在每次迭代中构建的缓冲区的当前端。
C 字符串使用空值'\0',二进制0,来标记字符串的结尾。可执行文件与这些一起加载,在整个随机位置。因此,您的可执行文件将连接到缓冲区中的第一个 0 字节,然后与下一个缓冲区一起覆盖 0 字节,依此类推。
所以你需要自己做指针运算来找出目标缓冲区中每个缓冲区需要去的地方,并使用内存副本而不是字符串副本将它放在那里。
StringCchCatA()
对以 null 结尾的字符串进行操作,但 EXE 文件不是文本数据,它是二进制数据,并且 有 0x00
字节在其中,这将导致 StringCchCatA
截断数据。
如果您正在下载的文件超过 20000 字节,当您将缓冲区填满到最大容量时,您将不得不扩大缓冲区。除非您正在下载小文件,否则您通常应该使用固定大小的缓冲区(WinHttpReadData()
文档建议 8KB),并且在每次循环迭代时重复使用该缓冲区并将其附加到临时文件。 WinHttpReadData()
告诉您每次读取后缓冲区中有多少字节。
您也没有检查 WinHttpQueryDataAvailable()
或 WinHttpReadData()
的 return 值是否未能在需要时中断下载循环。
尝试更像这样的东西:
...
DWORD dwFileCap = 20000;
DWORD dwFileSize = 0;
DWORD dwReadSize = 8192;
DWORD dwAvailableSize = 0;
DWORD dwDownloaded = 0;
// TODO: create a file instead...
LPSTR fileData = (LPSTR) GlobalAlloc(GMEM_FIXED, dwFileCap);
if (!fileData)
{
// error handling ...
return NULL;
}
LPSTR readBuffer = (LPSTR) GlobalAlloc(GMEM_FIXED, dwReadSize);
if (!readBuffer)
{
// error handling and cleanup ...
return NULL;
}
LPCWSTR accept[2] = { L"application/octet-stream", NULL };
HINTERNET hSession = WinHttpOpen(userAgent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession)
{
// error handling and cleanup ...
return NULL;
}
hConnect = WinHttpConnect(hSession, domain, INTERNET_DEFAULT_HTTP_PORT, 0);
if (!hConnect)
{
// error handling and cleanup ...
return NULL;
}
hRequest = WinHttpOpenRequest(hConnect, L"GET", pathL, NULL, WINHTTP_NO_REFERER, accept, NULL);
if (!hRequest)
{
// error handling and cleanup ...
return NULL;
}
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
{
// error handling and cleanup ...
return NULL;
}
if (!WinHttpReceiveResponse(hRequest, NULL))
{
// error handling and cleanup ...
return NULL;
}
bool done = false;
do
{
dwAvailableSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwAvailableSize))
{
// error handling and cleanup ...
return NULL;
}
do
{
if (!WinHttpReadData(hRequest, readBuffer, min(dwAvailableSize, dwReadSize), &dwDownloaded))
{
// error handling and cleanup...
return NULL;
}
if (dwDownloaded == 0)
{
done = true;
break;
}
// TODO: if using a file instead, ignore this part ...
if ((dwFileSize + dwDownloaded) > dwFileCap)
{
DWORD newCapacity = double(dwFileCap) * 1.5;
LPSTR newFileData = (LPSTR) GlobalReAlloc((HGLOBAL)fileData, newCapacity, 0);
if (!newFileData)
{
// error handling and cleanup ...
return NULL;
}
fileData = newFileData;
dwFileCap = newCapacity;
}
//
// TODO: if using a file instead, write the bytes to the file here...
memcpy(fileData + dwFileSize, readBuffer, dwDownloaded);
dwFileSize += dwDownloaded;
dwAvailableSize -= dwDownloaded;
}
while (dwAvailableSize > 0);
}
while (!done);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
GlobalFree((HGLOBAL)readBuffer);
// TODO: if using a file instead, close the file here...
// use file data up to dwFileSize bytes as needed ...
// TODO: if using a file instead, ignore this part ...
GlobalFree((HGLOBAL)fileData);
我正在尝试使用 WinHTTP 从 C 语言的 HTTP Web 服务器下载可执行文件。下面的代码与 HTML 文件完美配合,但是当我尝试下载可执行文件 (.exe) 时,它只会下载文件的一部分(并且每次下载的字节数都不同 运行 程序)。
代码(灵感来自this示例):
...
LPSTR retVal = (LPSTR) GlobalAlloc(GMEM_FIXED, 20000);
retVal[0] = 0;
LPSTR tempVal = 0;
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPCWSTR accept[2] = { L"application/octet-stream", NULL };
HINTERNET hSession = WinHttpOpen(userAgent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
hConnect = WinHttpConnect(hSession, domain, INTERNET_DEFAULT_HTTP_PORT, 0);
hRequest = WinHttpOpenRequest(hConnect, L "GET", pathL, NULL, WINHTTP_NO_REFERER, accept, NULL);
BOOL work = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
work = WinHttpReceiveResponse(hRequest, NULL);
if (work) {
do {
dwSize = 0;
WinHttpQueryDataAvailable(hRequest, & dwSize);
tempVal = (LPSTR) GlobalAlloc(GMEM_FIXED, dwSize + 1);
ZeroMemory(tempVal, dwSize + 1);
WinHttpReadData(hRequest, (LPVOID) tempVal, dwSize, & dwDownloaded);
StringCchCatA(retVal, 20000, tempVal);
GlobalFree(tempVal);
} while (dwSize > 0);
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return retVal;
这可能是什么原因,我该如何解决?我感谢每一条评论!
不要将 StringCchCatA 与可执行文件等二进制字符串一起使用。您需要使用 memcpy 或类似的东西,目标指向您在每次迭代中构建的缓冲区的当前端。
C 字符串使用空值'\0',二进制0,来标记字符串的结尾。可执行文件与这些一起加载,在整个随机位置。因此,您的可执行文件将连接到缓冲区中的第一个 0 字节,然后与下一个缓冲区一起覆盖 0 字节,依此类推。
所以你需要自己做指针运算来找出目标缓冲区中每个缓冲区需要去的地方,并使用内存副本而不是字符串副本将它放在那里。
StringCchCatA()
对以 null 结尾的字符串进行操作,但 EXE 文件不是文本数据,它是二进制数据,并且 有 0x00
字节在其中,这将导致 StringCchCatA
截断数据。
如果您正在下载的文件超过 20000 字节,当您将缓冲区填满到最大容量时,您将不得不扩大缓冲区。除非您正在下载小文件,否则您通常应该使用固定大小的缓冲区(WinHttpReadData()
文档建议 8KB),并且在每次循环迭代时重复使用该缓冲区并将其附加到临时文件。 WinHttpReadData()
告诉您每次读取后缓冲区中有多少字节。
您也没有检查 WinHttpQueryDataAvailable()
或 WinHttpReadData()
的 return 值是否未能在需要时中断下载循环。
尝试更像这样的东西:
...
DWORD dwFileCap = 20000;
DWORD dwFileSize = 0;
DWORD dwReadSize = 8192;
DWORD dwAvailableSize = 0;
DWORD dwDownloaded = 0;
// TODO: create a file instead...
LPSTR fileData = (LPSTR) GlobalAlloc(GMEM_FIXED, dwFileCap);
if (!fileData)
{
// error handling ...
return NULL;
}
LPSTR readBuffer = (LPSTR) GlobalAlloc(GMEM_FIXED, dwReadSize);
if (!readBuffer)
{
// error handling and cleanup ...
return NULL;
}
LPCWSTR accept[2] = { L"application/octet-stream", NULL };
HINTERNET hSession = WinHttpOpen(userAgent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession)
{
// error handling and cleanup ...
return NULL;
}
hConnect = WinHttpConnect(hSession, domain, INTERNET_DEFAULT_HTTP_PORT, 0);
if (!hConnect)
{
// error handling and cleanup ...
return NULL;
}
hRequest = WinHttpOpenRequest(hConnect, L"GET", pathL, NULL, WINHTTP_NO_REFERER, accept, NULL);
if (!hRequest)
{
// error handling and cleanup ...
return NULL;
}
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
{
// error handling and cleanup ...
return NULL;
}
if (!WinHttpReceiveResponse(hRequest, NULL))
{
// error handling and cleanup ...
return NULL;
}
bool done = false;
do
{
dwAvailableSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwAvailableSize))
{
// error handling and cleanup ...
return NULL;
}
do
{
if (!WinHttpReadData(hRequest, readBuffer, min(dwAvailableSize, dwReadSize), &dwDownloaded))
{
// error handling and cleanup...
return NULL;
}
if (dwDownloaded == 0)
{
done = true;
break;
}
// TODO: if using a file instead, ignore this part ...
if ((dwFileSize + dwDownloaded) > dwFileCap)
{
DWORD newCapacity = double(dwFileCap) * 1.5;
LPSTR newFileData = (LPSTR) GlobalReAlloc((HGLOBAL)fileData, newCapacity, 0);
if (!newFileData)
{
// error handling and cleanup ...
return NULL;
}
fileData = newFileData;
dwFileCap = newCapacity;
}
//
// TODO: if using a file instead, write the bytes to the file here...
memcpy(fileData + dwFileSize, readBuffer, dwDownloaded);
dwFileSize += dwDownloaded;
dwAvailableSize -= dwDownloaded;
}
while (dwAvailableSize > 0);
}
while (!done);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
GlobalFree((HGLOBAL)readBuffer);
// TODO: if using a file instead, close the file here...
// use file data up to dwFileSize bytes as needed ...
// TODO: if using a file instead, ignore this part ...
GlobalFree((HGLOBAL)fileData);