2015-04-19 7 views
2

У меня есть функция, которая асинхронно загружает файл с использованием функций WinINet. Подход я использую:InternetOpenUrl в асинхронном режиме

Как я могу подождать, пока InternetOpenUrl не будет сделано так, чтобы ручка e вернулось? Если я запустил InternetOpenUrl async, я не могу сказать, когда получаю последний INTERNET_STATUS_RESPONSE_RECEIVED из-за возможных перенаправлений. Также, когда сделано InternetOpenUrl, я хочу позвонить InternetQueryOption с флагом INTERNET_OPTION_URL, чтобы получить окончательный 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; 
    } 
} 

Кроме того, некоторые функции Wininet проваливается с ERROR_INTERNET_INCORRECT_HANDLE_STATE, внутри обратного вызова, поскольку InternetOpenUrl еще не сделано. Хотя InternetQueryOption показывает URL-адрес каждые 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_RECE IVED
  • 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 прибыл (статус 200 HTTP)?

ответ

0

Хорошо, нет необходимости запускать InternetOpenUrl синхронно. Проблема заключалась в том, что я не выполнял асинхронно, потому что забыл установить флаг INTERNET_FLAG_ASYNC в InternetOpen.

При запуске асинхронно появляется еще один статус обратного вызова, INTERNET_STATUS_REQUEST_COMPLETE, что указывает на завершение асинхронной операции. Когда вы получите это, можно безопасно вызвать InternetReadFile, потому что, когда закончится InternetReadFile, вы получите еще один INTERNET_STATUS_REQUEST_COMPLETE, который, в свою очередь, снова вызовет InternetReadFile. Вы делаете это, пока dwResult из INTERNET_ASYNC_RESULT не является ложным.

Также возможно, что 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; 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^