Windows 網際網路) 通用函式 (

不同的網際網路通訊協定 (,例如 ftp 和 HTTP) 會使用數個相同的 WinINet 函式來處理網際網路上的資訊。 這些通用函式會以一致的方式處理其工作,不論它們套用到的特定通訊協定為何。 應用程式可以使用這些函式來建立一般用途函式,以處理跨不同通訊協定的工作 (,例如讀取 ftp 和 HTTP) 的檔案。

常見的函式會處理下列工作:

使用通用函式

下表列出 WinINet 函式中包含的常見函式。 常見的函式可用於不同類型的 HINTERNET 控制碼,也可以在不同類型的會話期間使用。

函式 描述
InternetFindNextFile 繼續檔案列舉或搜尋。 需要 FtpFindFirstFileInternetOpenUrl 函式所建立的控制碼。
InternetLockRequestFile 允許使用者將鎖定放在正在使用的檔案上。 此函式需要 FtpOpenFileHttpOpenRequestInternetOpenUrl 函式所傳回的控制碼。
InternetQueryDataAvailable 擷取可用的資料量。 需要 FtpOpenFileHttpOpenRequest 函式所建立的控制碼。
InternetQueryOption 擷取網際網路選項的設定。
InternetReadFile 讀取 URL 資料。 需要 InternetOpenUrlFtpOpenFileHttpOpenRequest 函式所建立的控制碼。
InternetSetFilePointer 設定檔案中下一個讀取的位置。 需要 InternetOpenUrl (在 HTTP URL 上建立的控制碼,) 或使用 GET HTTP 動詞命令建立的 HttpOpenRequest 所建立的控制碼。
InternetSetOption 設定網際網路選項。
InternetSetStatusCallback 設定接收狀態資訊的回呼函式。 將回呼函式指派給指定的 HINTERNET 控制碼,以及衍生自它的所有控制碼。
InternetUnlockRequestFile 解除鎖定使用 InternetLockRequestFile 函式鎖定的檔案。

 

讀取檔案、尋找下一個檔案、操作選項,以及設定非同步作業是支援各種通訊協定和 HINTERNET 控制碼類型的函式通用。

讀取檔案

InternetReadFile函式可用來從InternetOpenUrlFtpOpenFileHttpOpenRequest函式所傳回的HINTERNET控制碼下載資源。

InternetReadFile 接受 void 指標變數,其中包含緩衝區的位址,以及包含緩衝區長度之變數的指標。 函式會傳回緩衝區中的資料,以及下載到緩衝區的資料量。

WinINet 函式提供兩種技術來下載整個資源:

InternetQueryDataAvailable會採用InternetOpenUrlFtpOpenFileHttpOpenRequest (在控制碼上呼叫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 來下載資料,並在傳回零個位元組讀取並成功完成時結束。

下列範例會從網際網路讀取資源,並在 intCtrlID 所指示的編輯方塊中顯示資源。 由InternetOpenUrlFtpOpenFileHttpOpenRequest (傳送HttpSendRequest) 之後,會傳回HINTERNET控制碼 hInternet。

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函式可用來尋找檔案搜尋中的下一個檔案,使用FtpFindFirstFileInternetOpenUrl的搜尋參數和HINTERNET控制碼。

若要完成檔案搜尋,請繼續使用FtpFindFirstFile所傳回的HINTERNET控制碼呼叫InternetFindNextFile,直到函式失敗並出現擴充錯誤訊息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);
     
}

操作選項

InternetSetOptionInternetQueryOption 可用來操作 WinINet 選項。

InternetSetOption 接受變數,指出要設定的選項、保留選項設定的緩衝區,以及包含包含緩衝區長度之變數位址的指標。

InternetQueryOption 接受變數,指出要擷取的選項、保留選項設定的緩衝區,以及包含包含緩衝區長度之變數位址的指標。

設定非同步作業

根據預設,WinINet 函式會同步運作。 應用程式可以在對InternetOpen函式的呼叫中設定INTERNET_FLAG_ASYNC旗標,以要求非同步作業。 針對衍生自 InternetOpen 之控制碼的所有未來呼叫,都會以非同步方式進行。

非同步與同步作業的理由是允許單一線程應用程式將 CPU 使用率最大化,而不需要等待網路 I/O 完成。 因此,視要求而定,作業可能會以同步或非同步方式完成。 應用程式應該檢查傳回碼。 如果函式傳回 FALSENull,而 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 控制碼

所有 HINTERNET 控制碼都可以使用 InternetCloseHandle 函式 來關閉。 用戶端應用程式必須在控制碼上呼叫InternetCloseHandle之前,先關閉衍生自HINTERNET控制碼的所有HINTERNET控制碼。

下列範例說明控制碼階層。

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_WRITEINTERNET_FLAG_DONT_CACHE 旗標,除非控制碼已連線到 HTTPs 資源, 否則 InternetLockRequestFile 會建立副檔名為 TMP 的暫存檔。 如果函式存取 HTTPs 資源,且已設定 INTERNET_FLAG_NO_CACHE_WRITE (或 INTERNET_FLAG_DONT_CACHE) , InternetLockRequestFile 就會失敗。

注意

WinINet 不支援伺服器實作。 此外,它不應該從服務使用。 對於伺服器實作或服務,請使用 Microsoft Windows HTTP 服務 (WinHTTP)