Windows Internet Overview (WinInet)

As previously mentioned, WinInet is a set of helper API functions that eases the creation of Internet client applications that use the more popular Internet protocols such as HTTP and FTP. What this basically means is that WinInet enables you to write programs over these protocols without having to worry about the underlying socket details. Just for the record, WinInet sits on top of Winsock.

To get a better idea of how WinInet interacts with Winsock, Figure 2.1 shows how it fits into the TCP/IP OSI model described in Chapter 1.

Figure 2.1. WinInet and the TCP/IP OSI layers


You can see that WinInet resides in the session layer of the protocol stack, talking directly with the transport layer to send information. Instead of working with TCP directly and creating sockets to read and write over a particular protocol, your application uses a WinInet session. HTTP and FTP sessions created with WinInet have a special kind of handle type, HINTERNET, which is synchronized and can be shared among multiple threads.

To use the WinInet library with your application, you need to include the header wininet.h, and link with the wininet.lib library.

Handling WinInet Errors

To get the last error from any WinInet API call, you can use the standard system GetLastError() function to return the error code for the last function that failed. More specific error definitions are located in wininet.h if you want to look up more information about an error code. If you require a more "human friendly" description of an error, or extended error information, you can simply call the InternetGetLastResponseInfo() function:

BOOL InternetGetLastResponseInfo(LPDWORD lpdwError,
  LPTSTR lpszBuffer, LPDWORD lpdwBufferLength);

The lpdwError parameter is a pointer to a variable that will contain the error code of the last function. The lpszBuffer and lpdwBufferLength parameters are used to describe the buffer that will contain the error text. When calling InternetGetLastResponseInfo(), set lpdwBufferLength to the size of lpszBuffer. If the buffer is too small, the function will return FALSE, with an error code of ERROR_INSUFFICIENT_BUFFER. The size of the buffer that is required will then be stored in lpdwBufferLength. If the function succeeds, it will return TRUE and the error description will be in the buffer pointed to by lpszBuffer.

If you are using the HTTP protocol over WinInet, Pocket PC also supports displaying a system error dialog box into which a user can enter data (such as a username or password) if an HTTP request fails. Table 2.1 shows the HTTP errors that are returned from GetLastError() by using the InternetErrorDlg() function.

Table 2.1. Errors Supported by InternetErrorDlg()

Error Name

Dialog Box Displayed


Notification that the user is crossing secure/nonsecure zones


User/Password dialog box


Notification of an invalid security certificate authority


Notification about passing data through a nonsecure connection


Notification that the SSL Common Name is invalid


Notification that the SSL certificate has expired

The function is defined as follows:

DWORD InternetErrorDlg(HWND hWnd, HINTERNET hRequest,
  DWORD dwError, DWORD wFlags, LPVOID *lppvData);

The hWnd parameter is the handle to a parent window for the dialog box and can be NULL. The next parameter, hRequest, is a HINTERNET handle to the Internet connection that was used to make the HttpSendRequest() that failed, and dwError is the error code that was returned from the GetLastErrror() function. The wFlags parameter specifies the action that the function should take (and can be one or a combination of the following values):

  • FLAGS_ERROR_UI_FILTER_FOR_ERRORS examines the HTTP header for additional errors.

  • FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS saves the results of the dialog box in the HINTERNET handle that was passed in.

  • FLAGS_ERROR_UI_FLAGS_GENERATE_DATA inspects the hRequest handle for any additional information for the error.

  • FLAGS_ERROR_UI_SERIALIZE_DIALOGS enables the serialization of multiple password requests.

Finally, lppvData contains any additional data that is returned from the error dialog box. Using the FLAGS_ERROR_UI_FLAGS_GENERATE_DATA flag will cause lppvData to be filled with error-specific data, such as invalid certificates, and FLAGS_ERROR_UI_SERIALIZE_DIALOGS will return an INTERNET_AUTH_NOTIFY_DATA structure with user/password data.

The InternetErrorDlg() function's return value will tell you how the user interacted with the dialog box. If the function was successful, it will return ERROR_SUCCESS; if the user decided to cancel the dialog box, an ERROR_CANCELLED result is returned. If the function needs to retry any part of the original request (such as rechecking a password), you would then be returned the value ERROR_INTERNET_FORCE_RETRY.

Internet Connections and Proxies

In order to use WinInet (and Winsock, for that matter), you first need an active Internet connection. Because Pocket PC does not support the desktop InternetAutodial() function, which would automatically establish a connection if you were not online already, you have to use the Pocket PC Connection Manager to establish an Internet connection (see Chapter 7).

Proxy servers are also supported by the WinInet APIs. A proxy server is typically used as both a speed enhancement and a security measure for internal networks. While a proxy prevents your network from unauthorized access, a user/password combination is usually required when making Internet requests over the proxy server. To specify that you want to use a proxy server for a current Internet session, you need to set the INTERNET_OPEN_TYPE_PROXY flag when you use InternetOpen() to start your Internet session. You can then use the InternetSetOption() function (see the section "Internet Options") with the flags INTERNET_OPTION_PROXY_USERNAME and INTERNET_OPTION_PROXY_PASSWORD to set your authorization; otherwise, Pocket PC will attempt to use your default server login information instead. If you receive an error when accessing proxy resources, you can use the InternetErrorDlg() function to have users re-enter their username and password.

Currently, WinInet supports the CERN and SOCKS proxies for HTTP and TIS gateway proxies for FTP requests. You can also use a CERN proxy for FTP requests as long as the request is converted to an HTTP address or made by opening a connection with a URL.

Uniform Resource Locators (URLs)

If you have ever used a Web browser (and I'm sure you have or you would not be reading this), then you have already seen and used Uniform Resource Locators, otherwise known as URLs. A URL is a string that represents the location of a particular resource (a file, a directory, and so on) that is somewhere on the Internet. The accepted syntax for a well-formed URL address is as follows:


For example, a well-formed URL could be something as simple as, or it might appear as something more complicated, such as the following:

If you look at the individual parts of a URL, you can see that the address contains all the information you need to create an application for working with this resource. Normally, if you want to parse the address, you need to perform a great deal of string manipulation for all of the possible combinations.

Fortunately, WinInet provides functions that enable you to create, parse, combine, and convert (also called canonicalize) URL addresses.

Because a URL can contain numerous "parts," both of the functions that create and break up a URL address use the URL_COMPONENTS structure. Before examining the specific WinInet URL functions, let's examine this structure:

typedef struct {
   DWORD dwStructSize;
   LPWSTR lpszScheme;
   DWORD dwSchemeLength;
   LPWSTR lpszHostName;
   DWORD dwHostNameLength;
   LPWSTR lpszUserName;
   DWORD dwUserNameLength;
   LPWSTR lpszPassword;
   DWORD dwPasswordLength;
   LPWSTR lpszUrlPath;
   DWORD dwUrlPathLength;
   LPWSTR lpszExtraInfo;
   DWORD dwExtraInfoLength;

The URL_COMPONENTS structure contains all the individual pieces of a URL:

  • dwStructSize is the size of the URL_COMPONENTS structure.

  • lpszScheme and dwSchemeLength are the address and length, respectively, of the string buffer containing the scheme (or protocol) name. nScheme is the value of an INTERNET_SCHEME enumeration that specifies the protocol scheme.

  • lpszHostName and dwHostNameLength are the address and length, respectively, of the string buffer containing the host address.

  • nPort is the server port.

  • lpszUserName and dwUserNameLength are the address and length, respectively, of the string buffer containing the user name.

  • lpszPassword and dwPasswordLength are the address and length, respectively, of the string buffer containing the password.

  • lpszUrlPath and dwUrlPathLength are the address and length, respectively, of the string buffer containing the URL path.

  • lpszExtraInfo and dwExtraInfoLength are the address and length, respectively, of the string buffer containing any extra URL information, such as additional anchor links (e.g., #anchor).

The nScheme member uses the INTERNET_SCHEME enumerator to determine the appropriate value for the protocol specified in the URL address. The enumerator is defined as follows:

typedef enum {

Now that you have defined all of the components of a URL address, let's take a look at what is required to create and parse this string.

To create a new URL string, you call the following function:

BOOL InternetCreateUrl(LPURL_COMPONENTS lpUrlComponents,
  DWORD dwFlags, LPWSTR lpszUrl, LPDWORD lpdwUrlLength);

The function takes the passed-in pointer to a URL_COMPONENTS structure, the lpUrlComponents parameter, and constructs a new URL that is placed in the string buffer to which lpszUrl points. The dwFlags parameter controls certain aspects of the URL's creation, and can be a combination of the flags found in Table 2.2.

Table 2.2. InternetCreateUrl() Flags




Converts all escape sequences into characters


Uses the default system username

Finally, the lpdwUrlLength parameter contains a pointer to a DWORD value, which is the size of the lpszUrl buffer. If the function successfully returns, lpdwUrlLength will contain the size of the new URL address string; otherwise, it will contain the required size of the buffer if it is too small. Finally, if you don't require a particular component piece, just make that member of URL_COMPONENTS a NULL value.

You can create a URL with the following:

TCHAR tchURL[1024] = TEXT("\0");
DWORD dwLength = 1024;
BOOL fSuccess = FALSE;

// Setup the URL_COMPONENTS structure
memset(&url, 0, sizeof(URL_COMPONENTS));
url.dwStructSize = sizeof(URL_COMPONENTS);
url.lpszScheme = TEXT("http");
url.lpszHostName = TEXT("");
url.nPort = 80;
url.lpszUrlPath = TEXT("index.htm");

// Create the URL
fSuccess = InternetCreateUrl(&url, ICU_ESCAPE, tchURL,

The corresponding output URL (the value of tchURL) from the preceding code would look as follows:

Now that you've seen what's required to assemble a URL, you can use the InternetCrackUrl() function to perform the inverse operation?that is, take a string and parse it into a URL_COMPONENTS structure:

BOOL InternetCrackUrl(LPCWSTR lpszUrl, DWORD dwUrlLength,
  DWORD dwFlags, LPURL_COMPONENTS lpUrlComponents);

When using InternetCrackUrl(), you first need to prepare the URL_COMPONENTS structure that will receive the parts of the string you pass in. Make sure that you initialize the structure's dwStructSize parameter to the size of URL_COMPONENTS, and that you set the corresponding length member value of the part you want to parse to a nonzero value, as shown in the following example:

memset(&urlCracked, 0, sizeof(URL_COMPONENTS));
urlCracked.dwStructSize = sizeof(URL_COMPONENTS);

// Set which parts of URL_COMPONENTS we want to have
// returned
urlCracked.dwHostNameLength = 1;
urlCracked.dwSchemeLength = 1;
urlCracked.dwUrlPathLength = 1;

Once the structure has been set up, you can call InternetCrackUrl(). Set the lpszUrl parameter to the string of the URL, and its length in the dwUrlLength parameter. The dwFlags parameter can be one of the values shown in Table 2.3.

Finally, lpUrlComponents is a pointer to your URL_COMPONENTS structure.

Table 2.3. InternetCrackUrl() Flags




Converts all encoded characters into their normal form


Converts all escape sequences into their corresponding characters

You can crack the previously created URL in the following way:

fSuccess = InternetCrackUrl(tchURL, lstrlen(tchURL)*sizeof(TCHAR),
   0, &urlCracked);

When the function returns, the urlCracked structure will contain all the parts of the passed-in URL parameter.

To combine two separate URL parts, such as a base URL and a relative URL, into one string, you can use the InternetCombineUrl() function:

BOOL InternetCombineUrl(LPCWSTR lpszBaseUrl, LPCWSTR
  lpszRelativeUrl, LPWSTR lpszBuffer, LPDWORD lpdwBufferLength,
  DWORD dwFlags);

The first two parameters are the URLs you want to combine?first the base URL, followed by the relative URL. The lpszBuffer parameter is a pointer to a string buffer that will hold your new URL, and lpdwBufferLength contains a pointer to the buffer size. If the buffer is too small, the required size will be in lpszBufferLength when the function fails. Finally, the dwFlags parameter can be one of the following:

  • ICU_BROWSER_MODE prevents the encoding or decoding of any characters following the # or ? characters.

  • ICU_DECODE coverts all escape sequences to characters.

  • ICU_ENCODE_SPACES_ONLY encodes spaces. Spaces are encoded as %20.

  • ICU_NO_ENCODE prevents the conversion of any unsafe characters.

  • ICU_NO_META prevents the removal of any meta-sequences from your URLs.

If the function succeeds, it will return TRUE.

Finally, to canonicalize a URL (which converts the URL into a "safe" form), you can use the "InternetCanonicalizeUrl" function:

BOOL InternetCanonicalizeUrl(LPCWSTR lpszUrl, LPWSTR lpszBuffer,
   LPDWORD lpdwBufferLength, DWORD dwFlags);

The lpszUrl parameter is the URL you want to convert. The lpszBuffer points to a string buffer that will hold the converted URL and its length, as specified by lpdwBufferLength. If the function fails because the buffer is too small, lpdwBufferLength will point to the length of the required buffer size. The last parameter, dwFlags, is the same as that in the InternetCombineUrl() function.

Internet Cache (Temporary Internet Files)

The temporary Internet cache is a storage area on your device that captures all data received over the network via WinInet transactions. Web pages downloaded with Pocket Internet Explorer, as well as files downloaded via FTP or HTTP, are stored here. When a request is made to download an Internet resource, WinInet first checks the cache to see if it already exists. If it does, then the data is retrieved from the cache instead, enabling an overall faster transfer (it's already on your device), and providing you with the capability to access resources when you are not connected to the network. All WinInet functions will store data (for both FTP and HTTP sessions) in the cache, unless you specify the INTERNET_FLAG_NO_CACHE_WRITE flag when downloading data.

WinInet's APIs enable you to enumerate items in the cache, set cached item data, and delete cache entries. Most of the functions use an INTERNET_CACHE_ENTRY_INFO structure to represent a cached item's information:

   DWORD dwStructSize;
   LPWSTR  lpszSourceUrlName;
   LPWSTR  lpszLocalFileName;
   DWORD CacheEntryType;
   DWORD dwUseCount;
   DWORD dwHitRate;
   DWORD dwSizeLow;
   DWORD dwSizeHigh;
   FILETIME LastModifiedTime;
   FILETIME ExpireTime;
   FILETIME LastAccessTime;
   FILETIME LastSyncTime;
   LPBYTE lpHeaderInfo;
   DWORD dwHeaderInfoSize;
   LPWSTR  lpszFileExtension;
   union {
      DWORD dwReserved;
      DWORD dwExemptDelta;

Table 2.4 describes the members of INTERNET_CACHE_ENTRY_INFO.





Specifies the size of the INTERNET_CACHE_ENTRY_INFO structure.


Specifies the URL name of the file.


Specifies the local filename.


Specifies the type of cache entry. If the file was downloaded from the Internet, the value is 0. If the file is a cookie or history entry, it is a combination of EDITED_CACHE_ENTRY, NORMAL_CACHE_ENTRY, and STICKY_CACHE_ENTRY.


Specifies the user count of the cache entry.


Specifies how many times the cache entry was used.


Specifies the low order of the cache file size.


Specifies the high order of the cache file size.


Specifies when the file was last modified in GMT format.


Specifies when this cache file will expire in GMT format.


Specifies when the cache file was last accessed.


Specifies when the cache file was last synchronized.


Points to a buffer that contains the header information for the cached file.


Specifies the size of the buffer used in lpHeaderInfo.


Points to a buffer that contains the cached file's extension.


Must be 0.


Specifies the exemption time from the last accessed time, in seconds.

For cache entries that are history or cookie files, the CacheEntryType member can be a combination of two values. The EDITED_CACHE_ENTRY value is used for entries that have been changed since the entry was originally downloaded, the NORMAL_CACHE_ENTRY value is for normal entries, and the STICKY_CACHE_ENTRY value is used for entries that are persistent and ignore the dwExemptDelta member.

Finding Out What's in the Cache

Enumerating entries that are in the local Internet cache is similar to the FindFirstFile() and FindNextFile() APIs that are used on local files. To "walk" through the cache and receive an INTERNET_CACHE_ENTRY_INFO structure for each entry, you'll use the FindFirstUrlCacheEntry() and FindNextUrlCacheEntry() functions:

HANDLE FindFirstUrlCacheEntry(LPCWSTR lpszUrlSearchPattern,
   LPDWORD lpdwFirstCacheEntryInfoBufferSize);

BOOL FindNextUrlCacheEntry(HANDLE hEnumHandle,
   LPDWORD lpdwNextCacheEntryInfoBufferSize);

To walk the cache files, you first call FindFirstUrlCacheEntry(). The lpszUrlSearchPattern parameter can be set to NULL (to return all entries), "visited:" (to return only URLs), or "cookie:" (to return only Web site cookie information). Next, the lpFirstCacheEntryInfo entry should point to an initialized INTERNET_CACHE_ENTRY_INFO structure. Make sure you set the structure's dwStructSize member variable to the size of INTERNET_CACHE_ENTRY_INFO before calling the function. The last parameter, lpdwFirstCacheEntryInfoBufferSize, should point to a DWORD that is the size of the lpFirstCacheEntryInfo parameter.

After calling FindFirstUrlCacheEntry(), the function should return a valid handle that you can use to walk through the rest of the cache files by passing it into the first parameter of the FindNextUrlCacheEntry() function. If it fails, you will be returned a NULL value.

To continue enumerating through the Internet cache, you repeatedly call FindNextUrlCacheEntry() to get each cached item until it returns FALSE. Once it does, you can close your enumerator by calling the following:

BOOL FindCloseUrlCache(HANDLE hEnumHandle);

Therefore, to walk through all the cache files, you can simply do the following:

HANDLE hCacheHandle = NULL;
DWORD dwCacheInfoSize = 1024;
BOOL fSuccess = FALSE;

// Create a buffer, which will be of 1024 bytes for the
// INTERNET_CACHE_ENTRY_INFO structure. This is larger than
// the normal size (80 bytes), to make sure it's large
// enough for most cache data.
   return FALSE;

piCacheInfo->dwStructSize = dwCacheInfoSize;
hCacheHandle = FindFirstUrlCacheEntry(NULL, piCacheInfo,

if(!hCacheHandle) {
   if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
      // Under normal circumstances, we would have to reallocate
      // a larger buffer, and try to make the call again.
      return FALSE;
   return FALSE;

// By this point, we should have a good handle, so let's
// start enumerating
do {
   dwCacheInfoSize = 1024;
   memset(piCacheInfo, 0, dwCacheInfoSize);
   piCacheInfo->dwStructSize = dwCacheInfoSize;

   // Walk through to the next entry
   fSuccess = FindNextUrlCacheEntry(hCacheHandle,
     piCacheInfo, &dwCacheInfoSize);
   if(!fSuccess) {
      if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

      // Under normal circumstances, we would have to
      // reallocate a larger buffer and try to make
      // the call again.
} while(fSuccess);

return 0;

While walking through all of the files in the cache is interesting, it's typically more useful to directly interact with a cache entry for a particular URL:

BOOL GetUrlCacheEntryInfo(LPCWSTR lpszUrlName,
   LPDWORD lpdwCacheEntryInfoBufferSize);

The lpszUrlName parameter specifies the URL for which you want to retrieve information from the local cache, and the rest of the parameters are the same as the FindFirstUrlCacheEntry() function.

Once you have found a particular cache entry you are interested in, you need to lock the cache file in order to do anything with it. To do so, you can call RetrieveUrlCacheEntryFile():

BOOL RetrieveUrlCacheEntryFile(LPCWSTR lpszUrlName,
   LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved);

The parameters are the same as for GetUrlCacheEntryInfo(). If the function is successful, your cached data file is now locked, and will not be removed by other processes. You can now do whatever you want with the file, but remember to unlock it before the cache manager can access it again.

To unlock a cache file, use the UnlockUrlCacheEntryFile() function, which is defined as follows:

BOOL UnlockUrlCacheEntryFile(LPCWSTR lpszUrlName, DWORD dwReserved);

The lpszUrlName parameter is the URL of the cache file, and dwReserved must be set to 0.

Creating a Cached File

Although using the WinInet functions to download files through HTTP and FTP will automatically add files to the cache, you may sometimes need to manually add a new file. Doing so is basically a two-step process: You need to first specify where to store the entry in the cache:

BOOL CreateUrlCacheEntry(LPCWSTR lpszUrlName, DWORD
   dwExpectedFileSize, LPCWSTR lpszFileExtension,
   LPWSTR lpszFileName, DWORD dwReserved);

The first parameter, lpszUrlName, is the URL for the file you want to put in the cache. The dwExpectedFileSize parameter is the size of the file, or 0 if you don't currently know it. You will also pass in the lpszFileExtension parameter, which is a buffer containing the extension of the file you are storing. The lpszFileName parameter should point to a buffer that is at least the length of MAX_PATH. This parameter will receive the cache path and name for your file when the function returns. Finally, dwReserved is set to 0.

Now that you have a cache file path (returned in lpszFileName), you can get the file you want to put in the cache using whatever method you want. Once you have the entire file, all you need to do to store it in the cache is call the following:

BOOL CommitUrlCacheEntry(LPCWSTR lpszUrlName, LPCWSTR
   lpszLocalFileName, FILETIME ExpireTime, FILETIME
   LastModifiedTime, DWORD CacheEntryType, LPWSTR
   lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR
   lpszFileExtension, DWORD dwReserved);

As you've seen before, the lpszUrlName parameter is the URL of the file you are sending to the cache. The lpszLocalFileName parameter should be the set to the same name that you received from calling the CreateUrlCacheEntry() function, which was set in its lpszFileName parameter. The ExpireTime and LastModifiedTime parameters are time values for the newly cached file. CacheEntryType can be set to STICKY_CACHE_ENTRY if you want to make this file persistent in the cache; otherwise, set it to 0. The lpHeaderInfo and dwHeaderSize parameters can be used to set any additional header information for your file. Finally, the lpszFileExtension parameter should point to a buffer specifying the current file's extension; and dwReserved can be set to 0.

Deleting Cache Entries

Deleting an entry in the cache is as simple as calling the DeleteUrlCacheEntry() function:

BOOL DeleteUrlCacheEntry(LPCWSTR lpszUrlName);

The only parameter, lpszUrlName, represents the URL for the cached file you want to delete.

Cache Groups

The final topic to cover regarding caching is cache groups. Basically, a cache group is a set of several cache entries that relate to one another and are represented by a group identifier. For example, suppose you have an application that reads data from multiple Web sites?you might want to use a cache group to identify all of the entries from one particular site. Later, when your program is offline, you could easily retrieve any entries you are interested in for that site from the cache group, rather than enumerating all of the cache entries.

Before you can add cache entries to a group, you must first create a new cache group identifier by using the function CreateUrlCacheGroup(), which is defined as follows:

GROUPID CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved);

The only option that you can use with the dwFlags parameter is CACHEGROUP_FLAG_GIDONLY, which creates a unique group identifier, rather than the actual group. Most of the time, you can pass in 0 here. The second parameter, lpReserved, must be set to NULL.

If it is successful, the function will return a new group identifier that you can use to add cache entries to the group. CreateUrlCacheGroup() will return FALSE if it fails.

Now that you have a group ID, you can add and remove cache entries to and from the new group. This is done by using the function SetUrlCacheEntryGroup():

BOOL SetUrlCacheEntryGroup(LPCWSTR lpszUrlName, DWORD
   dwFlags, GROUPID GroupId, LPBYTE pbGroupAttributes,
   DWORD cbGroupAttributes, LPVOID lpReserved);

The first parameter is the familiar lpszUrlName, which is the URL for the cache entry. The dwFlags parameter specifies whether you are adding or removing an entry from the group. Use the flag INTERNET_CACHE_GROUP_ADD to add a file, and INTERNET_CACHE_GROUP_REMOVE to remove it. The GroupId parameter should be set to the group ID for which you want to add or remove the file. The last three parameters must all be set to NULL.

If you need to delete the cache group, you can call the following:

BOOL DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags,
   LPVOID lpReserved);

Finally, if you want to enumerate all the files in a particular group, you can use the FindFirstUrlCacheEntryEx() and FindNextUrlCacheEntryEx() functions. These work the same as the FindFirstUrlCacheEntry() and FindNextUrlCacheEntry() functions, with the addition of a few new parameters. When you are finished enumerating, you must call FindCloseUrlCache():

HANDLE FindFirstUrlCacheEntryEx(LPCWSTR lpszUrlSearchPattern,
   DWORD dwFlags, DWORD dwFilter, GROUPID GroupId,
   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
   LPVOID lpGroupAttributes, LPDWORD pcbGroupAttributes,
   LPVOID lpReserved);

BOOL FindNextUrlCacheEntryEx(HANDLE hEnumHandle,
   LPDWORD lpdwFirstCacheEntryInfoBufferSize, LPVOID
   lpGroupAttributes, LPDWORD pcbGroupAttributes,
   LPVOID lpReserved);

The parameters are basically the same as what you previously defined for FindFirstUrlCacheEntry() and FindNextUrlCacheEntry(). The only real addition is the GroupId parameter, which specifies the group for which you want to enumerate the entries. lpGroupAttributes, pcbGroupAttributes, and lpReserved should all be set to NULL.

Internet Options

Although you can set several available options for an Internet session, you need only two functions to manipulate them: InternetSetOption() and InternetQueryOption(). Let's look at their definitions:

BOOL InternetSetOption(HINTERNET hInternet, DWORD dwOption,
   LPVOID lpBuffer, DWORD dwBufferLength);

BOOL InternetQueryOption(HINTERNET hInternet, DWORD
  dwOption, LPVOID lpBuffer, LPDWORD lpdwBufferLength);

When working with Internet session options, the first parameter always specifies the Internet handle; the second parameter, dwOption, specifies what option you will be working with (see Table 2.5). The last two parameters depend on whether you're getting or setting an option value. If you are setting an option value, lpBuffer will be a pointer to a buffer that contains the option setting, and dwBufferLength will specify its size. When getting option values, lpBuffer will be a pointer to a buffer that receives the option data, and lpdwBufferLength will be a pointer to a variable that contains the length of lpBuffer. When the function returns, if the buffer wasn't large enough, you will get the error ERROR_INSUFFICIENT_BUFFER, and lpdwBufferLength will contain the size, in bytes, that you need to get the option data.

Table 2.5. Pocket PC Internet Options

Option Name





The address of the callback for this handle, specified as a DWORD



The context value associated with this handle, specified as a DWORD pointer



The timeout value, in milliseconds, before a connection request is cancelled, specified as an unsigned long



The number of times a request will attempt to resolve and connect before failing, specified as an unsigned long



The timeout value, in milliseconds, before a send request is cancelled, specified as an unsigned long



The timeout value, in milliseconds, before a receive request is cancelled, specified as an unsigned long



The size, in bytes, of the read buffer, specified as an unsigned long



The size, in bytes, of the write buffer, specified as an unsigned long



The username associated with the session handle, specified as a LPWSTR



The password associated with the session handle, specified as a LPWSTR



Information about the current proxy associated with the session handle, specified as an INTERNET_PROXY_INFO structure



The current proxy password, specified as a LPWSTR



The current proxy username, specified as a LPWSTR



The User-Agent header that is used for HTTP requests, specified as a LPWSTR



Notifies the system that an option value has changed, which will force Pocket PC to reload values from the registry



The type of Internet connection associated with the session handle (FTP, HTTP, HTTPS), specified as an unsigned long



The parent handle to this handle as a HINTERNET

Both functions will return TRUE if they succeed, or FALSE if they fail.

The INTERNET_OPTION_PROXY option uses an INTERNET_PROXY_INFO structure that specifies the current proxy settings for an Internet session handle:

typedef struct {
   DWORD dwAccessType;
   LPCWSTR lpszProxy;
   LPCWSTR lpszProxyBypass;

The dwAccessType member contains the current access method for the handle, which is the same as what was defined previously for the InternetOpen() function. The lpszProxy field will contain the name of the proxy sever associated with the session handle, and lpszProxyBypass will be NULL.

Differences between Windows and Pocket PC WinInet

Note several minor differences between the desktop version of WinInet and what is currently supported on Pocket PC devices:

  • There is no direct support for autodialing an Internet connection. The InternetAutodial(), InternetAutodialHangup(), InternetGetConnectedState(), InternetHangup(), and InternetGoOnline() functions are not currently supported on Pocket PC.

  • The gopher protocol is not supported on Windows CE.

  • Windows CE does not support multiple proxy servers. You can specify only a single proxy server when establishing an Internet connection.

  • Several Internet options are not supported on Pocket PC. Consult Table 2.5 for the Internet options currently supported on Pocket PC.

  • InternetGetCookie() does not support "named" cookie values.

  • The FtpCommand() function is not supported on Pocket PC.

  • The FtpGetFile() and FtpPutFile() functions always return the ERROR_INVALID_PARAMETER error. This is a known bug and is documented in Microsoft KB article Q312039 (found at

  • Windows CE does not support proxy bypass lists.