InternetOpenUrl处于异步模式

InternetOpenUrl in asynchronous mode

本文关键字:异步 模式 于异步 InternetOpenUrl      更新时间:2023-10-16

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

  • InternetOpen
  • Internet设置状态回调
  • InternetOpenUrl(同步)
  • InternetReadFileEx(异步)
  • InternetReadFileEx在回调函数中,直到接收到的缓冲区为0

如何等待InternetOpenUrl完成,以便返回的句柄有效?如果我运行InternetOpenUrl异步,由于可能的重定向,我无法判断何时收到最后一个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失败。尽管CCD_ 9确实在每个CCD_。

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

  • INTERNET _STATUS_HANDLE_CREATED
  • INTERNET_STATUS_DETECTING_PROXY
  • INTERNET_STATUS_SENDING_REQUEST
  • INTERNET_STATUS_REQUEstrongENT
  • 互联网状态接收响应
  • 互联网状态响应接收
  • INTERNET_STATUS_REDIRECT
  • INTERNET_STATUS_DETECTING_PROXY
  • INTERNET状态解析名称
  • INTERNET状态名称已解决
  • INTERNET_STATUS_CONNECTING_TO_SERVER
  • INTERNET_STATUS_CONNECTED_TO_SERVER
  • INTERNET_STATUS_SENDING_REQUEST
  • INTERNET_STATUS_REQUEstrongENT
  • 互联网状态接收响应
  • 互联网状态响应接收

如何判断最后一个INTERNET_STATUS_RESPONSE_RECEIVED何时到达(HTTP状态200)?

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

异步运行时,还会收到一个回调状态INTERNET_STATUS_REQUEST_COMPLETE,表示异步操作已完成。当您收到此消息时,可以安全地以递归方式调用InternetReadFile,因为当InternetReadFile完成时,您将收到另一个INTERNET_STATUS_REQUEST_COMPLETE,它将再次调用InternetReadFile。执行此操作直到INTERNET_ASYNC_RESULTdwResult为false。

如果有大量缓冲数据,InternetReadFile也可能同步运行,此时您必须再次运行InternetReadFile,直到InternetReadFile之后对GetLastError()的调用不会导致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;
}
}