The different Internet protocols (such as FTP, HTTP, and Gopher) use several of the same Win32® Internet functions to handle information on the Internet. These common functions handle their tasks in a consistent manner, regardless of the particular protocol to which they are being applied. Applications can use these functions to create general-purpose functions that handle tasks across the different protocols (such as reading files for FTP, HTTP, and Gopher protocols).
The common functions handle the following tasks:
The following table lists the common functions included in the Win32 Internet functions. The common functions can be used on different types of HINTERNET handles or can be used during different types of sessions.
InternetFindNextFile | Continues file enumeration or search. Requires a handle created by the FtpFindFirstFile, GopherFindFirstFile, or InternetOpenUrl function. |
InternetLockRequestFile | Allows the user to place a lock on the file that is being used. This function requires a handle returned by the FtpOpenFile, GopherOpenFile, HttpOpenRequest, or InternetOpenUrl function. |
InternetQueryDataAvailable | Queries the amount of data available. Requires a handle created by the FtpOpenFile, GopherOpenFile, or HttpOpenRequest function. |
InternetQueryOption | Queries the setting of an Internet option. |
InternetReadFile | Reads URL data. Requires a handle created by the InternetOpenUrl, FtpOpenFile, GopherOpenFile, or HttpOpenRequest function. |
InternetSetFilePointer | Sets the position for the next read in a file. Requires a handle created by InternetOpenUrl (on an HTTP URL only) or a handle created by HttpOpenRequest using the GET method. |
InternetSetOption | Sets an Internet option. |
InternetSetStatusCallback | Sets a callback function that is called with status information. Assigns a callback function to the designated HINTERNET handle and all handles derived from it. |
InternetUnlockRequestFile | Unlocks a file that was locked using the InternetLockRequestFile function. |
Reading files, finding the next file, manipulating options, and setting up asynchronous operations are common to various protocols and HINTERNET handle types.
The InternetReadFile function is used to download resources from an HINTERNET handle returned by the InternetOpenUrl, FtpOpenFile, GopherOpenFile, or HttpOpenRequest function.
InternetReadFile accepts a void pointer variable that contains the address of a buffer and a pointer to an unsigned long integer (DWORD) variable that contains the length of the buffer. It returns the data in the buffer and the amount of data downloaded into the buffer.
The Win32 Internet functions provide two techniques to download an entire resource:
InternetQueryDataAvailable takes the HINTERNET handle created by InternetOpenUrl, FtpOpenFile, GopherOpenFile, or HttpOpenRequest (after HttpSendRequest has been called on the handle) and returns the number of bytes available. The application should allocate a buffer equal to the number of bytes available +1 for the NULL terminator, and use that buffer with InternetReadFile. This method does not always work because InternetQueryDataAvailable is checking the file size listed in the header and not the actual file. The information in the header file could be outdated or the header file could be missing, since it is not currently required under all standards.
The following example reads the contents of the resource accessed by the hResource handle and displayed in the edit box indicated by intCtrlID.
int WINAPI Dumper(HWND hX, int intCtrlID, HINTERNET hResource) { LPSTR 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 textbox LPSTR lpszHolding; // buffer to merge the textbox 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(),"InternetReadFile"); SetCursor(LoadCursor(NULL,IDC_ARROW)); return FALSE; } else { // Allocate a buffer of the size returned by // InternetQueryDataAvailable lpszData = new char[dwSize+1]; // Read the data from the HINTERNET handle. if(!InternetReadFile(hResource,(LPVOID)lpszData,dwSize,&dwDownloaded)) { ErrorOut(hX,GetLastError(),"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 char[dwSizeSum + dwDownloaded + 1]; // Check if there has been any data written to the textbox. if (dwSizeSum != 0) { // Retrieve the data stored in the textbox if any. GetDlgItemText(hX,intCtrlID,(LPSTR)lpszHolding,dwSizeSum); // Add a null terminator at the end of the textbox data. lpszHolding[dwSizeSum]='\0'; } else { // Make the holding buffer an empty string. lpszHolding[0]='\0'; } // Add the new data to the holding buffer. strcat(lpszHolding,lpszData); // Write the holding buffer to the textbox. SetDlgItemText(hX,intCtrlID,(LPSTR)lpszHolding); // Delete the two buffers. delete[] lpszHolding; delete[] lpszData; // Add the size of the downloaded data to the textbox data size. dwSizeSum = dwSizeSum + dwDownloaded + 1; // Check the size of the remaining data. If it is zero, break. if (dwDownloaded == 0) break; } } } while(TRUE); // Close the HINTERNET handle. InternetCloseHandle(hResource); // Set the cursor back to an arrow. SetCursor(LoadCursor(NULL,IDC_ARROW)); // Return. return TRUE; }
InternetReadFile returns zero bytes read and completes successfully when all available data has been read. This allows an application to use InternetReadFile in a loop to download the data and exit when it returns zero bytes read and completes successfully.
The following example reads the resource from the Internet and displays the resource in the edit box indicated by intCtrlID. The HINTERNET handle, hResource, has been returned by InternetOpenUrl, FtpOpenFile, GopherOpenFile, or HttpOpenRequest (after being sent by HttpSendRequest).
int WINAPI Dump(HWND hX, int intCtrlID, HINTERNET hResource) { DWORD dwSize = 0; LPSTR lpszData; LPSTR lpszOutPut; LPSTR lpszHolding; 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 char[BigSize+1]; // Read the data. if(!InternetReadFile(hResource,(LPVOID)lpszData,BigSize,&dwSize)) { ErrorOut(hX,GetLastError(),"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 textbox. 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 textbox (if any). nBufferSize = (nCounter*BigSize)+1; // Increment the number of buffers read. nCounter++; // Allocate the output buffer. lpszOutPut = new char[nBufferSize]; // Make sure the buffer is not the initial buffer. if(nBufferSize != int(BigSize+1)) { // Copy the data in the holding buffer. strcpy(lpszOutPut,lpszHolding); // Concatenate the new buffer with the output buffer. strcat(lpszOutPut,lpszData); // Delete the holding buffer. delete [] lpszHolding; } else { // Copy the data buffer. strcpy(lpszOutPut, lpszData); } // Allocate a holding buffer. lpszHolding = new char[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; }
The InternetFindNextFile function is used to find the next file in a file search, using the search parameters and HINTERNET handle from FtpFindFirstFile, GopherFindFirstFile, or InternetOpenUrl.
To complete a file search, continue to call InternetFindNextFile using the HINTERNET handle returned by FtpFindFirstFile, GopherFindFirstFile, or InternetOpenUrl until the function fails with the extended error message ERROR_NO_MORE_FILES. To get the extended error information, call the GetLastError function.
The following example displays the contents of an FTP directory in the list box indicated by lstDirectory. The HINTERNET handle, hConnect, is a handle returned by the InternetConnect function after it establishes an FTP session.
bool WINAPI DisplayDir(HWND hX, int lstDirectory, HINTERNET hConnect, DWORD dwFlag) { WIN32_FIND_DATA pDirInfo; HINTERNET hDir; char 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. if ( !(hDir = FtpFindFirstFile (hConnect, TEXT ("*.*"), &pDirInfo, dwFlag, 0) )) { // Check if the error was because there were no files. if (GetLastError() == ERROR_NO_MORE_FILES) { // Alert user. MessageBox(hX,"There are no files here!!!","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 (), "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. sprintf(DirList, 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. strcat(DirList," <DIR> "); } // 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(), "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. sprintf(DirList, 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. strcat(DirList," <DIR> "); } // Add the file name (or directory) to the list box. SendDlgItemMessage(hX,lstDirectory,LB_ADDSTRING,0,(LPARAM)DirList); } } while ( TRUE); }
InternetSetOption and InternetQueryOption are used to manipulate the Win32 Internet API options.
InternetSetOption accepts an unsigned long integer value that indicates the option to set, a buffer to hold the option setting, and a pointer that contains the address of the variable containing the length of the buffer.
InternetQueryOption accepts an unsigned long integer value that indicates the option to query, a buffer to hold the option setting, and a pointer that contains the address of the variable containing the length of the buffer.
By default, the Win32 Internet functions operate synchronously. An application can request asynchronous operation by setting the INTERNET_FLAG_ASYNC flag in the call to the InternetOpen function. All future calls made against handles derived from the handle returned from InternetOpen will be made asynchronously.
The rationale for asynchronous versus synchronous operation is to allow a single-threaded application to maximize its utilization of the CPU without having to wait for network I/O to complete. Therefore, depending on the request, the operation might complete synchronously or asynchronously. The application should check the return code. If a function returns FALSE or NULL, and GetLastError returns ERROR_IO_PENDING, the request has been made asynchronously, and the application will be called back with INTERNET_STATUS_REQUEST_COMPLETE when the function has completed.
To begin asynchronous operation, the application must set the INTERNET_FLAG_ASYNC flag in its call to InternetOpen. It must then register a valid callback function, using InternetSetStatusCallback.
After a callback function is registered for a handle, all operations on that handle can generate status indications, provided that the context value that was supplied when the handle was created was not zero. Providing a zero context value forces an operation to complete synchronously, even though INTERNET_FLAG_ASYNC was specified in InternetOpen.
Status indications are intended to give the application feedback about the operation's progress and are concerned with network operations, such as resolving a host name, connecting to a server, and receiving data. Three special-purpose status indications can be made for a handle:
The application must check the INTERNET_ASYNC_RESULT structure to determine whether the operation succeeded or failed after receiving an INTERNET_STATUS_REQUEST_COMPLETE indication.
The following sample shows an example of a callback function and a call to InternetSetStatusCallback to register the function as the callback function.
void CALLBACK CInternet::InternetCallback( HINTERNET hInternet, DWORD dwcontext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength ) { // Insert code here. }; INTERNET_STATUS_CALLBACK dwISC; dwISC = InternetSetStatusCallback( hInternet, (INTERNET_STATUS_CALLBACK) InternetCallback);
All HINTERNET handles can be closed by using the InternetCloseHandle function. Client applications must close all HINTERNET handles derived from the HINTERNET handle to be closed before calling InternetCloseHandle. For more information about HINTERNET handles and the handle hierarchy, see Appendix A: HINTERNET Handles.
The following example illustrates the handle hierarchy for the Win32 Internet functions.
HINTERNET hRootHandle, hOpenUrlHandle; hRootHandle = InternetOpen("Example", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); hOpenUrlHandle = InternetOpenUrl(hRootHandle, "http://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);
The InternetLockRequestFile function allows an application to ensure that the cached resource associated with the HINTERNET handle passed to it will not disappear from the cache. If another download tries to commit a resource that has the same URL as the locked file, the cache avoids removing the file by doing a safe delete. After the application calls the InternetUnlockRequestFile function, the cache is given permission to delete the file.
If the INTERNET_FLAG_NO_CACHE_WRITE or INTERNET_FLAG_DONT_CACHE flag has been set, InternetLockRequestFile creates a temporary file with the extension TMP, unless the handle is connected to an HTTPS resource. If the function is accessing an HTTPS resource and INTERNET_FLAG_NO_CACHE_WRITE (or INTERNET_FLAG_DONT_CACHE) has been set, InternetLockRequestFile fails.