Windows 網際網路) 通用函式 (
不同的網際網路通訊協定 (,例如 ftp 和 HTTP) 會使用數個相同的 WinINet 函式來處理網際網路上的資訊。 這些通用函式會以一致的方式處理其工作,不論它們套用到的特定通訊協定為何。 應用程式可以使用這些函式來建立一般用途函式,以處理跨不同通訊協定的工作 (,例如讀取 ftp 和 HTTP) 的檔案。
常見的函式會處理下列工作:
- 從網際網路下載資源 (InternetReadFile、 InternetSetFilePointer、 InternetFindNextFile和 InternetQueryDataAvailable) 。
- (InternetSetStatusCallback) 設定非同步作業。
- (InternetSetOption 和 InternetQueryOption) 檢視和變更選項。
- 關閉所有類型的 HINTERNET 控制碼, (InternetCloseHandle) 。
- (InternetLockRequestFile 和 InternetUnlockRequestFile) ,在資源上放置和移除鎖定。
使用通用函式
下表列出 WinINet 函式中包含的常見函式。 常見的函式可用於不同類型的 HINTERNET 控制碼,也可以在不同類型的會話期間使用。
函式 | 描述 |
---|---|
InternetFindNextFile | 繼續檔案列舉或搜尋。 需要 FtpFindFirstFile或 InternetOpenUrl 函式所建立的控制碼。 |
InternetLockRequestFile | 允許使用者將鎖定放在正在使用的檔案上。 此函式需要 FtpOpenFile、 HttpOpenRequest或 InternetOpenUrl 函式所傳回的控制碼。 |
InternetQueryDataAvailable | 擷取可用的資料量。 需要 FtpOpenFile或 HttpOpenRequest 函式所建立的控制碼。 |
InternetQueryOption | 擷取網際網路選項的設定。 |
InternetReadFile | 讀取 URL 資料。 需要 InternetOpenUrl、 FtpOpenFile或 HttpOpenRequest 函式所建立的控制碼。 |
InternetSetFilePointer | 設定檔案中下一個讀取的位置。 需要 InternetOpenUrl (在 HTTP URL 上建立的控制碼,) 或使用 GET HTTP 動詞命令建立的 HttpOpenRequest 所建立的控制碼。 |
InternetSetOption | 設定網際網路選項。 |
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 來下載資料,並在傳回零個位元組讀取並成功完成時結束。
下列範例會從網際網路讀取資源,並在 intCtrlID 所指示的編輯方塊中顯示資源。 由InternetOpenUrl、FtpOpenFile或HttpOpenRequest (傳送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函式可用來尋找檔案搜尋中的下一個檔案,使用FtpFindFirstFile或InternetOpenUrl的搜尋參數和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);
}
操作選項
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 控制碼
所有 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_WRITE 或 INTERNET_FLAG_DONT_CACHE 旗標,除非控制碼已連線到 HTTPs 資源, 否則 InternetLockRequestFile 會建立副檔名為 TMP 的暫存檔。 如果函式存取 HTTPs 資源,且已設定 INTERNET_FLAG_NO_CACHE_WRITE (或 INTERNET_FLAG_DONT_CACHE) , InternetLockRequestFile 就會失敗。
注意
WinINet 不支援伺服器實作。 此外,它不應該從服務使用。 對於伺服器實作或服務,請使用 Microsoft Windows HTTP 服務 (WinHTTP) 。