Windows Internet) (常见函数
不同的 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 | 设置文件中下一次读取的位置。 要求仅) HTTP URL 上由 InternetOpenUrl (创建的句柄,或使用 GET HTTP 谓词的 HttpOpenRequest 创建的句柄。 |
InternetSetOption | 设置 Internet 选项。 |
InternetSetStatusCallback | 设置接收状态信息的回调函数。 将回调函数分配给指定的 HINTERNET 句柄以及派生自它的所有句柄。 |
InternetUnlockRequestFile | 解锁使用 InternetLockRequestFile 函数锁定的文件。 |
读取文件、查找下一个文件、操作选项和设置异步操作是支持各种协议和 HINTERNET 句柄类型的函数的常见功能。
读取文件
InternetReadFile 函数用于从 InternetOpenUrl、FtpOpenFile 或 HttpOpenRequest 函数返回的 HINTERNET 句柄下载资源。
InternetReadFile 接受一个 void 指针变量,该变量包含缓冲区的地址,以及一个指向包含缓冲区长度的变量的指针。 函数返回缓冲区中的数据以及下载到缓冲区中的数据量。
WinINet 函数提供两种技术来下载整个资源:
- InternetQueryDataAvailable 函数。
- InternetReadFile 的返回值。
InternetQueryDataAvailable 在句柄) 调用 HttpSendRequest 后,采用 InternetOpenUrl、FtpOpenFile 或 HttpOpenRequest (创建的 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 指示的编辑框中显示资源。 在 HttpSendRequest) 发送后,InternetOpenUrl、FtpOpenFile 或 HttpOpenRequest (返回了 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 句柄或 InternetOpenUrl 调用 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 句柄
可以使用 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 Services (WinHTTP) 。