Hypertext Transfer Protocol (HTTP and HTTPS)

As with any piece of software that communicates over TCP/IP, a protocol is needed to delegate how information is transferred between a client and its server, as well as to define its "session." In the case of the Web, the protocol that is used is the Hypertext Transfer Protocol, also known as HTTP (or HTTPS when referring to "secure" transactions). HTTP is an extremely flexible protocol?it is relatively simple to use, it can make resource requests over firewalls (as most have been designed to allow traffic over port 80), and it can be adapted for numerous types of information transfer between applications other than Web browsers (such as .NET Web Services, SOAP, and so on). In fact, so many applications and technologies are making use of HTTP, it has been referred to as "the cockroach of Internet protocols."

The HTTP Protocol

There are two major versions of HTTP on the Internet today: HTTP/1.0 and HTTP/1.1 (for more detailed information about the protocol commands and extensions, you should check out RFC 1945 and RFC 2068). While they both can make requests in the same basic fashion, HTTP/1.1 adds some additional functionality to the core protocol, such as sessions that stay active between requests, and block transfers.

The HTTP protocol itself is known as a stateless protocol. It does not keep track of things that other session-based protocols do, such as connection time, idle time, and so on. Rather, it is intended to be simple: connect, make a request, get a response, and disconnect.

In its most simplistic form, the whole process looks something like what is shown in Figure 2.2.

Figure 2.2. HTTP protocol


Although a complete description of the HTTP protocol is beyond the scope of this book, it is important to understand a few basic concepts about what HTTP requests and responses look like. While WinInet will provide you with plenty of support functions and APIs to manipulate the protocol and download information, the following sections provide basic information about how extendable and useful HTTP can be.

HTTP Requests

When you query an HTTP server for a particular resource (e.g., file, Web page, and so on), the request is accomplished by first making a connection to the server (usually on the well-known HTTP port 80) and then sending an HTTP request message. An HTTP request is typically made up of three parts: the request line, the HTTP header, and an optional HTTP body.

An example of a request line could look something like the following:

GET /Index.html HTTP/1.1\r\n

The general format is an HTTP command, followed by the resource to download, and the HTTP version that the client can support. In the preceding example, you are instructing the server to GET the /Index.html file using version 1.1 of the HTTP protocol.

The next block of information needed by the server is the actual HTTP header. An HTTP header contains instructions about the request itself, along with information about the client that might be useful to the server, such as the browser type, or connection information. A server can use this information to dynamically create customized content (with an ASP server, such as IIS).

An HTTP request header could look like the following:

Connection: Keep-Alive\r\n
Accept: */*\r\n
User-Agent: Sample Application\r\n
Host: www.microsoft.com\r\n\r\n

As you can see, the header is comprised of several lines that are formed by a description and a value. For example, the Accept line of this request states that the client can receive any data type (designated by the */* value). A blank line is used at the end of the header to indicate that the header is complete.

The final part of an HTTP request is the HTTP body. Typically, the body is left blank unless you are passing specific data to a server (such as form data using the POST command).

Therefore, you could construct an entire HTTP request as follows:

GET /Index.html HTTP/1.1\r\n
Connection: Keep-Alive\r\n
Accept: */*\r\n
User-Agent: Sample Application\r\n
Host: www.microsoft.com\r\n\r\n
HTTP Responses

Once the server receives an HTTP request, it will process it and return an HTTP response to the client (which can be an application, a browser, and so on). An HTTP response contains almost the same type of data as the HTTP request. It will be made up of the response line, the HTTP header, and an HTTP body.

An HTTP response line contains the status of the request (e.g., if it failed, succeeded, needs authorization, and so on):

HTTP/1.1 200 OK

You can see here that the first piece of a response is the HTTP version that the server is using to communicate with you, followed by an HTTP status code. In this case, the code 200 indicates that the request was successful.

Next, the HTTP response header is returned. This is similar to the request header that was originally sent to the server, as the HTTP response header is comprised of several lines of information, which are formed with a description and a value. It can contain very useful information about the server, response data, and so on, as shown in the following example:

Server: Microsoft-IIS/5.0\r\n
Content-Location: http://www.microsoft.com/default.htm\r\n
Date: Tue, 25 Jun 2002 19:33:18 GMT\r\n
Content-Type: text/html\r\n
Accept-Ranges: bytes\r\n
Last-Modified: Mon, 24 Jun 2002 20:27:23 GMT\r\n
Content-Length: 26812\r\n

Finally, if the HTTP request was successful, the HTTP response body will then contain the data you requested (which can be binary, HTML, and so on). Once the response body has been transmitted, the HTTP server (if not using HTTP/1.1 Keep-Alive requests) will then disconnect.

Putting the entire process together provides a clearer view of a request and response, as shown in Figure 2.3.

Figure 2.3. HTTP protocol requests and responses



A cookie is a standardized method by which a server creates user-specific information that is stored on a client device?information such as preferences, passwords, or other types of data. When a client makes an HTTP request to the server that originally created the cookie, it will also transfer the data contained in the cookie. For example, if a Web site has a way to customize its look and feel based on user preferences, those preferences could be stored on a device in the form of a cookie. When the client device accesses that Web site, the browser sends the preference information along with the HTTP request so that the server knows how to customize it.

The actual data that is stored inside a cookie is a set of properties in the form of name=value pairs that are separated by commas, with the parameter separated with a semicolon. For example, a cookie might look like the following:

   01-Jan-2007 00:00:00 GMT;

Table 2.6 lists a few of the reserved value names used with cookies:

Table 2.6. Cookie Reserved Value Names




The date that the cookie expires in GMT. If no expiration date is set, the cookie will expire as soon as the process creating the cookie ends.


The domain for which the cookie is valid.


The path part of a URL for which the cookie is valid.


No value is associated with the secure flag. It only indicates that the cookie is safe for sending to HTTPS server requests (name=value;secure).

Even though cookies are typically used for Web-based applications, there is no reason why you couldn't use the WinInet cookie functions to store client-specific information for your own applications. You can use two functions on Pocket PC to create and read cookie information: InternetSetCookie() and InternetGetCookie().

To create a new cookie, you can use the following:

BOOL InternetSetCookie(LPCWSTR lpszUrl, LPCWSTR
  lpszCookieName, LPCWSTR lpszCookieData);

The first parameter, lpszUrl, is the URL for which the cookie is valid. The lpszCookieName parameter can be used to set the name of a cookie, but it also can be set to NULL if it is not needed. Finally, lpszCookieData should point to the buffer that contains the actual cookie value.

A simple example for creating a cookie would look like the following:

TCHAR tchURL[64] = TEXT("http://myweb.com");
TCHAR tchCookieName[64] = TEXT("MyCookie");
TCHAR tchCookieData[128] =
   expires=Sat, 01-Jan-2007 00:00:00 GMT;");

BOOL fSucceeded = InternetSetCookie(tchURL, tchCookieName,

To read cookie information, you use the InternetGetCookie() function:

BOOL InternetGetCookie(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
   LPWSTR lpCookieData, LPDWORD lpdwSize);

InternetGetCookie()'s parameters are similar to those of InternetSetCookie(). As before, the lpszUrl parameter specifies the URL for which you want to get the cookie, and lpszCookieName should be set to NULL, as searching for named cookies is not implemented on Pocket PC. When the function returns, the buffer that lpCookieData points to will contain the actual cookie information. Finally, lpdwSize should point to a DWORD value that specifies the size of the lpCookieData buffer; otherwise, it will set this to the number of bytes that are needed if the buffer is too small.

Therefore, to read a cookie, you could do the following:

TCHAR tchURL[64] = TEXT("http://myweb.com");
TCHAR tchCookieBuffer[128] = TEXT("\0");
DWORD dwSize = 128;

BOOL fSucceeded = InternetGetCookie(tchURL, NULL, tchCookieBuffer,

Making an HTTP Request

Using HTTP to request resources is a relatively easy process regardless of which HTTP version you are using (as discussed earlier, the basic protocol works the same for HTTP/1.0 and HTTP/1.1). There are typically four steps for creating an HTTP transaction:

  1. Create a connection to an HTTP server on a specified port (typically, port 80).

  2. Create a request and send it to the server. A request typically consists of a command and a set of request headers.

  3. Read the response from the server. Usually, you should inspect the response header for additional information (such as the size of the response).

  4. Close the session. If you are using HTTP/1.1, you can use the Keep-Alive header to keep your connection to the server open. Requesting additional URL resources in this situation would only require you to repeat this process from Step 2. Otherwise, the server will automatically close its connection when the transfer is complete.

The first thing you need to do is create a connection to an HTTP server and send it a request for a URL. You can accomplish this by using the HttpOpenRequest() function to create a new Internet handle for your request session, followed by HttpSendRequest() to build and send the actual request. When using these functions, an actual connection to the server is not made until you call HttpSendRequest():

   lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion, LPCWSTR
   lpszReferrer, LPCWSTR FAR *lplpszAcceptTypes, DWORD dwFlags,
   DWORD dwContext);

The first parameter you need to pass in is the handle to the open Internet session returned from previously calling the InternetOpen() function. The next three parameters are used to build the actual HTTP request. The HTTP action, such as GET or POST, is set by using the lpszVerb parameter. If you set this to NULL, it will default to the GET action. Next, lpszObjectName should point to a string that has the name of the resource you want to target, and the lpszVersion parameter specifies the HTTP version to use for the transfer. If this is set to NULL, it will default to HTTP/1.1.

The next set of parameters is used to build the HTTP request header. Use the lpszReferrer parameter to specify the URL address of the location making the request (setting the Http-Referrer header field), and use lplpszAcceptTypes to indicate which MIME content types are accepted by the client. If you set this to NULL, no types are accepted (this sets the Content-Type header field).

The dwFlags parameter is used to control any of the specifics of the actual request, and can be one or more of the flags described in Table 2.7.

The final parameter, dwContext, enables you to attach an application-specific value to the session handle. If you receive a NULL value when the function returns, an error has occurred. Otherwise, at this point, you should have a valid HTTP request handle (remember that you will need to call InternetCloseHandle() on this handle once you have finished using it).

Now that you have a request handle, you can send the request to the server for processing. This is accomplished by using the HttpSendRequest() function, which is defined as follows:

BOOL HttpSendRequest(HINTERNET hRequest, LPCWSTR
   lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional,
   DWORD dwOptionalLength);

The hRequest parameter is the request handle returned from HttpOpenRequest(). If you have any additional headers to add to this request, you can use the lpszHeaders and dwHeadersLength parameters to specify the buffer and the size of them (or, you can specify the size to be -1L if you want the function to automatically calculate the length of the additional headers). If you have no headers to add, just set both of these to a NULL value. The lpOptional parameter is used to point to any additional data that you need to send after the headers if you are doing a POST operation; otherwise, this too can be set to NULL. Finally, dwOptionalLength is the size of the buffer used for lpOptional.

If the function succeeds, it will then contact your server and send the following: the HTTP request, any HTTP headers that are determined by the flags you set, and any additional headers you've specified in HttpSendRequest(), followed by the optional data. Be aware that if you use the lpOptional parameters, you will need to start your buffer using "/r/n" to ensure that your buffer is properly separated from the HTTP header.

Sending a basic request to get the root Web page would be as simple as the following:

// Make an HTTP-style request to a server
// hIConnect is the handle returned to us from a previous call to
// InternetConnect

HINTERNET hIRequest = HttpOpenRequest(hIConnect,
  TEXT("GET"), TEXT("/"), NULL,
   NULL, NULL, 0, 0);

// Send the request
BOOL fSuccess = HttpSendRequest(hIRequest, NULL, 0, NULL, 0);

Table 2.7. HttpOpenRequest() Flags




If the network or resource is inaccessible, return the data from the cache.


Do not cache any data.


Force a reload of the data.


Disable SSL certificate checking.


Disable SSL certificate date checking.


Transparently allow redirection from HTTP to HTTPS locations.


Transparently allow redirection from HTTPS to HTTP locations.


Use Keep-Alive for HTTP/1.1 transactions.


Make this item persistent in the cache.


Create a temporary file if the file cannot be cached.




Do not attempt automatic authentication.


Do not automatically handle redirection.




Do not automatically add cookie headers to any requests.


Disable the cookie UI dialog box.


Force a request to be handled by the server regardless of whether there is cache data.


Reload data from the wire.


Reload HTTP data if the resource was modified since the last time it was requested.


Use SSL for secure transactions.

If you examined the HTTP header for what was just sent to the server, it would look like the following:

GET / HTTP/1.1\r\n
User-Agent: Sample Application\r\n
Host: www.microsoft.com\r\n\r\n

In some instances?especially if you are using HTTP to transfer data for some custom information, rather than a Web site?you might need to add additional headers for your HTTP request. While you can also do this with the HttpSendRequest() function, you can get more control of the HTTP headers by using HttpAddRequestHeaders(), as you have the capability to add, remove, or replace header commands:

BOOL HttpAddRequestHeaders(HINTERNET hRequest, LPCWSTR
   lpszHeaders, DWORD dwHeadersLength, DWORD dwModifiers);

As with HttpSendRequest(), the hRequest parameter is the current request handle returned from HttpOpenRequest(). The lpszHeaders parameter is a pointer to a buffer of carriage return/linefeed (or "\r\n") terminated headers that you want to add to your request, and the size is specified by the dwHeadersLength parameter. If you would like HttpAddRequestHeaders() to automatically calculate the length, set dwHeadersLength to -1L. The last parameter, dwModifiers, specifies how the new request headers should be added to the current request. It can be one or more of the following listed in Table 2.8.

Table 2.8. HTTP Request Modifier Flags




Add the header if it does not exist.


Add the header only if it does not exist.


Append to the header of the same name.


Append to the header of the same name, with a comma.


Append to the header of the same name, with a semicolon.


Replace or remove the header. If the header value is empty, the header is removed; otherwise, it is replaced.

Note that if you are trying to replace or remove a header, you can only modify a single header entry at a time; otherwise, you can use HttpAddRequestHeader() to modify multiple entries.

Now that you have completed modifying the headers, you can then proceed to call HttpSendRequest() to submit your HTTP request. Let's take a look at the previous example now that some of the HTTP headers have been modified:

HINTERNET hIRequest = HttpOpenRequest(hIConnect,
   TEXT("GET"), TEXT("/"), NULL, NULL, NULL, 0, 0);

// Modify some headers
TCHAR tchNewHeaders[256] = TEXT("MyHeader:
   TestHeader\r\nCoffee-Order: Double Tall Mocha\r\n");
BOOL fSuccess = HttpAddRequestHeaders(hIRequest,
   tchNewHeaders, -1, HTTP_ADDREQ_FLAG_ADD);

// Send the request
fSuccess = HttpSendRequest(hIRequest, NULL, 0, NULL, 0);

The preceding code would result in a new HTTP header that looked like the following:

GET / HTTP/1.1\r\n
MyHeader: TestHeader\r\n
Coffee-Order: Double Tall Mocha\r\n
User-Agent: Sample Application\r\n
Host: www.microsoft.com\r\n\r\n

Getting Information from the HTTP Headers

Before we look at retrieving the actual result data from the request, we should take a quick look at how to get information from your HTTP session headers. Both the HTTP request and the HTTP response headers can provide us with a lot of interesting and useful information about the state of your HTTP session. Fortunately, there is an easier method using the HttpQueryInfo() function.

The prototype for HttpQueryInfo() is as follows:

BOOL HttpQueryInfo(HINTERNET hRequest, DWORD dwInfoLevel,
  LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex);

Once again, the hRequest parameter needs to be the HTTP request handle that was previously created with the call to either HttpOpenRequest() or InternetOpenUrl(). Next, the dwInfoLevel parameter specifies what information you want to get from the headers that will be placed inside the return buffer to which lpBuffer points. This buffer can be a variety of different types: a string, a SYSTEMTIME value, or a DWORD, depending on what information you are requesting with the dwInfoLevel parameter. For example, if you want to find out the size of the content returned on the request (Content-Length), you use a DWORD value. The lpdwBufferLength parameter is a pointer to the size of your buffer. If the function fails, this will contain the size of bytes you actually need if your buffer is too small. Finally, the lpdwIndex parameter is used if you have multiple headers with the same name?you pass in a pointer to the index of the header to use.

Table 2.9 describes the possible values for the dwInfoLevel parameter.

Table 2.9. Possible Values for dwInfoLevel




Get the accepted media types.


Get the accepted character sets.


Get the accepted encoding values.


Get the accepted language name.


Get the range request.


Get the age value.


Get the supported methods on the server.


Get the username/password for the request.


Get cache control directives.


Get connection-specific directives.


Get any cookies for the request.


Get the base URL of the request.


Get any additional response encodings.


Get the identification of the content.


Get the language of the content.


Get the length of the content in bytes.


Get the location of the content in the message.


Get the MD5 digest of the content body.


Get the location in the content where the partial data begins.


Get any additional content encoding.


Get the MIME content type.


Get the date and time the content was created.


Get the entity tag for the content.


Get the date and time that the content expires.


Get the e-mail address of the requestor of the content.


Get the host name and port of the resource request.


Get the If-Match header field.


Get the If-Modified-Since header field.


Get the If-None-Match header field.


Get the If-Range header field.


Get the If-Unmodified-Since header field.


Get the date and time that the resource was last modified.


Get the URL from the response header.


Get the maximum value of an HTTP_QUERY value.


Get the maximum number of forwards for the request.


Get the MIME version of the request.


Get any application-specific commands.


Get the authentication request from the proxy server.


Get the authorization information from the proxy server.


Get the supported methods on the server.


Get the byte range for the content.


Get all the headers from the server response. Each header value is null-terminated.


Get all the headers from the server response. Each header value is terminated by a carriage return/linefeed (\r\n).


Get the URL from which the request originated.


Get the action type for the current request.


Get the amount of time between request retries.


Get the name of the server software.


Get the value information to set a cookie on the request.


Get the return status code for the request.


Get the return status text for the request.


Get the transfer encoding information.


Get information about any additional protocols supported on the server.


Get the URL information for the request.


Get the User-Agent header for the request.


Get the header information for multiple-version responses.


Get the version information from the server response.


Get information on any intermediate protocols between the client and server.


Get the return status information for the request.


Get the authentication scheme of the server.

In addition, you can combine any of the queries with the modifiers described in Table 2.10.

Table 2.10. HTTP Query Modifiers




Search the header for a name that is specified in lpBuffer, and store the value in the same buffer when returning.


Return the header value as a DWORD.


Return header values only for the request headers.


Return the header value as a SYSTEMTIME.

As you can see by the variety of flags, HttpQueryInfo() enables you to inspect any part of the header information that is part of an HTTP request or its response. For example, if you wanted to look at the outgoing HTTP header for the previous example, you could simply do something like this:

HINTERNET hIRequest = HttpOpenRequest(hIConnect,
  TEXT("GET"), TEXT("/"), NULL, NULL, NULL, 0, 0);

DWORD dwHeaderSize = 1024;
TCHAR tchHeader[1024] = TEXT("\0");

// Send the request
BOOL fSuccess = HttpSendRequest(hIRequest, NULL, 0, NULL, 0);

// Inspect the send headers
fSuccess = HttpQueryInfo(hIRequest,
   CRLF, tchHeader, &dwHeaderSize, 0);

The preceding example creates a text buffer to capture the entire outgoing request header. After HttpQueryInfo() returns, tchHeader will look like the following:

GET / HTTP/1.1\r\n
User-Agent: Sample Application\r\n
Host: www.microsoft.com\r\n\r\n

If you were interested only in the User-Agent field, you could modify the request as follows:

fSuccess = HttpQueryInfo(hIRequest,
   tchHeader, &dwHeaderSize, 0);

After this executes, the tchHeader buffer will contain only your User-Agent string:

Sample Application\r\n

Finally, if you need to convert the response information (which is by default stored as a string) into a different data type, such as a number, you can use the HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_FLAG_SYSTEMTIME modifiers. For example, if you wanted to find out the length of the content that was returned to you after you made your request, it would be easier to do something with that information if it were in a number format. You could simply call HttpQueryInfo() as follows:

DWORD dwLength = 0;
DWORD dwLengthSize = sizeof(DWORD);

// Get the content length
fSuccess = HttpQueryInfo(hIRequest, HTTP_QUERY_CONTENT_LENGTH|
   HTTP_QUERY_FLAG_NUMBER, &dwLength, &dwLengthSize, 0);

Your DWORD variable, dwLength, now will contain the size of the data you received from the server.

Reading HTTP Results

Now that you have learned how to make a request and inspect the response information, you have everything you need to read the actual contents. You can download data from your request using the InternetReadFile() function. Be aware that both HTTP and FTP use this function the same way, and WinInet will determine the protocol it is using by the request handle you pass into it.

Here's what the prototype for InternetReadFile() looks like:

BOOL InternetReadFile(HINTERNET hFile, LPVOID lpBuffer,
   DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead);

The hFile parameter is the handle to your HTTP request, which you make with either InternetOpenUrl() or HttpOpenRequest(). Next, lpBuffer is a pointer to a buffer that will receive the downloaded content, and dwNumberOfBytesToRead is the number of bytes you are attempting to read from the request. The last parameter, lpdwNumberOfBytesRead, points to the actual number of bytes that are downloaded into lpBuffer. If the function succeeds, the return value for InternetReadFile() will be TRUE. Be aware that InternetReadFile() works in a similar fashion to a file handle?if there is no more data to be read, the function will still return TRUE, but the lpdwNumberOfBytesRead parameter will be 0.

Here's an example of how to read the response from a request:

// Download the file
LPVOID lpBuffer = NULL;
DWORD dwRead = 0;
BOOL bActive = TRUE;

lpBuffer = (LPVOID)LocalAlloc(LPTR, 4096);

if(!lpBuffer) {
   // An error has occurred, so close the handles
   return FALSE;

   fSuccess = InternetReadFile(hIRequest, lpBuffer, 4096,

   if(!fSuccess || dwRead == 0) {
   bActive = FALSE;

   // Do something with the buffer, and get more data

   memset(lpBuffer, 0, 4096);
   dwRead = 0;
} while(bActive);


// Ok, we got our data; make sure to close the handles!


Because the data you are receiving is being transmitted over a network, the safest way to ensure that you receive all your content is to first query your request handle for the actual content size (using HttpQueryInfo()), before you start reading data. Once you know the size of the content the server is sending, you can compare it with what you have received to make sure you have the entire amount of data.

Once you have completed downloading all of your data, be sure to close all the handles you used to make the request in reverse order (close the request, then the session, then the main Internet handle) using the InternetCloseHandle() function.

Additional WinInet HTTP Functions

Several other available WinInet API functions will enable you to have more precise control over an HTTP request (or will just make your life easier). For the sake of completeness, I have included them here.

Time Conversion

HTTP requests use a standardized format for displaying date/time information, which is defined by RFC 1123. The text-based format is in GMT, and can be easily converted from/to a SYSTEMTIME value using the following functions:

BOOL InternetTimeFromSystemTime(CONST SYSTEMTIME *pst,
   DWORD dwRFC, LPWSTR lpszTime, DWORD cbTime);

BOOL InternetTimeToSystemTime(LPCWSTR lpszTime,
   SYSTEMTIME *pst, DWORD dwReserved);

To convert a SYSTEMTIME structure to a GMT format, use the InternetTimeFromSystemTime() function. The first parameter is the time value to convert, and dwRFC specifies the format. Pocket PC supports only INTERNET_RFC1123_FORMAT for this parameter. Finally, the cbTime and lpszTime parameters point to a text buffer and its size, respectively. This will be used to store the string containing the converted time format.

For example, the following converts the current time to a valid RFC 1123 Internet time:

TCHAR tchInternetTime[256] = TEXT("\0");

InternetTimeFromSystemTime(&sysTime, INTERNET_RFC1123_FORMAT,
   tchInternetTime, 256);

This would cause the tchInternetTime buffer to be set as follows:

Tue, 18 Jun 2002 09:52:40 GMT

To do the reverse conversion (from an Internet time to a SYSTEMTIME), you can use the InternetTimeToSystemTime() function. The only parameter it needs is lpszTime, which is a buffer in GMT format, and pst, which points to a SYSTEMTIME variable to receive the converted data. The dwReserved parameter is not used and should be set to 0.

Advanced HTTP File Operations

The InternetSetFilePointer() function enables you to set the position in the remote file where you would like the file pointer to be set for future calls to InternetReadFile(). This function can be used only when the target file is already in the local Internet cache, or the HTTP server you are talking with supports random access reading of files, specifically to be used with the HTTP/1.1 protocol. Here's the definition of InternetSetFilePointer():

DWORD InternetSetFilePointer(HINTERNET hFile, LONG
   lDistanceToMove, PVOID pReserved, DWORD dwMoveMethod,
   DWORD dwContext);

As usual, the hFile parameter is the Internet handle that was returned from either the InternetOpenUrl() or HttpOpenRequest() functions. The lDistanceToMove parameter is the number of bytes (positive or negative) that the file pointer should move, and the pReserved parameter should be set to NULL. As with the file-based SetFilePointer() function, the dwMoveMethod parameter can be FILE_BEGIN, FILE_CURRENT, or FILE_END, depending on where you want to start the move from. The final parameter, dwContext, is not used and should be set to NULL.

Another function to mention is InternetQueryDataAvailable(). This function provides you with an easy way to query the amount of data, in bytes, that is currently available in the receive buffer for a particular request handle (that was created by using the InternetOpenUrl() or HttpOpenRequest() functions). You can use this as an alternative to checking the result header information along with the return values to the InternetReadFile() function. Here's what the function looks like:

BOOLAPI InternetQueryDataAvailable(HINTERNET hFile,
   LPDWORD lpdwNumberOfBytesAvailable, DWORD dwFlags,
   DWORD dwContext);

The first parameter will be the handle for your request, which is followed by lpdwNumberOfBytesAvailable, a pointer to a DWORD value, which will return the number of bytes that are ready for you to read with the next call to InternetReadFile(). The last two parameters should be set to 0.

Locking Cached Data

Because you are on a portable device, your applications need to perform both on and off the network, as you might go offline frequently. In this operating mode, it is important to frequently update your Internet-cached files so that your application can perform properly even when the network is not available. If you ever need to lock a file that is in the Internet cache, you can use the functions InternetLockRequestFile() and InternetUnlockRequestFile(). Once a file is locked, if your application attempts to download data from the data Internet location as specified in the cache, Pocket PC will protect the file until you call the unlock function. Here's how the functions are prototyped:

BOOL InternetLockRequestFile(HINTERNET hInternet,
   HANDLE *lphLockRequestInfo);

BOOL InternetUnlockRequestFile(HANDLE hLockRequestInfo);

The InternetLockRequestFile() function is fairly simple: Pass in a HINTERNET request handle from InternetOpenUrl() or HttpOpenRequest() and you will be passed back a pointer to a lock request handle. The InternetUnlockRequestFile() function needs this handle to unlock the file.