我有一个使用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_RESPONSE_RECEIVED
何时到达(HTTP状态200)?
答案 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;
}
}