使用 Wininet 下载二进制文件

发布于 2024-11-28 06:57:49 字数 264 浏览 2 评论 0原文

我目前正在编写一个简单的程序,我想分发给我的朋友。我想要完成的任务是在启动程序时将一些外部二进制文件从互联网写入缓冲区。为此,我使用 Windows Internet(wininet)。目前,我正在使用 InternetReadFile 将文件写入缓冲区,稍后我将在程序中使用该缓冲区。但是,文件没有被完全读取,因为结果大小比服务器上文件的大小小得多,而实际上它应该是相同的。

我想在不使用任何外部库的情况下执行此操作。

知道什么可以解决我的问题吗?

谢谢, 安德鲁

I am currently programming a simple program, I want to distribute to my friends. What I am trying to accomplish, is to write some external binary files to a buffer from the internet, upon starting the program. To do this, I am using windows internet(wininet). Currently, I am using InternetReadFile to write the file to a buffer which I use later in the program. However, the File is not read completely, as in, the resulting size is much smaller than the size of the file on the server, when it should be the same.

I would like to do this, without using any external libraries.

Any idea of what could solve my problem?

Thanks,
Andrew

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

蓝色星空 2024-12-05 06:57:49

文档做出以下评论:

InternetReadFile 的运行方式与基本 ReadFile 函数非常相似,但有一些例外。通常,InternetReadFile 从 HINTERNET 句柄中以顺序字节流的形式检索数据。每次调用 InternetReadFile 时要读取的数据量由 dwNumberOfBytesToRead 参数指定,并且数据在 lpBuffer 参数中返回。正常读取会为每次调用 InternetReadFile 检索指定的 dwNumberOfBytesToRead,直到到达文件末尾。 为了确保检索到所有数据,应用程序必须继续调用 InternetReadFile 函数,直到该函数返回 TRUE 并且 lpdwNumberOfBytesRead 参数等于 0。

基本上,不能保证该函数准确读取 dwNumberOfBytesToRead。使用 lpdwNumberOfBytesRead 参数检查实际读取了多少字节。

此外,一旦总文件大小大于dwNumberOfBytesToRead,您将需要多次调用该调用。因为它无法一次读取超过 dwNumberOfBytesToRead 的内容。

如果您预先知道文件总大小,则循环采用以下形式:

::DWORD error = ERROR_SUCCESS;
::BYTE data[SIZE]; // total file size.
::DWORD size = 0;
::DWORD read = 0;
do {
    ::BOOL result = ::InternetReadFile(stream, data+size, SIZE-size, &read);
    if ( result == FALSE ) {
        error = ::GetLastError();
    }
}
while ((error == ERROR_SUCCESS) && (read > 0) && ((size+=read) < SIZE));
  // check that `SIZE` was correct.
if (size != SIZE) {
}

如果没有,则需要将缓冲区中的数据写入另一个文件,而不是累加它。

编辑(示例测试程序)

这是一个获取 StackOverflow 首页的完整程序。这会以 1K 块的形式下载大约 200K 的 HTML 代码,并检索整个页面。你能运行一下看看它是否有效吗?

#include <Windows.h>
#include <Wininet.h>
#include <iostream>
#include <fstream>

namespace {

    ::HINTERNET netstart ()
    {
        const ::HINTERNET handle =
            ::InternetOpenW(0, INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0);
        if ( handle == 0 )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetOpen(): " << error << "."
                << std::endl;
        }
        return (handle);
    }

    void netclose ( ::HINTERNET object )
    {
        const ::BOOL result = ::InternetCloseHandle(object);
        if ( result == FALSE )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetClose(): " << error << "."
                << std::endl;
        }
    }

    ::HINTERNET netopen ( ::HINTERNET session, ::LPCWSTR url )
    {
        const ::HINTERNET handle =
            ::InternetOpenUrlW(session, url, 0, 0, 0, 0);
        if ( handle == 0 )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetOpenUrl(): " << error << "."
                << std::endl;
        }
        return (handle);
    }

    void netfetch ( ::HINTERNET istream, std::ostream& ostream )
    {
        static const ::DWORD SIZE = 1024;
        ::DWORD error = ERROR_SUCCESS;
        ::BYTE data[SIZE];
        ::DWORD size = 0;
        do {
            ::BOOL result = ::InternetReadFile(istream, data, SIZE, &size);
            if ( result == FALSE )
            {
                error = ::GetLastError();
                std::cerr
                    << "InternetReadFile(): " << error << "."
                    << std::endl;
            }
            ostream.write((const char*)data, size);
        }
        while ((error == ERROR_SUCCESS) && (size > 0));
    }

}

int main ( int, char ** )
{
    const ::WCHAR URL[] = L"http://stackoverflow.com/";
    const ::HINTERNET session = ::netstart();
    if ( session != 0 )
    {
        const ::HINTERNET istream = ::netopen(session, URL);
        if ( istream != 0 )
        {
            std::ofstream ostream("output.txt", std::ios::binary);
            if ( ostream.is_open() ) {
                ::netfetch(istream, ostream);
            }
            else {
                std::cerr << "Could not open 'output.txt'." << std::endl;
            }
            ::netclose(istream);
        }
        ::netclose(session);
    }
}

#pragma comment ( lib, "Wininet.lib" )

The documentation makes the following remarks:

InternetReadFile operates much like the base ReadFile function, with a few exceptions. Typically, InternetReadFile retrieves data from an HINTERNET handle as a sequential stream of bytes. The amount of data to be read for each call to InternetReadFile is specified by the dwNumberOfBytesToRead parameter and the data is returned in the lpBuffer parameter. A normal read retrieves the specified dwNumberOfBytesToRead for each call to InternetReadFile until the end of the file is reached. To ensure all data is retrieved, an application must continue to call the InternetReadFile function until the function returns TRUE and the lpdwNumberOfBytesRead parameter equals zero.

Basically, there is no guarantee that the function to read exactly dwNumberOfBytesToRead. Check out how many bytes were actually read using the lpdwNumberOfBytesRead parameter.

Moreover, as soon as the total file size is larger than dwNumberOfBytesToRead, you will need to invoke the call multiple times. Because it cannot read more than dwNumberOfBytesToRead at once.

If you have the total file size in advance, the loop takes the following form:

::DWORD error = ERROR_SUCCESS;
::BYTE data[SIZE]; // total file size.
::DWORD size = 0;
::DWORD read = 0;
do {
    ::BOOL result = ::InternetReadFile(stream, data+size, SIZE-size, &read);
    if ( result == FALSE ) {
        error = ::GetLastError();
    }
}
while ((error == ERROR_SUCCESS) && (read > 0) && ((size+=read) < SIZE));
  // check that `SIZE` was correct.
if (size != SIZE) {
}

If not, then you need to write the data in the buffer to another file instead of accumulating it.

EDIT (SAMPLE TEST PROGRAM):

Here's a complete program that fetches StackOverflow's front page. This downloads about 200K of HTML code in 1K chunks and the full page is retrieved. Can you run this and see if it works?

#include <Windows.h>
#include <Wininet.h>
#include <iostream>
#include <fstream>

namespace {

    ::HINTERNET netstart ()
    {
        const ::HINTERNET handle =
            ::InternetOpenW(0, INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0);
        if ( handle == 0 )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetOpen(): " << error << "."
                << std::endl;
        }
        return (handle);
    }

    void netclose ( ::HINTERNET object )
    {
        const ::BOOL result = ::InternetCloseHandle(object);
        if ( result == FALSE )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetClose(): " << error << "."
                << std::endl;
        }
    }

    ::HINTERNET netopen ( ::HINTERNET session, ::LPCWSTR url )
    {
        const ::HINTERNET handle =
            ::InternetOpenUrlW(session, url, 0, 0, 0, 0);
        if ( handle == 0 )
        {
            const ::DWORD error = ::GetLastError();
            std::cerr
                << "InternetOpenUrl(): " << error << "."
                << std::endl;
        }
        return (handle);
    }

    void netfetch ( ::HINTERNET istream, std::ostream& ostream )
    {
        static const ::DWORD SIZE = 1024;
        ::DWORD error = ERROR_SUCCESS;
        ::BYTE data[SIZE];
        ::DWORD size = 0;
        do {
            ::BOOL result = ::InternetReadFile(istream, data, SIZE, &size);
            if ( result == FALSE )
            {
                error = ::GetLastError();
                std::cerr
                    << "InternetReadFile(): " << error << "."
                    << std::endl;
            }
            ostream.write((const char*)data, size);
        }
        while ((error == ERROR_SUCCESS) && (size > 0));
    }

}

int main ( int, char ** )
{
    const ::WCHAR URL[] = L"http://stackoverflow.com/";
    const ::HINTERNET session = ::netstart();
    if ( session != 0 )
    {
        const ::HINTERNET istream = ::netopen(session, URL);
        if ( istream != 0 )
        {
            std::ofstream ostream("output.txt", std::ios::binary);
            if ( ostream.is_open() ) {
                ::netfetch(istream, ostream);
            }
            else {
                std::cerr << "Could not open 'output.txt'." << std::endl;
            }
            ::netclose(istream);
        }
        ::netclose(session);
    }
}

#pragma comment ( lib, "Wininet.lib" )
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文