InternetOpenUrl处于异步模式

时间:2015-04-19 20:12:29

标签: c++ asynchronous callback msdn wininet

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

我怎么能等到InternetOpenUrl完成所以返回的句柄有效?如果我运行InternetOpenUrl异步,由于可能的重定向,我无法告知我何时收到最后一个INTERNET_STATUS_RESPONSE_RECEIVED。同样,当InternetOpenUrl完成后,我想用INTERNET_OPTION_URL标记InternetQueryOption,以获取所有重定向后的最终网址(如果有)。

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;
    }
}

由于ERROR_INTERNET_INCORRECT_HANDLE_STATE尚未完成,回调内部的某些WinINet函数也会因InternetOpenUrl而失败。虽然InternetQueryOption确实会在每个INTERNET_STATUS_RESPONSE_RECEIVED显示网址。

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)?

1 个答案:

答案 0 :(得分:0)

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

当您异步运行时,会收到另一个回调状态INTERNET_STATUS_REQUEST_COMPLETE,表示异步操作已完成。当您收到此消息时,可以安全地以递归方式拨打InternetReadFile,因为当InternetReadFile完成后,您将收到另一个INTERNET_STATUS_REQUEST_COMPLETE,然后又会拨打InternetReadFile再次dwResult。您执行此操作直到INTERNET_ASYNC_RESULT的{​​{1}}为假。

如果有大量缓冲数据,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;
    }
}