不同的 Internet 协议(如 ftp 和 http)使用多个相同的 WinINet 函数来处理 Internet 上的信息。 这些常见函数以一致的方式处理其任务,而不考虑要向其应用的特定协议。 应用程序可以使用这些函数创建用于处理不同协议的任务的常规用途函数(例如读取 ftp 和 http 的文件)。
常见函数处理以下任务:
- 从 Internet 下载资源(InternetReadFile、InternetSetFilePointer、InternetFindNextFile和 InternetQueryDataAvailable)。
 - 设置异步作(InternetSetStatusCallback)。
 - 查看和更改选项(InternetSetOption 和 InternetQueryOption)。
 - 关闭所有类型的 HINTERNET 句柄(InternetCloseHandle)。
 - 在资源上放置和删除锁(InternetLockRequestFile 和 InternetUnlockRequestFile)。
 
使用通用函数
下表列出了 WinINet 函数中包含的常见函数。 常见函数可用于不同类型的 HINTERNET 句柄,也可以在不同类型的会话期间使用。
| 功能 | 描述 | 
|---|---|
| InternetFindNextFile | 继续文件枚举或搜索。 需要 FtpFindFirstFile创建的句柄,或 InternetOpenUrl 函数。 | 
| InternetLockRequestFile | 允许用户对正在使用的文件放置锁。 此函数需要由 FtpOpenFile、HttpOpenRequest或 InternetOpenUrl 函数返回的句柄。 | 
| InternetQueryDataAvailable | 检索可用数据量。 需要由 FtpOpenFile创建的句柄,或 HttpOpenRequest 函数。 | 
| InternetQueryOption | 检索 Internet 选项的设置。 | 
| InternetReadFile | 读取 URL 数据。 需要由 InternetOpenUrl、FtpOpenFile或 HttpOpenRequest 函数创建的句柄。 | 
| InternetSetFilePointer | 设置文件中下一次读取的位置。 需要使用 GET HTTP 谓词通过 InternetOpenUrl(仅限 HTTP URL)创建的句柄或由 HttpOpenRequest 创建的句柄。 | 
| InternetSetOption | 设置 Internet 选项。 | 
| InternetSetStatusCallback | 设置接收状态信息的回调函数。 将回调函数分配给指定的 HINTERNET 句柄及其派生的所有句柄。 | 
| InternetUnlockRequestFile | 使用 InternetLockRequestFile 函数解锁锁定的文件。 | 
读取文件、查找下一个文件、作选项和设置异步作对于支持各种协议和 HINTERNET 句柄类型的函数很常见。
读取文件
InternetReadFile 函数用于从 InternetOpenUrl、FtpOpenFile或 HttpOpenRequest 函数返回的 HINTERNET 句柄下载资源。
InternetReadFile 接受包含缓冲区地址的 void 指针变量,以及指向包含缓冲区长度的变量的指针。 该函数返回缓冲区中的数据和下载到缓冲区中的数据量。
WinINet 函数提供两种下载整个资源的技术:
InternetQueryDataAvailable 采用由 InternetOpenUrl、FtpOpenFile或 HttpOpenRequest(HttpSendRequest 调用后)创建的 HINTERNET 句柄,并返回可用的字节数。 应用程序应分配等于可用字节数的缓冲区,以及终止 null 字符的 1,并将该缓冲区与 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 来下载数据,并在返回零字节读取并成功完成时退出。
以下示例从 Internet 读取资源,并在 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。
若要完成文件搜索,请使用 FtpFindFirstFile返回的 HINTERNET 句柄继续调用 InternetFindNextFile,或 InternetOpenUrl,直到函数失败并显示扩展错误消息 ERROR_NO_MORE_FILES。 若要获取扩展的错误信息,请调用 GetLastError 函数。
以下示例在 lstDirectory 指示的列表框中显示 FTP 目录的内容。 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 函数以同步方式运行。 应用程序可以通过在调用 InternetOpen 函数时设置 INTERNET_FLAG_ASYNC 标志来请求异步作。 针对从从 InternetOpen 返回的句柄派生的句柄发出的所有将来调用都是异步进行的。
异步与同步作的理由是允许单线程应用程序最大程度地利用 CPU,而无需等待网络 I/O 完成。 因此,根据请求,该作可能以同步或异步方式完成。 应用程序应检查返回代码。 如果函数返回 FALSE 或 NULL,并且 GetLastError 返回ERROR_IO_PENDING,则请求已异步发出,并在函数完成时使用INTERNET_STATUS_REQUEST_COMPLETE调用应用程序。
若要开始异步作,应用程序必须在调用 InternetOpen时设置 INTERNET_FLAG_ASYNC 标志。 然后,应用程序必须使用 InternetSetStatusCallback注册有效的回调函数。
为句柄注册回调函数后,该句柄上的所有作都可以生成状态指示,前提是创建句柄时提供的上下文值不是零。 提供零上下文值会强制作同步完成,即使 InternetOpen中指定了 INTERNET_FLAG_ASYNC。
状态指示提供有关网络作进度的应用程序反馈,例如解析主机名、连接到服务器和接收数据。 可以为句柄发出三种特殊用途状态指示:
- 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 句柄
可以使用 InternetCloseHandle 函数关闭所有 HINTERNET 句柄。 客户端应用程序必须关闭派生自 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)。