Общие функции (Windows Internet)

Различные протоколы Интернета (например, ftp и http) используют несколько одинаковых функций WinINet для обработки информации в Интернете. Эти общие функции выполняют свои задачи согласованно, независимо от конкретного протокола, к которому они применяются. Приложения могут использовать эти функции для создания функций общего назначения, которые обрабатывают задачи по разным протоколам (например, чтение файлов для FTP и HTTP).

Распространенные функции выполняют следующие задачи:

Использование общих функций

В следующей таблице перечислены распространенные функции, включенные в функции WinINet. Общие функции можно использовать в разных типах дескрипторов HINTERNET или во время различных типов сеансов.

Функция Описание
InternetFindNextFile Продолжает перечисление или поиск файлов. Требуется дескриптор, созданный функцией FtpFindFirstFile или InternetOpenUrl .
InternetLockRequestFile Позволяет пользователю заблокировать используемый файл. Для этой функции требуется дескриптор, возвращаемый функцией FtpOpenFile, HttpOpenRequest или InternetOpenUrl .
InternetQueryDataAvailable Извлекает объем доступных данных. Требуется дескриптор, созданный функцией FtpOpenFile или HttpOpenRequest .
InternetQueryOption Извлекает параметр internet.
InternetReadFile Считывает данные URL-адреса. Требуется дескриптор, созданный функцией InternetOpenUrl, FtpOpenFile или HttpOpenRequest .
InternetSetFilePointer Задает позицию для следующего чтения в файле. Требует дескриптора, созданного InternetOpenUrl (только для URL-адреса HTTP), или дескриптора, созданного HttpOpenRequest с помощью HTTP-команды GET.
InternetSetOption Задает параметр Интернета.
InternetSetStatusCallback Задает функцию обратного вызова, которая получает сведения о состоянии. Назначает функцию обратного вызова назначенному дескриптору HINTERNET и всем дескрипторам, производным от него.
InternetUnlockRequestFile Разблокирует файл, заблокированный с помощью функции InternetLockRequestFile .

 

Чтение файлов, поиск следующего файла, управление параметрами и настройка асинхронных операций являются общими для функций, поддерживающих различные протоколы и типы дескрипторов HINTERNET .

Чтение файлов

Функция InternetReadFile используется для скачивания ресурсов из дескриптора HINTERNET , возвращаемого функцией InternetOpenUrl, FtpOpenFile или HttpOpenRequest .

InternetReadFile принимает переменную пустого указателя, содержащую адрес буфера, и указатель на переменную, содержащую длину буфера. Функция возвращает данные в буфере и объем данных, загруженных в буфер.

Функции WinINet предоставляют два метода загрузки всего ресурса:

InternetQueryDataAvailable принимает дескриптор HINTERNET , созданный InternetOpenUrl, FtpOpenFile или HttpOpenRequest (после вызова HttpSendRequest для дескриптора), и возвращает количество доступных байтов. Приложение должно выделить буфер, равный количеству доступных байтов, плюс 1 для завершающего символа NULL , и использовать этот буфер с InternetReadFile. Этот метод не всегда работает, так как InternetQueryDataAvailable проверяет размер файла, указанного в заголовке, а не фактический файл. Сведения в файле заголовка могут быть устаревшими, или файл заголовка может отсутствовать, так как в настоящее время это не требуется по всем стандартам.

В следующем примере считывается содержимое ресурса, доступного дескриптору hResource и отображаемого в поле ввода, обозначенном intCtrlID.

int WINAPI Dumper(HWND hX, int intCtrlID, HINTERNET hResource)
{
    LPTSTR    lpszData;           // buffer for the data
    DWORD     dwSize;             // size of the data available
    DWORD     dwDownloaded;       // size of the downloaded data
    DWORD     dwSizeSum=0;        // size of the data in the text box
    LPTSTR    lpszHolding;        // buffer to merge the text box 
                                  // data and buffer

    // Set the cursor to an hourglass.
    SetCursor(LoadCursor(NULL,IDC_WAIT));

    // This loop handles reading the data.  
    do
    {
        // The call to InternetQueryDataAvailable determines the
        // amount of data available to download.
        if (!InternetQueryDataAvailable(hResource,&dwSize,0,0))
        {
            ErrorOut(hX,GetLastError(),TEXT("InternetReadFile"));
            SetCursor(LoadCursor(NULL,IDC_ARROW));
            return FALSE;
        }
        else
        {    
            // Allocate a buffer of the size returned by
            // InternetQueryDataAvailable.
            lpszData = new TCHAR[dwSize+1];

            // Read the data from the HINTERNET handle.
            if(!InternetReadFile(hResource,(LPVOID)lpszData,
                                 dwSize,&dwDownloaded))
            {
                ErrorOut(hX,GetLastError(),TEXT("InternetReadFile"));
                delete[] lpszData;
                break;
            }
            else
            {
                // Add a null terminator to the end of the 
                // data buffer.
                lpszData[dwDownloaded]='\0';

                // Allocate the holding buffer.
                lpszHolding = new TCHAR[dwSizeSum + dwDownloaded + 1];
                    
                // Check if there has been any data written to 
                // the text box.
                if (dwSizeSum != 0)
                {
                    // Retrieve the data stored in the text 
                    // box, if any.
                    GetDlgItemText(hX,intCtrlID,
                                   (LPTSTR)lpszHolding, 
                                   dwSizeSum);
                         
                    // Add a null terminator at the end of 
                    // the text box data.
                    lpszHolding[dwSizeSum]='\0';
                }
                else
                {
                    // Make the holding buffer an empty string. 
                    lpszHolding[0]='\0';
                }

                size_t cchDest = dwSizeSum + dwDownloaded + 
                                 dwDownloaded + 1;
                LPTSTR pszDestEnd;
                size_t cchRemaining;

                // Add the new data to the holding buffer.
                HRESULT hr = StringCchCatEx(lpszHolding, cchDest, 
                                            lpszData, &pszDestEnd, 
                                            &cchRemaining, 
                                            STRSAFE_NO_TRUNCATION);
                if(SUCCEEDED(hr))
                {
                    // Write the holding buffer to the text box.
                    SetDlgItemText(hX,intCtrlID,(LPTSTR)lpszHolding);

                    // Delete the two buffers.
                    delete[] lpszHolding;
                    delete[] lpszData;

                    // Add the size of the downloaded data to 
                    // the text box data size.
                    dwSizeSum = dwSizeSum + dwDownloaded + 1;

                    // Check the size of the remaining data.  
                    // If it is zero, break.
                    if (dwDownloaded == 0)
                    {
                        break;
                    }                    
                    else
                    {
                        //  Insert error handling code here.
                    }
                }
            }
        }
    }
    while(TRUE);

    // Close the HINTERNET handle.
    InternetCloseHandle(hResource);

    // Set the cursor back to an arrow.
    SetCursor(LoadCursor(NULL,IDC_ARROW));

    // Return.
    return TRUE;
}

InternetReadFile возвращает ноль байтов, прочитанных и успешно завершается после считывания всех доступных данных. Это позволяет приложению использовать InternetReadFile в цикле для загрузки данных и выхода, когда он возвращает ноль байтов, прочитанных и успешно завершается.

В следующем примере ресурс считывается из Интернета и отображается в поле ввода, обозначенном intCtrlID. Дескриптор HINTERNET hInternet был возвращен InternetOpenUrl, FtpOpenFile или HttpOpenRequest (после отправки HttpSendRequest).

int WINAPI Dump(HWND hX, int intCtrlID, HINTERNET hResource)
{
     DWORD dwSize = 0;
     LPTSTR lpszData;
     LPTSTR lpszOutPut;
     LPTSTR lpszHolding = TEXT("");
     int nCounter = 1;
     int nBufferSize = 0;
     DWORD BigSize = 8000;

     // Set the cursor to an hourglass.
     SetCursor(LoadCursor(NULL,IDC_WAIT));

     // Begin the loop that reads the data.
     do
     {
          // Allocate the buffer.
          lpszData =new TCHAR[BigSize+1];

          // Read the data.
          if(!InternetReadFile(hResource,
                              (LPVOID)lpszData,
                              BigSize,&dwSize))
          {
               ErrorOut(hX,GetLastError(),TEXT("InternetReadFile"));
               delete []lpszData;
               break;
          }
          else
          {
               // Add a null terminator to the end of the buffer.
               lpszData[dwSize]='\0';

               // Check if all of the data has been read.  This should
               // never get called on the first time through the loop.
               if (dwSize == 0)
               {
                    // Write the final data to the text box.
                    SetDlgItemText(hX,intCtrlID,lpszHolding);

                    // Delete the existing buffers.
                    delete [] lpszData;
                    delete [] lpszHolding;
                    break;
               }

               // Determine the buffer size to hold the new data and
               // the data already written to the text box (if any).
               nBufferSize = (nCounter*BigSize)+1;

               // Increment the number of buffers read.
               nCounter++;               

               // Allocate the output buffer.
               lpszOutPut = new TCHAR[nBufferSize];

               // Make sure the buffer is not the initial buffer.
               if(nBufferSize != int(BigSize+1))
               {
                    // Copy the data in the holding buffer.
                    StringCchCopy(lpszOutPut,nBufferSize,lpszHolding);
                    // Add error handling code here.

                    // Concatenate the new buffer with the 
                    // output buffer.
                    StringCchCat(lpszOutPut, nBufferSize, lpszData);
                    // Add error handling code here.
     
                    // Delete the holding buffer.
                    delete [] lpszHolding;
               }
               else
               {
                    // Copy the data buffer.
                    StringCchCopy(lpszOutPut, nBufferSize, lpszData);
                    // Add error handling code here.
               }

               // Allocate a holding buffer.
               lpszHolding = new TCHAR[nBufferSize]; 

               // Copy the output buffer into the holding buffer.
               memcpy(lpszHolding,lpszOutPut,nBufferSize);

               // Delete the other buffers.
               delete [] lpszData;
               delete [] lpszOutPut;

          }

     }
     while (TRUE);

     // Close the HINTERNET handle.
     InternetCloseHandle(hResource);

     // Set the cursor back to an arrow.
     SetCursor(LoadCursor(NULL,IDC_ARROW));

     // Return.
     return TRUE;
}

Поиск следующего файла

Функция InternetFindNextFile используется для поиска следующего файла в поиске с помощью параметров поиска и дескриптора HINTERNET из FtpFindFirstFile или InternetOpenUrl.

Чтобы завершить поиск файла, продолжайте вызывать InternetFindNextFile с помощью дескриптора HINTERNET , возвращаемого ftpFindFirstFile, или InternetOpenUrl , пока функция не завершится сбоем с расширенным сообщением об ошибке ERROR_NO_MORE_FILES. Чтобы получить расширенные сведения об ошибке, вызовите функцию GetLastError .

В следующем примере содержимое каталога FTP отображается в списке, указанном lstDirectory. Дескриптор HINTERNET hConnect — это дескриптор, возвращаемый функцией InternetConnect после установки сеанса FTP.

bool WINAPI DisplayDir( HWND hX, 
                        int lstDirectory, 
                        HINTERNET hConnect, 
                        DWORD dwFlag )
{
     WIN32_FIND_DATA pDirInfo;
     HINTERNET hDir;
     TCHAR DirList[MAX_PATH];

     // Set the cursor to an hourglass.
     SetCursor(LoadCursor(NULL,IDC_WAIT));

     // Reset the list box.
     SendDlgItemMessage(hX, lstDirectory,LB_RESETCONTENT,0,0);

     // Find the first file.
     hDir = FtpFindFirstFile (hConnect, TEXT ("*.*"), 
                              &pDirInfo, dwFlag, 0);
     if (!hDir)                                     
     {
          // Check if the error was because there were no files.
          if (GetLastError()  == ERROR_NO_MORE_FILES) 
          {
               // Alert user.
               MessageBox(hX, TEXT("There are no files here!!!"), 
                          TEXT("Display Dir"), MB_OK);

               // Close the HINTERNET handle.
               InternetCloseHandle(hDir);

               // Set the cursor back to an arrow.
               SetCursor(LoadCursor(NULL,IDC_ARROW));

               // Return.
               return TRUE;
          }
          else 
          {
               // Call error handler.
               ErrorOut (hX, GetLastError (), TEXT("FindFirst error: "));

               // Close the HINTERNET handle.
               InternetCloseHandle(hDir);

               // Set the cursor back to an arrow.
               SetCursor(LoadCursor(NULL,IDC_ARROW));

               // Return.
               return FALSE;
          }
     }
     else
     {
          // Write the file name to a string.
          StringCchPrintf(DirList, MAX_PATH, pDirInfo.cFileName);

          // Check the type of file.
          if (pDirInfo.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
          {
               // Add <DIR> to indicate that this is 
               // a directory to the user.
               StringCchCat(DirList, MAX_PATH, TEXT(" <DIR> "));
               // Add error handling code here.
          }
       
          // Add the file name (or directory) to the list box.
          SendDlgItemMessage(hX, lstDirectory, LB_ADDSTRING,
                             0, (LPARAM)DirList);
     }
     do
     {
          // Find the next file.
          if (!InternetFindNextFile (hDir, &pDirInfo))
          {
               // Check if there are no more files left. 
               if ( GetLastError() == ERROR_NO_MORE_FILES ) 
               {
                    // Close the HINTERNET handle.
                    InternetCloseHandle(hDir);

                    // Set the cursor back to an arrow.
                    SetCursor(LoadCursor(NULL,IDC_ARROW));

                    // Return.
                    return TRUE;
               }
               else
               {   
                    // Handle the error.
                    ErrorOut (hX, GetLastError(), 
                              TEXT("InternetFindNextFile"));

                    // Close the HINTERNET handle.
                    InternetCloseHandle(hDir);

                    // Set the cursor back to an arrow.
                    SetCursor(LoadCursor(NULL,IDC_ARROW));

                    // Return.
                    return FALSE;
               }
           }
           else
           {
               // Write the file name to a string.
               StringCchPrintf(DirList, MAX_PATH, pDirInfo.cFileName);

               // Check the type of file.
               if(pDirInfo.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY)
               {
                    // Add <DIR> to indicate that this is a 
                    // directory to the user.
                    StringCchCat(DirList, MAX_PATH, TEXT(" <DIR> "));
                    // Add error handling code here.
               }
     
               // Add the file name (or directory) to the list box.
               SendDlgItemMessage(hX, lstDirectory, LB_ADDSTRING,
                                  0, (LPARAM)DirList);
           }
     }
     while ( TRUE);
     
}

Управление параметрами

InternetSetOption и InternetQueryOption используются для управления параметрами WinINet.

InternetSetOption принимает переменную, указывающую параметр для задания, буфер для хранения параметра и указатель, содержащий адрес переменной, содержащей длину буфера.

InternetQueryOption принимает переменную, указывающую извлекаемую опцию, буфер для хранения параметра и указатель, содержащий адрес переменной, содержащей длину буфера.

Настройка асинхронных операций

По умолчанию функции WinINet работают синхронно. Приложение может запросить асинхронную операцию, установив флаг INTERNET_FLAG_ASYNC в вызове функции InternetOpen . Все будущие вызовы к дескрипторам, производным от дескриптора, возвращаемого из InternetOpen , выполняются асинхронно.

Обоснование асинхронных и синхронных операций заключается в том, чтобы однопоточное приложение максимально эффективно использовать ЦП, не дожидаясь завершения сетевого ввода-вывода. Таким образом, в зависимости от запроса операция может выполняться синхронно или асинхронно. Приложение должно проверка код возврата. Если функция возвращает FALSE или NULL, а GetLastError возвращает ERROR_IO_PENDING, запрос выполняется асинхронно, а приложение вызывается с INTERNET_STATUS_REQUEST_COMPLETE после завершения функции.

Чтобы начать асинхронную работу, приложение должно установить флаг INTERNET_FLAG_ASYNC в вызове InternetOpen. Затем приложение должно зарегистрировать допустимую функцию обратного вызова с помощью InternetSetStatusCallback.

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

Индикаторы состояния дают приложению отзывы о ходе выполнения сетевых операций, таких как разрешение имени узла, подключение к серверу и получение данных. Для дескриптора можно сделать три указания состояния специального назначения:

  • INTERNET_STATUS_HANDLE_CLOSING является последним признаком состояния дескриптора.
  • INTERNET_STATUS_HANDLE_CREATED указывает, когда дескриптор изначально создан.
  • INTERNET_STATUS_REQUEST_COMPLETE указывает, что асинхронная операция завершена.

Приложение должно проверка структуру INTERNET_ASYNC_RESULT, чтобы определить, была ли операция успешной или неудачной после получения INTERNET_STATUS_REQUEST_COMPLETE указания.

В следующем примере показан пример функции обратного вызова и вызова InternetSetStatusCallback для регистрации функции в качестве функции обратного вызова.

void CALLBACK InternetCallback(
    HINTERNET hInternet,
    DWORD_PTR dwcontext,
    DWORD dwInternetStatus,
    LPVOID lpvStatusInformation,
    DWORD dwStatusInformationLength
    )
{
    _tprintf(TEXT("%0xd %0xp %0xd %0xp %0xd\n"),
             hInternet,
             dwcontext,
             dwInternetStatus,
             lpvStatusInformation,
             dwStatusInformationLength);
};

INTERNET_STATUS_CALLBACK dwISC =
    InternetSetStatusCallback(hInternet, InternetCallback); 

Закрытие дескрипторов HINTERNET

Все дескрипторы HINTERNET можно закрыть с помощью функции InternetCloseHandle . Клиентские приложения должны закрыть все дескрипторы HINTERNET, производные от дескриптора HINTERNET , который они пытаются закрыть перед вызовом InternetCloseHandle для дескриптора.

В следующем примере показана иерархия дескрипторов.

HINTERNET hRootHandle, hOpenUrlHandle;

hRootHandle = InternetOpen( TEXT("Example"), 
                            INTERNET_OPEN_TYPE_DIRECT, 
                            NULL, 
                            NULL, 0);

hOpenUrlHandle = InternetOpenUrl(hRootHandle, 
    TEXT("https://www.server.com/default.htm"), NULL, 0, 
    INTERNET_FLAG_RAW_DATA,0);

// Close the handle created by InternetOpenUrl so that the
// InternetOpen handle can be closed.
InternetCloseHandle(hOpenUrlHandle); 

// Close the handle created by InternetOpen.
InternetCloseHandle(hRootHandle);

Блокировка и разблокировка ресурсов

Функция InternetLockRequestFile позволяет приложению гарантировать, что кэшированный ресурс, связанный с переданным ему дескриптором HINTERNET , не исчезает из кэша. Если другая загрузка пытается зафиксировать ресурс с тем же URL-адресом, что и заблокированный файл, кэш избегает удаления файла путем безопасного удаления. После того как приложение вызовет функцию InternetUnlockRequestFile , кэш получает разрешение на удаление файла.

Если установлен флаг INTERNET_FLAG_NO_CACHE_WRITE или INTERNET_FLAG_DONT_CACHE , InternetLockRequestFile создает временный файл с расширением TMP, если дескриптор не подключен к ресурсу HTTPS. Если функция обращается к ресурсу HTTPS и INTERNET_FLAG_NO_CACHE_WRITE (или INTERNET_FLAG_DONT_CACHE), InternetLockRequestFile завершается ошибкой .

Примечание

WinINet не поддерживает реализации сервера. Кроме того, его не следует использовать из службы. Для серверных реализаций или служб используйте службы Microsoft Windows HTTP (WinHTTP).