Common Functions (Windows Internet)

The different Internet protocols (such as ftp and http) use several of the same WinINet 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 and http).

The common functions handle the following tasks:

Using Common Functions

The following table lists the common functions included in the WinINet functions. The common functions can be used on different types of HINTERNET handles or can be used during different types of sessions.

Function Description
InternetFindNextFile Continues file enumeration or search. Requires a handle created by the FtpFindFirstFile, 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, HttpOpenRequest, or InternetOpenUrl function.
InternetQueryDataAvailable Retrieves the amount of data available. Requires a handle created by the FtpOpenFile, or HttpOpenRequest function.
InternetQueryOption Retrieves the setting of an Internet option.
InternetReadFile Reads URL data. Requires a handle created by the InternetOpenUrl, FtpOpenFile, 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 HTTP verb.
InternetSetOption Sets an Internet option.
InternetSetStatusCallback Sets a callback function that receives 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 the functions that support various protocols and HINTERNET handle types.

Reading Files

The InternetReadFile function is used to download resources from an HINTERNET handle returned by the InternetOpenUrl, FtpOpenFile, or HttpOpenRequest function.

InternetReadFile accepts a void pointer variable that contains the address of a buffer and a pointer to a variable that contains the length of the buffer. The function returns the data in the buffer and the amount of data downloaded into the buffer.

The WinINet functions provide two techniques to download an entire resource:

InternetQueryDataAvailable takes the HINTERNET handle created by InternetOpenUrl, FtpOpenFile, 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, plus 1 for the terminating null character, 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)
{
    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 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, hInternet, was returned by InternetOpenUrl, FtpOpenFile, or HttpOpenRequest (after being sent by 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;
}

Finding the Next File

The InternetFindNextFile function is used to find the next file in a file search, using the search parameters and HINTERNET handle from FtpFindFirstFile, or InternetOpenUrl.

To complete a file search, continue to call InternetFindNextFile using the HINTERNET handle returned by FtpFindFirstFile, 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;
     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);
     
}

Manipulating Options

InternetSetOption and InternetQueryOption are used to manipulate the WinINet options.

InternetSetOption accepts a variable that indicates the option to set, a buffer to hold the option setting, and a pointer that contains the address of the variable that contains the length of the buffer.

InternetQueryOption accepts a variable that indicates the option to retrieve, a buffer to hold the option setting, and a pointer that contains the address of the variable that contains the length of the buffer.

Setting Up Asynchronous Operations

By default, the WinINet 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 are 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 is 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. The application 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 give the application feedback about the progress of 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:

  • INTERNET_STATUS_HANDLE_CLOSING is the last status indication that is made for a handle.
  • INTERNET_STATUS_HANDLE_CREATED indicates when the handle is initially created.
  • INTERNET_STATUS_REQUEST_COMPLETE indicates an asynchronous operation has completed.

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 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); 

Closing HINTERNET Handles

All HINTERNET handles can be closed by using the InternetCloseHandle function. Client applications must close all HINTERNET handles derived from the HINTERNET handle they are trying to close before calling InternetCloseHandle on the handle.

The following example illustrates the handle hierarchy.

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);

Locking and Unlocking Resources

The InternetLockRequestFile function allows an application to ensure that the cached resource associated with the HINTERNET handle passed to it does 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 accesses an https resource and INTERNET_FLAG_NO_CACHE_WRITE (or INTERNET_FLAG_DONT_CACHE) has been set, InternetLockRequestFile fails.

Note

WinINet does not support server implementations. In addition, it should not be used from a service. For server implementations or services use Microsoft Windows HTTP Services (WinHTTP).