【问题标题】:InternetOpenUrl in asynchronous mode异步模式下的 InternetOpenUrl
【发布时间】:2015-06-26 10:26:06
【问题描述】:

我有一个使用 WinINet 函数异步下载文件的函数。我使用的方法是:

我怎样才能等到InternetOpenUrl 完成,以便返回的句柄有效?如果我运行InternetOpenUrl async,由于可能的重定向,我无法判断何时收到最后一个INTERNET_STATUS_RESPONSE_RECEIVED。另外,当InternetOpenUrl 完成后,我想用INTERNET_OPTION_URL 标志调用InternetQueryOption,以获取所有重定向后的最终URL(如果有)。

std::vector<DOWNLOAD_CONTEXT> contexts;

void Download(TCHAR *url, unsigned int crc32, unsigned int length)
{
    HINTERNET hInternet = InternetOpen(_T("Test"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    InternetSetStatusCallback(hInternet, DownloadProgress);
    DOWNLOAD_CONTEXT context;
    context.hInternet = hInternet;
    contexts.push_back(context);
    HINTERNET hUrl = InternetOpenUrl(hInternet, url, _T(""), 0, INTERNET_FLAG_RELOAD, DWORD_PTR(&*(contexts.end()-1)));
    /* if InternetOpenUrl would have been executed in blocking mode,
       here I would have executed the first async InternetReadFileEx,
       and InternetQueryOption to get the final URL*/
}

void CALLBACK DownloadProgress(
    _In_  HINTERNET hInternet,
    _In_  DWORD_PTR dwContext,
    _In_  DWORD dwInternetStatus,
    _In_  LPVOID lpvStatusInformation,
    _In_  DWORD dwStatusInformationLength
    )
{
    DOWNLOAD_CONTEXT *context = (DOWNLOAD_CONTEXT*)dwContext;
    switch (dwInternetStatus)
    {
    case INTERNET_STATUS_HANDLE_CREATED:
        context->hUrl = (HINTERNET)((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult;
        break;

    case INTERNET_STATUS_RESPONSE_RECEIVED:
        size = 0;
        InternetQueryOptionA(context->hUrl, INTERNET_OPTION_URL, nullptr, &size);
        link = new char[size];
        InternetQueryOptionA(context->hUrl, INTERNET_OPTION_URL, link, &size);
        MessageBoxA(NULL, link, "", MB_OK);
        delete[] link;
        /* if this is the last response (the HTTP status code is 200)
           we initiate InternetReadFileEx recursion */
        break;

    ...

    default:
        MessageBoxA(NULL, "Status: Unknown (%d)\n", "", MB_OK);
        break;
    }
}

此外,由于InternetOpenUrl 尚未完成,因此在回调内部,一些 WinINet 函数将因ERROR_INTERNET_INCORRECT_HANDLE_STATE 而失败。虽然 InternetQueryOption 确实在每个 INTERNET_STATUS_RESPONSE_RECEIVED 处显示 URL。

InternetOpenUrl 将生成以下回调状态序列:

  • INTERNET_STATUS_HANDLE_CREATED
  • INTERNET_STATUS_DETECTING_PROXY
  • INTERNET_STATUS_SENDING_REQUEST
  • INTERNET_STATUS_REQUEST_SENT
  • INTERNET_STATUS_RECEIVING_RESPONSE
  • INTERNET_STATUS_RESPONSE_RECEIVED
  • INTERNET_STATUS_REDIRECT
  • INTERNET_STATUS_DETECTING_PROXY
  • INTERNET_STATUS_RESOLVING_NAME
  • INTERNET_STATUS_NAME_RESOLVED
  • INTERNET_STATUS_CONNECTING_TO_SERVER
  • INTERNET_STATUS_CONNECTED_TO_SERVER
  • INTERNET_STATUS_SENDING_REQUEST
  • INTERNET_STATUS_REQUEST_SENT
  • INTERNET_STATUS_RECEIVING_RESPONSE
  • INTERNET_STATUS_RESPONSE_RECEIVED

我如何知道最后一个 INTERNET_STATUS_RESPONSE_RECEIVED 何时到达(HTTP 状态 200)?

【问题讨论】:

    标签: c++ asynchronous callback msdn wininet


    【解决方案1】:

    好的,不需要同步运行InternetOpenUrl。问题是我实际上并没有异步运行,因为我忘记在InternetOpen 中设置INTERNET_FLAG_ASYNC 标志。

    当您异步运行时,还会收到一个回调状态INTERNET_STATUS_REQUEST_COMPLETE,这表明异步操作已完成。当您收到此消息时,可以安全地以递归方式调用InternetReadFile,因为当InternetReadFile 完成后,您将收到另一个INTERNET_STATUS_REQUEST_COMPLETE,而后者又会再次调用InternetReadFile。你这样做,直到INTERNET_ASYNC_RESULT 中的dwResult 为假。

    如果有大量缓冲数据,InternetReadFile 也有可能同步运行,此时您将不得不再次运行InternetReadFile,直到调用GetLastError(),就在InternetReadFile 之后,不会导致ERROR_IO_PENDING。之后你关闭手柄就完成了。

    typedef struct{
        HWND        hDialog;      // Window handle
        HINTERNET   hUrl;    // HINTERNET handle created by InternetOpenUrl
        char        buffer[512];
        DWORD       size; // buffer fill size
        unsigned int crc32;
        unsigned int length; // file length
        unsigned int received; // downloaded bytes so far
    } DOWNLOAD_CONTEXT;
    
    std::vector<DOWNLOAD_CONTEXT> contexts;
    
    void Download(TCHAR *url, unsigned int crc32, unsigned int length)
    {
        HINTERNET hInternet = InternetOpen(_T("Test"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC); // async flag
        InternetSetStatusCallback(hInternet, DownloadProgress);
        DOWNLOAD_CONTEXT context;
        context.hDialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hMainWindow, DownloadDlg);
        context.length = length;
        context.crc32 = crc32;
        context.received = 0;
        context.size = 0;
        contexts.push_back(context);
        HINTERNET hUrl = InternetOpenUrl(hInternet, url, _T(""), 0, INTERNET_FLAG_RELOAD, DWORD_PTR(&*(contexts.end()-1)));
    }
    
    void CALLBACK DownloadProgress(
        _In_  HINTERNET hInternet,
        _In_  DWORD_PTR dwContext,
        _In_  DWORD dwInternetStatus,
        _In_  LPVOID lpvStatusInformation,
        _In_  DWORD dwStatusInformationLength
        )
    {
        DOWNLOAD_CONTEXT *context = (DOWNLOAD_CONTEXT*)dwContext;
        switch (dwInternetStatus)
        {
        case INTERNET_STATUS_HANDLE_CREATED:
            context->hUrl = (HINTERNET)((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult;
            ShowWindow(context->hDialog, SW_SHOW);
            break;
    
        case INTERNET_STATUS_HANDLE_CLOSING:
            // clean up
            break;
    
        case INTERNET_STATUS_RESPONSE_RECEIVED: // don't need to do anything here
            break;
    
        case INTERNET_STATUS_REQUEST_COMPLETE:
            if ((HINTERNET)((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult == context->hUrl)
            {
                DWORD size = 0;
                char *text;
                InternetQueryOptionA(context->hUrl, INTERNET_OPTION_URL, nullptr, &size);
                text = new char[size];
                InternetQueryOptionA(context->hUrl, INTERNET_OPTION_URL, text, &size);
                SetWindowTextA(GetDlgItem(context->hDialog, IDC_EDIT1), text);
                delete[] text;
            }
            if (((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult > 0)
            {
                bool res;
                do
                {
                    context->received += context->size;
                    // proccess first size bytes from buffer
                    res = InternetReadFile(context->hUrl, context->buffer, 512, &context->size);
    
                } while (res && context->size > 0);
                if (GetLastError() != ERROR_IO_PENDING)
                {
                    InternetCloseHandle(hInternet);
                    InternetCloseHandle(context->hUrl);
                }
    
                SetWindowTextA(GetDlgItem(context->hDialog, IDC_STATIC_SIZE), to_string(context->received).c_str());
            }
            else
            {
                InternetCloseHandle(hInternet);
                InternetCloseHandle(context->hUrl);
            }
            break;
    
        ...
    
        default:
            MessageBoxA(NULL, "Status: Unknown (%d)\n", "", MB_OK);
            break;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-02-21
      • 1970-01-01
      • 1970-01-01
      • 2020-10-15
      • 2016-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多