Developer.com Click here to support our advertisers
Click here to support our advertisers
SOFTWARE
FOR SALE
BOOKS
FOR SALE
SEARCH CENTRAL
* JOB BANK
* CLASSIFIED ADS
* DIRECTORIES
* REFERENCE
Online Library Reports
* TRAINING CENTER
* JOURNAL
* NEWS CENTRAL
* DOWNLOADS
* DISCUSSIONS
* CALENDAR
* ABOUT US
----- Journal:

Get the weekly email highlights from the most popular online Journal for developers!
Current issue -----
developer.com
developerdirect.com
htmlgoodies.com
javagoodies.com
jars.com
intranetjournal.com
javascripts.com

REFERENCE

All Categories : ActiveX

Chapter 4

WinInet


CONTENTS

The WinInet API provides access to the common Internet application-layer protocols HTTP, FTP, and Gopher. With this API you can add the ability to access Internet resources to both new and existing Windows applications. The API provides control over each step of a transaction and provides the means to read and writes files. By using WinInet, a programming project is simplified. Coding native windows' socket calls is unnecessary because the API handles this function. Similarly, the underlying mechanics of the Internet protocols (particularly the mechanics of establishing a connection) become transparent to the developer. In other words, WinInet offers typical API efficiency gains to the developer by backgrounding the low-level "grunge" coding that is often the most time-consuming phase of a project.

This chapter provides a solid foundation to the developer who is interested in using the WinInet API. We will cover some of the basic steps needed to establish a connection to the Internet and to access files. Also, we'll take a look at some of the API functions that you'll need to use. Following this overview, you'll get a chance to see some sample applications that use WinInet.

NOTE
We won't cover exhaustively all the details of WinInet. This API is a broad topic-the technical reference itself is about 100 pages. And WinInet is bound to expand. You'll notice that one of the structures we explore in this chapter contains definitions for the Internet mail and news protocols. It's reasonable to infer from this that other application-layer protocols, such as SMTP and NNTP, are likely to be supported in the not-too-distant future.

NOTE
The WinInet API has been in a state of flux since the ActiveX SDK was first released. The original versions of the SDK had a couple of sample applications, SurfBear and AsyncFtp. The AsyncFtp application is an FTP client that does asynchronous file transfers; SurfBear is a simple HTTP client. These applications were subsequently dropped from the SDK without explanation. At about the same time, the documentation disappeared from the Microsoft Web site, although SurfBear did show up on the Web site. The documentation later resurfaced, looking pretty much the same, but without the samples. We have found that they still run (as of this writing)-if you can find a copy.

WinInet Keys

To develop applications using the WinInet API, you must

  • Add a reference to the wininet.lib in your build settings under Link | Input.
  • Add an include statement for the WinInet header file, which we usually put in the stdafx.h file.

In the rest of this section we will look at a few other required items that are common to all WinInet applications.

Handles

With each of the protocols, a few steps are universally required for conducting Internet transactions. The first of these steps involves setting up a few handles of the type HINTERNET. HINTERNET handles are specific to the WinInet functions and cannot be interchanged with other Windows handles. Typically, you need an Internet session handle, a connection handle, and in the case of HTTP, a request handle.

The handles are hierarchical in nature. That means you normally need to establish just one Internet session handle, and from that you can create multiple session handles. From each session handle you can create multiple request handles.

Requesting and Reading Files

After the appropriate Internet handles have been created, the next step is to request a file. If the request is successful you will get another handle-this time to the requested file, from which you can then read data.

When you are done with a file, good programming practice dictates that you should close any handles opened. Closing a parent causes its children to close.

Setting Options

With each of the protocols, one of the keys to success is to pay attention to the dwFlags parameter through which you can set various options. All of the flags whose values are constants are defined in the wininet.h header file. If you get some inexplicable error, it's a good idea to double-check the Microsoft documentation against this header file.

For example, the InfoViewer version of the WinInet documentation states that both InternetCanonicalizeUrl and InternetCombineUrl can accept the value ICU_BROWSER_MODE in dwFlags, which alters how the URL is encoded. When we tried this value, we got an error, and then discovered that there is no definition in wininet.h. A header file is usually your best reference, along with the Microsoft documents, as usual.

Error Handling

Regularly checking for errors is a good habit to get into, and with WinInet, we have found that it is necessary at almost every step. A couple of error checking methods are available. Many of the WinInet functions return a Boolean value type, so you will at least know that a function has failed and the program shouldn't proceed to the next step.

When a function fails, you can then try to determine the cause with GetLastError(). We dropped in a block of code like the following just about every time there was a problem:

LPTSTR lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
FORMAT_MESSAGE_FROM_SYSTEM, 
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
256,NULL);
TRACE("Last Error:  %s\n", lpMsgBuf);

GetLastError is generally the most useful debugging tool with WinInet. It will report such things as an invalid handle or a successful or unsuccessful operation. If you are calling a function improperly, however, GetLastError will simply report "a parameter is incorrect," in which case you're pretty much on your own in determining which parameter is incorrect and why.

A Simple WinInet Example

Before beginning the WinInet function overview, here's a quick sample program to whet your appetite. We thought up this example after reading about some trouble at an office where employees were having a little too much fun Web surfing. The boss wanted to use the Internet to get information to his employees, but the open nature of the Web browser meant that employees could do a lot more than just read interoffice memos.

Our simple example takes advantage of WinInet to let the boss use the Internet as he intended-to provide information from the Internet without giving the staff any other options. This example application opens a connection to an HTTP server, retrieves a text file, and displays it in a dialog box. That's it-all the hapless employee can do is read the day's notice and then click OK to dismiss the application, as shown in Figure 4.1.

Figure 4.1 : An office memo, distributed via a WinInet application.

This may be a trivial example, but it shows the potential for WinInet: All the boss has to do is change the text of the file on the HTTP server that is referred to in the employee's application. The information becomes available, without giving the employee full Internet access. Listing 4.1 shows the relevant portion of the source code that generates the information output. This function retrieves and displays data via HTTP.


Listing 4.1 Part of the OnInitDialog function in notice.exe
hSession = InternetOpen(AfxGetAppName(),
               INTERNET_OPEN_TYPE_DIRECT, 
               NULL,NULL,NULL);

     LPCTSTR lpszHostName = "127.0.0.1";
     INTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTP_PORT;
               DWORD dwService = INTERNET_SERVICE_HTTP;
               DWORD dwFlags = NULL;
               DWORD dwContext = 0; //synchronous transfer

     hConnection = InternetConnect(hSession,
               lpszHostName,nServerPort,
               NULL,NULL,
               dwService,dwFlags,
               dwContext);
     
     hRequest = HttpOpenRequest(hConnection,
               "GET","/Notice.htm",
               NULL,NULL,NULL,
               INTERNET_FLAG_RELOAD|
               INTERNET_FLAG_EXISTING_CONNECT,
               dwContext);
     
          if(hRequest==NULL)
          {
               SetDlgItemText(IDC_NOTICE, "No Warning Today");
               InternetCloseHandle(hSession);
               return TRUE;
          }

     BOOL bSendRequest=FALSE;
     LPCTSTR lpszHeaders=NULL;
     DWORD dwHeadersLength;
     LPVOID lpOptional=NULL;
     DWORD dwOptionalLength;

     bSendRequest = HttpSendRequest(hRequest,
               lpszHeaders,dwHeadersLength,
               lpOptional,dwOptionalLength);
     BOOL bRead=TRUE;
     CHAR * szBuffer[4096];
     DWORD dwNumberOfBytesToRead = 4096;
     DWORD lpNumberOfBytesRead;

     bRead = InternetReadFile(hRequest,
               szBuffer,dwNumberOfBytesToRead,
               &lpNumberOfBytesRead);
     
          if(!bRead)
          {
               SetDlgItemText(IDC_NOTICE, "No Warning Today");          
          }else
          {     SetDlgItemText(IDC_NOTICE, (LPCTSTR)szBuffer);}
     InternetCloseHandle(hSession);     

A Sampling of WinInet Functions

This section provides an overview of frequently used WinInet functions. The functions covered here are the ones we tried out while writing this chapter. Complete details on WinInet functions can be found in the InfoViewer file that ships with the ActiveX SDK, or on the Microsoft Web site at

http://www.microsoft.com/intdev/sdk/docs/wininet/.

Handle/Connection Functions

This group of functions is used to open and manage connections to both local and remote hosts, and to local and remote files.

InternetOpen

This function must be called within any program that actually accesses the Internet.

HINTERNET InternetOpen(
    IN LPCTSTR lpszAgent,
    IN DWORD dwAccessType,
    IN LPCTSTR lpszProxyName OPTIONAL,
    IN LPCSTR lpszProxyBypass OPTIONAL,
    IN DWORD dwFlags);

InternetOpen establishes parameters related to the client machine's connection to the Internet. Its parameters are defined as follows:

lpszAgent is the value that will be sent to the server as the HTTP environment variable HTTP_USER_AGENT.

dwAccessType indicates the client machine's type of access to the Internet, with one of the following values:

  • INTERNET_OPEN_TYPE_DIRECT indicates client does not have to go through a proxy server to access the Internet; it doesn't matter whether the machine is hard-wired or connected via modem.
  • INTERNET_OPEN_TYPE_PROXY indicates client must first pass through a proxy server to access the Internet.
  • INTERNET_OPEN_TYPE_PRECONFIG indicates client program should use whatever value is in the Registry to determine the type of access. We didn't deal with a proxy server, so all of our samples use the direct access type.

dwFlags allows you to specify two options when the handle is created:

  • INTERNET_FLAG_OFFLINE indicates that all requests should be satisfied from the cache.
  • INTERNET_FLAG_ASYNC indicates that requests should be satisfied using asynchronous behavior.

If you set the async flag, the Context value associated with at least one of the handles involved in a transaction must be nonzero, and a Callback routine must be established. (We'll take a look at asynchronous behavior in a later section, "HTML Dump.")

NOTE
According to the Microsoft documentation that ships with the ActiveX SDK, if a request cannot be completed asynchronously, the request will be completed synchronously. We did not find this to be consistent. Sometimes, although we might have had everything in place, failed async behavior caused Exceptions, not synchronous transfers.

InternetConnect
The InternetConnect function establishes the parameters that define the connection between your client machine and a remote machine.
HINTERNET InternetConnect(
    IN HINTERNET hInternetSession,
    IN LPCTSTR lpszServerName,
    IN INTERNET_PORT nServerPort,
    IN LPCTSTR lpszUsername OPTIONAL,
    IN LPCTSTR lpszPassword OPTIONAL,
    IN DWORD dwService,
    IN DWORD dwFlags,
    IN DWORD dwContext);
For the server name, you can supply either an IP number or a fully qualified domain name. The service is a constant, either INTERNET_SERVICE_FTP, INTERNET_SERVICE_GOPHER, or INTERNET_SERVICE_HTTP.
The value for the remote server port is one of the following:
  • INTERNET_DEFAULT_FTP_PORT (port 21)
  • INTERNET_DEFAULT_HTTP_PORT (port 80)
  • INTERNET_DEFAULT_HTTPS_PORT (port 443)
  • INTERNET_DEFAULT_GOPHER_PORT (port 70)
Or, you can avoid specifying an explicit port with the value INTERNET_ INVALID_PORT_NUMBER. This uses the default port on the remote machine for the service specified by the service parameter.
The one flag that can be set for the InternetConnect function is INTERNET_CONNECT_FLAG_PASSIVE. This is for setting FTP service into passive mode.
The Context value is one that you supply, with a value greater than zero indicating that transfers will be done asynchronously.
Note that with the HTTP protocol, InternetConnect does not actually trigger the connection. That does not occur until the HttpSendRequest function is called.
InternetOpenUrl
The next function, InternetOpenUrl, will open a file regardless of the protocol type and can be used to open a single ASCII file.
HINTERNET InternetOpenUrl(
    IN HINTERNET hInternetSession, 
    IN LPCTSTR lpszUrl,
    IN LPCTSTR lpszHeaders OPTIONAL,
    IN DWORD dwHeadersLength,
    IN DWORD dwFlags,
    IN DWORD dwContext);
We didn't have much occasion to use this function, generally preferring to use the protocol-specific methods. However, the flags available for this function, listed below, show up in other functions.
  • INTERNET_FLAG_RELOAD causes the program to reload the URL even if a copy exists in the cache.
  • INTERNET_FLAG_DONT_CACHE indicates not to cache the file retrieved.
  • INTERNET_FLAG_RAW_DATA returns raw data if specified. For FTP and Gopher, this data will be placed in the appropriate _FIND_DATA structure.
  • INTERNET_FLAG_SECURE is for conducting secure HTTP transactions via either SSL or PCT.
  • INTERNET_FLAG_EXISTING_CONNECT instructs WinInet to attempt to use an existing connection to the remote server.
InternetCloseHandle
InternetCloseHandle closes the specified handle and any handles descended from it.
BOOL InternetCloseHandle(IN HINTERNET hInet);
Closing a handle usually terminates any pending operations on any handles or files descended from the closed handle. There are some cases where, if there is data pending, the handle can remain open, although you can't initiate any new operations with it or reuse the handle.
InternetAttemptConnect
This function "tests the waters" and returns a value indicating either ERROR_SUCCESS or a Win32 error code. Since this function does not accept an Internet handle as a parameter, it can only test the client's connection to the Internet, not to a particular server.
DWORD InternetAttemptConnect(DWORD dwReserved);
NOTE
This function is in the documentation, but not in wininet.h, so the status of the function is uncertain

URL Functions

The URL-related functions give you control over the various parts of a URL, allowing you to create, split apart, and encode/decode a URL.

InternetCrackUrl
"Cracking" a URL has a nice sound to it, and this function proves highly useful indeed. It takes a URL as a parameter and breaks it down into components, which are then accessible via the UrlComponents structure.
BOOL InternetCrackUrl(
    IN LPCSTR lpszUrl,
    IN DWORD dwUrlLength,
    IN DWORD dwFlags,
    IN OUT LPURL_COMPONENTS lpUrlComponents);
The function returns False if it cannot parse the string supplied as a URL.
The cracking function, as well as InternetCreateUrl (described in the upcoming section), uses a structure defined as follows:
typedef struct {
    DWORD dwStructSize;
    LPTSTR lpszScheme;
    DWORD dwSchemeLength;
    INTERNET_SCHEME nScheme;
    LPTSTR lpszHostName;
    DWORD dwHostNameLength;
    INTERNET_PORT nPort;
    LPTSTR lpszUserName;
    DWORD dwUserNameLength;
    LPTSTR lpszPassword;
    DWORD dwPasswordLength;
    LPTSTR lpszUrlPath;
    DWORD dwUrlPathLength;
    LPTSTR lpszExtraInfo;
    DWORD dwExtraInfoLength;
} URL_COMPONENTS, * LPURL_COMPONENTS;
Most of these parameters are self-explanatory or have already been discussed here. Let's take a closer look.
In this structure, Scheme is used to indicate the protocol type, with the following possible values:
typedef enum {
    INTERNET_SCHEME_PARTIAL = -2,
    INTERNET_SCHEME_UNKNOWN = -1,
    INTERNET_SCHEME_DEFAULT = 0,
    INTERNET_SCHEME_FTP,
    INTERNET_SCHEME_GOPHER,
    INTERNET_SCHEME_HTTP,
    INTERNET_SCHEME_HTTPS,
    INTERNET_SCHEME_FILE,
    INTERNET_SCHEME_NEWS,
    INTERNET_SCHEME_MAILTO,
    INTERNET_SCHEME_FIRST = INTERNET_SCHEME_FTP,
    INTERNET_SCHEME_LAST = INTERNET_SCHEME_MAILTO
} INTERNET_SCHEME, * LPINTERNET_SCHEME;
Although it is possible to supply the news or mailto values to the structure, WinInet does not yet have any functions to support those protocols. Only FTP, Gopher, HTTP, and HTTPS will do anything useful.
You should initialize the structure before using it; for example:
char scheme[100];
char host[255];
char path[255];
char extrainfo[255];
URL_COMPONENTS url_comp;
memset(&url_comp, 0, sizeof(url_comp));
url_comp.dwStructSize = sizeof(url_comp);
url_comp.lpszScheme = scheme;
url_comp.dwSchemeLength = 100;
url_comp.lpszHostName = host;
url_comp.dwHostNameLength = 255;
url_comp.lpszUrlPath = path;
url_comp.dwUrlPathLength = 255;
url_comp.lpszExtraInfo = extrainfo;
url_comp.dwExtraInfoLength = 255;
With the structure initialized, you're ready to crack the URL. From our Samp.exe application, we take a URL input by the user and print some of the fields in the Output text box. You can see the result in Figure 4.2.

Figure 4.2 : Some fields from a cracked URL.

BOOL bCrack = InternetCrackUrl(szTestUrl, 
     szTestUrl.GetLength(), ICU_ESCAPE, &url_comp);
CString szOutput;
     szOutput = szOutput + "Scheme: " + url_comp.lpszScheme + "\r\n" +
     "Hostname: " + url_comp.lpszHostName + "\r\n" +
     "UrlPath: " + url_comp.lpszUrlPath + "\r\n" +
     "Extra: " + url_comp.lpszExtraInfo;
SetDlgItemText(IDC_OUTPUT, szOutput);
The InternetCanonicalizeUrl function can be used to encode or decode the trailing characters in a URL, and to convert relative pathnames to full paths.
BOOL InternetCanonicalizeUrl(
    IN LPCTSTR lpszUrl,
    OUT LPTSTR lpszBuffer,
    IN OUT LPDWORD lpdwBufferLength,
    IN DWORD dwFlags);
By default, this function will convert "unsafe" characters (that is, characters outside of the a-Z and 0-9 sets) to escape sequences represented by the % and the ASCII character code, as documented in the official HTTP specifications. It also removes trailing whitespace, and converts relative paths to complete paths if possible. The flags available to alter this default behavior include the following:
  • ICU_DECODE converts the unsafe characters to %escape sequences.
  • ICU_NO_ENCODE does not convert unsafe characters.
  • ICU_NO_META does not convert relative paths to complete paths.
  • ICU_ENCODE_SPACES_ONLY does just what it says: encodes only spaces into %20.
  • ICU_BROWSER_MODE does not encode any characters after the first ? or #, and does not remove trailing whitespace. (This flag was not implemented in our version of wininet.h.)
Encoding is typically done by an HTTP server when it receives a CGI or ISAPI type of request that will be handed to a program on the server. Characters are encoded in order to prevent counterfeit parameters being passed to the gateway program. In the UNIX environment, for instance, the ! character could be interpreted as a shell-escape command. A user could follow that character with UNIX shell commands and possibly execute programs on the server. The various flags available with InternetCanonicalizeUrl give you full control on URLs-both incoming to your client program and outgoing from your client program.
InternetCreateUrl
The converse to cracking a URL is creating one with the InternetCreateUrl function. With this function you can first set whatever fields are needed in a URL components structure, and then construct a complete URL.
BOOL InternetCreateUrl( 
    IN LPURL_COMPONENTS lpUrlComponents,
    IN DWORD dwFlags,
    OUT LPSTR lpszUrl,
    IN OUT LPDWORD lpdwUrlLength);
As an example, the following code produces the output shown in Figure 4.3.

Figure 4.3 : A URL created with the ICU_ESCAPE flag.

     char * scheme = "gopher://";
     char * hostname = "ebt.dialup.access.net";
     char * path = "/foopath";
     char * extra = "!@#$@#$";
     INTERNET_SCHEME nScheme = INTERNET_SCHEME_GOPHER;
     INTERNET_PORT nPort = 70;
     URL_COMPONENTS url_comp;
     memset(&url_comp, 0, sizeof(url_comp));      
     url_comp.lpszScheme = scheme;
     url_comp.nScheme = nScheme;
     url_comp.lpszHostName = hostname;
     url_comp.nPort = nPort;
     url_comp.lpszUrlPath = path;
     url_comp.lpszExtraInfo = extra;
     url_comp.dwStructSize = sizeof(url_comp);      

     DWORD dwFlags=ICU_ESCAPE;
     CHAR lpszUrl[512];
     DWORD dwUrlLength=512;

     BOOL bCreate = InternetCreateUrl(&url_comp,
     dwFlags,lpszUrl,     &dwUrlLength);
InternetCombineUrl
Rounding out the URL function group is the InternetCombineUrl function, which allows you to supply the base and relative parts of a URL to create a whole URL.
BOOL InternetCombineUrl(
    IN LPCTSTR lpszBaseUrl,
    IN LPCTSTR lpszRelativeUrl,
    OUT LPTSTR lpszBuffer,
    IN OUT lpdwBufferLength,
    IN DWORD dwFlags);
For example:
     LPCTSTR lpszBaseUrl = "http://127.0.0.1";
     LPCTSTR lpszRelativeUrl = "../home/foo.htm/test?xxx yyy zzz";
     char lpszBuffer[128];
     DWORD dwBufLen = 128;
     DWORD dwFlags = ICU_NO_META;
     BOOL bCombine = InternetCombineUrl(lpszBaseUrl,
          lpszRelativeUrl,lpszBuffer,&dwBufLen,dwFlags);
This example will produce the following as output:
http://127.0.0.1/../home/foo.htm/test?xxx%20yyy%20zzz
Because we specified the ICU_NO_META flag, the /../ remains in the output.

Status Functions

InternetQueryOption and InternetSetOption are used to determine and set the state of various options that are available for an Internet handle.

InternetQueryOption
This function can be dropped in about any place that you have an Internet handle about which you need to get information.
BOOL InternetQueryOption(
    IN HINTERNET hInternet OPTIONAL,
    IN DWORD dwOption,
    OUT LPVOID lpBuffer OPTIONAL,
    IN OUT LPDWORD lpdwBufferLength);
There are several dozen options that you can query, including the various security options. The following code snippet shows an example of querying the INTERNET_HANDLE_TYPE and using the result to alter program flow:
     BOOL bQuery = InternetQueryOption(
     hConnection,dwOption,&dwBuffer,&dwBufLen);
     if(!bQuery) {TRACE("bQuery returned FALSE\n");return;}
     switch(dwBuffer)
     {
     case INTERNET_HANDLE_TYPE_CONNECT_HTTP:
          UpdateStatus("INTERNET_HANDLE_TYPE_CONNECT_HTTP");
     break;

     case INTERNET_HANDLE_TYPE_CONNECT_FTP:
          UpdateStatus("INTERNET_HANDLE_TYPE_CONNECT_FTP");
     break;
     // etc.
     default:
          UpdateStatus("Unknown handle type\n");
     }
InternetSetOption
The InternetSetOption function lets you set various options. A number of these options are related to time-out lengths, which by default are all infinite.
BOOL InternetSetOption(
    IN HINTERNET hInternetSession OPTIONAL,
    IN DWORD dwOption,
    IN LPVOID lpBuffer,
    IN DWORD dwBufferLength);
For example, the following code sets the connection time-out to be the number of milliseconds' delay before canceling an attempt to establish a connection:
DWORD dwOption = INTERNET_OPTION_CONNECT_TIME-OUT;
DWORD dwBuffer=23; 
DWORD dwBufLen; DWORD dwBuffer;
BOOL bSet = InternetSetOption(hConnection,dwOption,&dwBuffer,dwBufLen);
This sets the time-out to 5,000 milliseconds.
InternetQueryDataAvailable
The InternetQueryDataAvailable function can be used to determine if there is data available to be read on an open file handle.
BOOL InternetQueryDataAvailable( 
    IN HINTERNET hFile,
    OUT LPDWORD lpdwNumberOfBytesAvailable,
    IN DWORD dwFlags,
    IN DWORD dwContext);
This function will return False if the file is not available to read. Later in this chapter, in our Async.exe example, we use this function to determine when it is suitable to start reading from a Web page that is in the process of downloading. In that case, the function returns False, and GetLastError reports that "overlapped I/O is in progress" while the file is downloading; thus we could not read from the file. We found this behavior somewhat contrary to the purpose of an asynchronous file transfer. Nonetheless, once the function returns True, we were able to start reading from the file.
When InternetQueryDataAvailable returns True, it shows the number of bytes available. According to the WinInet documentation, if the function returns True and the number of bytes available is equal to zero, then the end of file has been reached. Again, we did not always find this to be the case when doing asynchronous transfers.

HTTP Functions

Opening an HTTP resource is a two-step process.

HttpOpenRequest
The first step is acquiring another handle with the HttpOpenRequest function.
HINTERNET HttpOpenRequest(
    IN HINTERNET hHttpSession,
    IN LPCTSTR lpszVerb,
    IN LPCTSTR lpszObjectName,
    IN LPCTSTR lpszVersion,
    IN LPCTSTR lpszReferer OPTIONAL,
    IN LPCTSTR FAR * lpszAcceptTypes OPTIONAL,
    IN DWORD dwFlags,
    IN DWORD dwContext);
With this function, you specify various options that will be sent to the HTTP server in the request headers. The Verb refers to the request method, typically GET or POST; the ObjectName corresponds to the file that is the target of the request. The Version is the HTTP level; you can leave this at the default of Null, which will be interpreted as HTTP/1.0. The Referer value can be used to specify the URL from which the current ObjectName was obtained. AcceptTypes refers to the MIME types that this client application can accept (the default MIME type is text/*). The Context is a value you can set and later use to refer to this particular request. The available Flags are the same as the flags associated with InternetOpenUrl.
Once you have acquired a handle to an HTTP request, you can proceed to start the download process with the next function, HttpSendRequest.
HttpSendRequest
After supplying the Internet handle, you set the parameters of HttpSendRequest. Add headers to the request with the Headers and HeadersLength variable. The other two parameters, Optional and OptionalLength, can be used to append other data to be sent along, usually for a POST type of request. For example:
BOOL HttpSendRequest(
    IN HINTERNET hHttpRequest,
    IN LPCTSTR lpszHeaders OPTIONAL,
    IN DWORD dwHeadersLength,
    IN LPVOID lpOptional OPTIONAL,
    DWORD dwOptionalLength);
HttpQueryInfo
HttpQueryInfo can be used to determine the status of a handle created with the HttpOpenRequest function:
BOOL HttpQueryInfo(
    IN HINTERNET hHttpRequest,
    IN DWORD dwInfoLevel,
    IN LPVOID lpvBuffer OPTIONAL,
    IN LPWORD lpvBufferLength,
    IN OUT LPWORD lpdwIndex OPTIONAL);
To use this function, you need to set the InfoLevel parameter to one of the acceptable flags. These range from HTTP_QUERY_MIME_VERSION to HTTP_QUERY_CONTENT_LENGTH, and many other values. Most of the flags correspond to HTTP response headers. Others, such as HTTP_ QUERY_STATUS_CODE, can be used to get the result of an HTTP response-for instance "200" for "OK", "404" for "File not Found", and so on. (One amusing flag is HTTP_QUERY_COST; we like to know who's paying what.)
In addition to specifying the query, you can modify the result with other various flags. For instance, adding the flag HTTP_QUERY_FLAG_REQUEST_HEADERS lets you look at the request headers.
HttpAddRequestHeaders
The last HTTP related function is HttpAddRequestHeaders.
BOOL HttpAddRequestHeaders(
    IN HINTERNET hHttpRequest,
    IN LPCTSTR lpszHeaders,
    IN DWORD dwHeadersLength,
    IN DWORD dwModifiers);
Although you can specify optional headers when calling the HttpSend-Request function, you may wish to add headers at some other point in the program; that's where HttpAddRequestHeaders comes in. With the Modifiers flag, you can also replace headers. Options for the Modifiers flag allow you to add and replace headers already in place, and to coalesce or combine headers of the same name into a single header.

FTP Functions

Most users are probably quite familiar with FTP client sessions on the command line in DOS or UNIX. The client connects to an FTP server, issues control commands on one channel, and receives (GETs) or posts (PUTs) data on another channel. The dual channel design enables the user to halt a potentially long-lasting data transfer with a Ctrl-C Break command on the control channel.

The FTP functions discussed here provide the equivalents of client-side FTP control commands. The typical maneuvering done by the FTP client in a command-line session might involve such commands as listed in Table 4.1. All of the sample functions described in the following paragraphs are used in the FTP client application in this chapter's "Sample Applications" section.

Table 4.1 Typical Command-Line FTP Commands
CommandDescription
binSpecifies binary file type.
Lcd <dir-name>Changes working directory on client machine.
cd <dir-name>Changes working directory on FTP server, if client has the appropriate permissions.
get <file-name>Fetches a file from the server to the client, of type ASCII or binary (ASCII is often the default with many FTP servers).
put <file-name>Opposite of get command. Client transfers a file from local machine to host FTP server in either ASCII or binary mode.
PwdDisplays the current working directory on the FTP host.
ByeEnds the FTP session.

FtpGetCurrentDirectory
This function is equivalent to typing pwd (shorthand for Print Working Directory) in an FTP session. It returns the current FTP server directory that the client happens to be in. For example, to start an FTP session to the machine www.acme.com you'd type cd pub and then pwd. This command will return /pub as the current working directory. Here's how the FtpGetCurrentDirectory function looks:
BOOL FtpGetCurrentDirectory(
    IN HINTERNET hFtpSession,
    OUT LPCTSTR lpszCurrentDirectory,
    IN OUT LPDWORD lpdwCurrentDirectory);
FtpSetCurrentDirectory
The FtpSetCurrentDirectory function provides the equivalent of the command-line cd <dir-name> function, where the user changes to a directory that is in the FTP server name space.
BOOL FtpSetCurrentDirectory(
    IN HINTERNET hFtpSession,
    IN LPCTSTR lpszDirectory);
FtpFindFirstFile
In an FTP session, it is quite common for the user to want a listing of files in the current working directory via the dir command. There is no equivalent WinInet function in this case. Instead, you will use the FTPFindFirstFile to get structured data, as follows:
HINTERNET FtpFindFirstFile(
    IN HINTERNET hFtpSession,
    IN LPCTSTR lpszSearchFile OPTIONAL,
    OUT LPWIN32_FIND_DATA lpFindFileData,
    IN DWORD dwFlags
    IN DWORD dwContext);
This function places the data returned-information on the first file or irectory found in the current directory-into the structure WIN32_FIND_DATA, as shown here:
typedef struct _WIN32_FIND_DATA { 
    DWORD dwFileAttributes; 
    FILETIME ftCreationTime; 
    FILETIME ftLastAccessTime; 
    FILETIME ftLastWriteTime; 
    DWORD    nFileSizeHigh; 
    DWORD    nFileSizeLow; 
    DWORD    dwReserved0; 
    DWORD    dwReserved1; 
    TCHAR    cFileName[ MAX_PATH ]; 
    TCHAR    cAlternateFileName[ 14 ]; 
} WIN32_FIND_DATA; 
After finding the first file, you can then get at the rest of the files or directories using the function InternetFindNextFile.
InternetFindNextFile
This function gets the next file within a directory. By setting up a loop, you can continue to list all of the files and directories until the function returns False.
BOOL InternetFindNextFile(
    IN HINTERNET hFind,
    OUT LPVOID lpvFindData);
FtpOpenFile
Prior to transferring a file between the FTP server and the local client machine, you must create an Internet handle to the file, with the FtpOpenFile function.
HINTERNET FtpOpenFile(
    IN HINTERNET hFtpSession,
    IN LPCTSTR lpszFileName,
    IN DWORD fdwAccess,
    IN DWORD dwFlags,
    IN DWORD dwContext);
Many FTP servers by default set the file transfer type to ASCII, which can corrupt binary files during a transfer. You can set the flags in FtpOpenFile to either FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY to set the appropriate transfer type. You can also specify the transfer type at the time of transfer, when calling either of the next two functions (FtpGetFile and FtpPutFile).
FtpGetFile
On the command line, the user will type get <file-name> to retrieve a file from the FTP server. However, it is sometimes necessary to precede this command with bin to specify binary data transfer (to avoid ASCII file transfer with a binary file, which won't work). The FTPGetFile function is the equivalent of the get command, and it also handles the file type (ASCII or binary).
BOOL FtpGetFile(
    IN HINTERNET hFtpSession,
    IN LPCTSTR lpszRemoteFile,
    IN LPCTSTR lpszNewFile,
    IN BOOL fFailIfExists,
    IN DWORD dwFlagsAndAttributes,
    IN DWORD dwFlags,
    IN DWORD dwContext);
FtpPutFile
The opposite of the command-line get is the command-line put. In this case a user wants to send a file from the current working directory on the client machine to the FTP server. As in the case of get, the user must be careful on the command line not to send binary data when the FTP server is expecting ASCII, and must specify bin for binary file transfer. The FtpPutFile function provides the put equivalent, and accepts parameters to distinguish binary from ASCII data.
BOOL FtpPutFile(
    IN HINTERNET hFtpSession,
    IN LPCTSTR lpszLocalFile,
    IN LPCTSTR lpszNewRemoteFile,
    IN DWORD dwFlags,
    IN DWORD dwContext);

Sample Applications

This section provides the source code for two sample applications developed with the WinInet API. The first is an FTP client. The second uses the asynchronous transfer mode to display raw HTML from a user-specified Web page.

FTP Client

Our sample FTP client application uses the synchronous mode to retrieve directory listings from a remote server, placing the data into a tree control. This application illustrates what is usually one of the most difficult (or simply tedious) aspects of programming with Internet protocols: parsing the output data. The data in an FTP session is limited to character output that's basically handed to you in a fixed format. On the other hand, consider what goes into a full-featured Web browser, with the requirement to parse HTML tags. That should give you an appreciation for the amount of effort it takes to write a Web browser compatible with even HTML 2.0.

On starting up the application, you will get the screen illustrated in Figure 4.4, with the tree on the left of the screen displaying files in the current directory. Figure 4.5 shows the Connect dialog in the application. And finally, Figure 4.6 shows the FTP client after a successful log-on to a remote server.

Figure 4.4 : FTP client on startup.

Figure 4.5 : FTP client Connect dialog.

Figure 4.6 : FTP client after log-on.


NOTE
This application is not meant to be an industrial-strength FTP client. In developing this example, we focused on using the WinInet API functions and didn't include much in the way of error checking for various user actions.

Listings 4.2 and 4.3 are the source code for the files in the FTP client application.


Listing 4.2 FTPDlg.cpp
// FTPDlg.cpp : implementation file
//

#include "stdafx.h"
#include "wininet.h"
#include "FTP.h"
#include "FTPDlg.h"
#include <string.h>
#include "login.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
     CAboutDlg();

// Dialog Data
     //{{AFX_DATA(CAboutDlg)
     enum { IDD = IDD_ABOUTBOX };
     //}}AFX_DATA

     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CAboutDlg)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
     //}}AFX_VIRTUAL

// Implementation
protected:
     //{{AFX_MSG(CAboutDlg)
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
     //{{AFX_DATA_INIT(CAboutDlg)
     //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CAboutDlg)
     //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
     //{{AFX_MSG_MAP(CAboutDlg)
          // No message handlers
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFTPDlg dialog

CFTPDlg::CFTPDlg(CWnd* pParent /*=NULL*/)
     : CDialog(CFTPDlg::IDD, pParent)
{
     //{{AFX_DATA_INIT(CFTPDlg)
     //}}AFX_DATA_INIT
     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
     //init the current path. this is the place where everything starts
}

void CFTPDlg::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CFTPDlg)
     DDX_Control(pDX, IDC_TREETHERE, m_There);
     DDX_Control(pDX, IDC_TREEHERE, m_Here);
     //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CFTPDlg, CDialog)
     //{{AFX_MSG_MAP(CFTPDlg)
     ON_WM_SYSCOMMAND()
     ON_WM_PAINT()
     ON_WM_QUERYDRAGICON()
     ON_NOTIFY(NM_DBLCLK, IDC_TREEHERE, OnDblclkTreehere)
     ON_NOTIFY(NM_DBLCLK, IDC_TREETHERE, OnDblclkTreethere)
     ON_BN_CLICKED(IDC_BUTTONTOHERE, OnButtontohere)
     ON_BN_CLICKED(IDC_BUTTONTOTHERE, OnButtontothere)
     ON_BN_CLICKED(IDC_THEREDELETE, OnTheredelete)
     ON_BN_CLICKED(IDC_THEREMKDIR, OnTheremkdir)
     ON_BN_CLICKED(IDC_THERERENAME, OnThererename)
     ON_BN_CLICKED(IDC_HEREDELETE, OnHeredelete)
     ON_BN_CLICKED(IDC_HEREMKDIR, OnHeremkdir)
     ON_BN_CLICKED(IDC_HERERENAME, OnHererename)
     ON_BN_CLICKED(IDC_BUTTONCONNECT, OnButtonconnect)
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFTPDlg message handlers

BOOL CFTPDlg::OnInitDialog()
{
     CDialog::OnInitDialog();

     // Add "About..." menu item to system menu.

     // IDM_ABOUTBOX must be in the system command range.
     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
     ASSERT(IDM_ABOUTBOX < 0xF000);

     CMenu* pSysMenu = GetSystemMenu(FALSE);
     CString strAboutMenu;
     strAboutMenu.LoadString(IDS_ABOUTBOX);
     if (!strAboutMenu.IsEmpty())
     {
          pSysMenu->AppendMenu(MF_SEPARATOR);
          pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
     }

     // Set the icon for this dialog. The framework does this automatically
     //  when the application's main window is not a dialog
     SetIcon(m_hIcon, TRUE);               // Set big icon
     SetIcon(m_hIcon, FALSE);          // Set small icon

     ilist.Create(16,13,0,6,10);
     fileB.LoadBitmap(IDB_FILE);
     ilist.Add(&fileB,(CBitmap*)NULL);
     openB.LoadBitmap(IDB_OPENF);
     ilist.Add(&openB,(CBitmap*)NULL);
     closeB.LoadBitmap(IDB_CLOSEF);
     ilist.Add(&closeB,(CBitmap*)NULL);
     m_Here.SetImageList(&ilist,TVSIL_NORMAL);
     m_There.SetImageList(&ilist,TVSIL_NORMAL);

     FillHere();
     // TODO: Add extra initialization here
     return TRUE;  // return TRUE  unless you set the focus to a control
}

void CFTPDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
     if ((nID & 0xFFF0) == IDM_ABOUTBOX)
     {
          CAboutDlg dlgAbout;
          dlgAbout.DoModal();
     }
     else
     {
          CDialog::OnSysCommand(nID, lParam);
     }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon. For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CFTPDlg::OnPaint() 
{
     if (IsIconic())
     {
          CPaintDC dc(this); // device context for painting

          SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

          // Center icon in client rectangle
          int cxIcon = GetSystemMetrics(SM_CXICON);
          int cyIcon = GetSystemMetrics(SM_CYICON);
          CRect rect;
          GetClientRect(&rect);
          int x = (rect.Width() - cxIcon + 1) / 2;
          int y = (rect.Height() - cyIcon + 1) / 2;

          // Draw the icon
          dc.DrawIcon(x, y, m_hIcon);
     }
     else
     {
          CDialog::OnPaint();
     }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CFTPDlg::OnQueryDragIcon()
{
     return (HCURSOR) m_hIcon;
}

void CFTPDlg::FillThere(){
     TCHAR path[MAX_PATH];
     HTREEITEM root;
     TV_INSERTSTRUCT TreeCtrlItem;
     ULONG plength=MAX_PATH;
     CString name,pass,host;
     CLogin login(this,&name,&pass,&host);
     if(login.DoModal()==IDOK){
          iconnection=InternetOpen("FTP",INTERNET_OPEN_TYPE_DIRECT,
          NULL,NULL,INTERNET_FLAG_ASYNC);
          iftp= InternetConnect(
          iconnection,(LPCTSTR)host,INTERNET_DEFAULT_FTP_PORT,
          (LPCTSTR)name,(LPCTSTR)pass,INTERNET_SERVICE_FTP,0,1);
          FtpGetCurrentDirectory(iftp,path,&plength);
          char *token;
          char breaks[]="/";
          TreeCtrlItem.hParent = TVI_ROOT;
          TreeCtrlItem.hInsertAfter = TVI_LAST;
          TreeCtrlItem.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_STATE;
          TreeCtrlItem.item.state=TVIS_EXPANDED ;
          TreeCtrlItem.item.stateMask=TVIS_EXPANDED ;
          TreeCtrlItem.item.iImage=2;
          TreeCtrlItem.item.iSelectedImage=1;
          TreeCtrlItem.item.lParam = 0; //this is the dir
          token=strtok(path,breaks);     
          while(token!=NULL){
               TreeCtrlItem.item.pszText = token;
               root=m_There.InsertItem(&TreeCtrlItem);
               TreeCtrlItem.hParent = root;
               token=strtok(NULL,breaks);     
          }
          for(ULONG i=0;i<plength;i++){
               if(path[i]=='\0')
                    path[i]='/';
          }
          path[plength]='/';
          path[plength+1]='*';
          path[plength+2]='.';
          path[plength+3]='*';
          path[plength+4]='\0';
          paththere=path;
          FillThereCurrent(path,root);
     }
}

void CFTPDlg::FillThereCurrent(const TCHAR *path,HTREEITEM root){
     WIN32_FIND_DATA data;
     BOOL result=1,selected=0;
     HINTERNET ifile;
     HTREEITEM item,child1,child2,sel=NULL;
     TV_INSERTSTRUCT TreeCtrlItem;
     child1=m_There.GetNextItem(root,TVGN_CHILD);
     while(child1!=NULL){
          child2=m_There.GetNextSiblingItem(child1);
          m_There.DeleteItem(child1);
          child1=child2;
     }
     TreeCtrlItem.hParent = TVI_ROOT;
     TreeCtrlItem.hInsertAfter = TVI_LAST;
     TreeCtrlItem.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_STATE;
     TreeCtrlItem.item.state=0;
     TreeCtrlItem.item.stateMask=0;
     TreeCtrlItem.item.iImage=2;
     TreeCtrlItem.item.iSelectedImage=1;
     TreeCtrlItem.item.lParam = 0; //this is the dir

     ifile=FtpFindFirstFile(iftp,path,&data,INTERNET_FLAG_RAW_DATA,0);
     if(ifile==NULL)
          return;
     while(result){
          TreeCtrlItem.hParent = root;
          TreeCtrlItem.item.pszText = data.cFileName;
          if((data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)==FILE_ATTRIBUTE_DIRECTORY){
               TreeCtrlItem.item.iImage=1;
               TreeCtrlItem.item.iSelectedImage=2;
               TreeCtrlItem.item.lParam = 0; //this is the dir
               item=m_There.InsertItem(&TreeCtrlItem);
          }else{
               TreeCtrlItem.item.iImage=0;
               TreeCtrlItem.item.iSelectedImage=0;
               TreeCtrlItem.item.lParam = 1; //this is a file
               item=m_There.InsertItem(&TreeCtrlItem);
          }
          if(selected==0){
               selected=1;
               sel=item;
          }
          result=InternetFindNextFile(ifile,&data);
     }
     if((m_There.GetSelectedItem()==NULL) && (sel!=NULL))
          m_There.SelectItem(sel);
     InternetCloseHandle(ifile);
}

void CFTPDlg::FillHere(){
     BOOL result=1;
     HTREEITEM root;
     TV_INSERTSTRUCT TreeCtrlItem;
     TCHAR path[MAX_PATH];
     char *token;
     char breaks[]="\\\0";

     pathlength=GetCurrentDirectory(MAX_PATH-4,path);

     TreeCtrlItem.hParent = TVI_ROOT;
     TreeCtrlItem.hInsertAfter = TVI_LAST;
     TreeCtrlItem.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_STATE;
     TreeCtrlItem.item.state=TVIS_EXPANDED;
     TreeCtrlItem.item.stateMask=TVIS_EXPANDED;
     TreeCtrlItem.item.iImage=2;
     TreeCtrlItem.item.iSelectedImage=1;
     TreeCtrlItem.item.lParam = 0; //this is the dir
     token=strtok(path,breaks);     
     while(token!=NULL){
          TreeCtrlItem.item.pszText = token;
          root=m_Here.InsertItem(&TreeCtrlItem);
          TreeCtrlItem.hParent = root;
          token=strtok(NULL,breaks);     
     }
     for(int i=0;i<pathlength;i++){
          if(path[i]=='\0')
               path[i]='\\';
     }
     path[pathlength]='\\';
     path[pathlength+1]='*';
     path[pathlength+2]='.';
     path[pathlength+3]='*';
     path[pathlength+4]='\0';
     pathhere=path;
     FillHereCurrent(path,root);
}

void CFTPDlg::FillHereCurrent(const TCHAR *path,HTREEITEM root){
     HANDLE file;
     WIN32_FIND_DATA data;
     BOOL result=1,selected=0;
     HTREEITEM item,child1,child2,sel=NULL;
     TV_INSERTSTRUCT TreeCtrlItem;
     child1=m_Here.GetNextItem(root,TVGN_CHILD);
     while(child1!=NULL){
          child2=m_Here.GetNextSiblingItem(child1);
          m_Here.DeleteItem(child1);
          child1=child2;
     }
     TreeCtrlItem.hParent = TVI_ROOT;
     TreeCtrlItem.hInsertAfter = TVI_LAST;
     TreeCtrlItem.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_STATE;
     TreeCtrlItem.item.state=0;
     TreeCtrlItem.item.stateMask=0;
     TreeCtrlItem.item.iImage=2;
     TreeCtrlItem.item.iSelectedImage=1;
     TreeCtrlItem.item.lParam = 0; //this is the dir

     file=FindFirstFile(path,&data);
     FindNextFile(file,&data);
     while(FindNextFile(file,&data)){
          TreeCtrlItem.hParent = root;
          TreeCtrlItem.item.pszText = data.cFileName;
          if((data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)==FILE_ATTRIBUTE_DIRECTORY){
               TreeCtrlItem.item.iImage=1;
               TreeCtrlItem.item.iSelectedImage=2;
               TreeCtrlItem.item.lParam = 0; //this is the dir
               item=m_Here.InsertItem(&TreeCtrlItem);
          }else{
               TreeCtrlItem.item.iImage=0;
               TreeCtrlItem.item.iSelectedImage=0;
               TreeCtrlItem.item.lParam = 1; //this is a file
               item=m_Here.InsertItem(&TreeCtrlItem);
          }
          if(selected==0){
               selected=1;
               sel=item;
          }
     }
     if((m_Here.GetSelectedItem()==NULL) && (sel!=NULL))
          m_Here.SelectItem(sel);
     FindClose(file);
}

void CFTPDlg::OnDblclkTreehere(NMHDR* pNMHDR, LRESULT* pResult) 
{
     // TODO: Add your control notification handler code here
     HTREEITEM item,selitem;
     //if((*pResult)==0){
     pathhere="";
     item=m_Here.GetSelectedItem();
     if(m_Here.GetItemData(item)==0){
          if((m_Here.GetItemState(item, TVIS_EXPANDED )&TVIS_EXPANDED)!=TVIS_EXPANDED){
               m_Here.SetItemImage(item, 2, 1);
               selitem=item;
               while(item!=NULL){
                    pathhere=m_Here.GetItemText(item)+'\\'+pathhere;
                    item=m_Here.GetParentItem(item);
               }
               pathhere+="*.*";
               FillHereCurrent((LPCTSTR)pathhere,selitem);
          }else{
               m_Here.SetItemImage(item, 1,2);
          }
     }
     *pResult = 0;
}



BOOL CFTPDlg::DestroyWindow() 
{
     // TODO: Add your specialized code here and/or call the base class
     InternetCloseHandle(iftp);
     InternetCloseHandle(iconnection);
     return CDialog::DestroyWindow();
}

void CFTPDlg::OnDblclkTreethere(NMHDR* pNMHDR, LRESULT* pResult) 
{
     // TODO: Add your control notification handler code here
     HTREEITEM item,selitem;
     paththere="";
     item=m_There.GetSelectedItem();
     if(m_There.GetItemData(item)==0){
          if((m_There.GetItemState(item, TVIS_EXPANDED )&TVIS_EXPANDED)!=TVIS_EXPANDED){
               m_There.SetItemImage(item, 2, 1);
               selitem=item;
               while(item!=NULL){
                    paththere=m_There.GetItemText(item)+'/'+paththere;
                    item=m_There.GetParentItem(item);
               }
               paththere+="*.*";
               paththere='/'+paththere;
               FillThereCurrent((LPCTSTR)paththere,selitem);
          }else{
               m_There.SetItemImage(item, 1,2);
          }
     }
     *pResult = 0;
}

void CFTPDlg::OnButtontohere() {
     CString paththere,pathhere,file;
     HTREEITEM item;
     if(iftp==NULL) {
//          DispError(1);
          return;
     }
     paththere=getPathThere();
     pathhere=getPathHere();
     file=m_There.GetItemText((m_There.GetSelectedItem()));
     FtpGetFile(iftp,(LPCTSTR)(paththere+file),(LPCTSTR)(pathhere+file),
	      0,CREATE_ALWAYS,FTP_TRANSFER_TYPE_BINARY,0);
     item=m_Here.GetSelectedItem();
     if(m_Here.GetItemData(item)==1)//this is a file and we need a dir
          item=m_Here.GetParentItem(item);
     FillHereCurrent((LPCTSTR)(pathhere+"*.*"),item);
}

void CFTPDlg::OnButtontothere() {
     CString paththere,pathhere,file;
     HTREEITEM item;
     if(iftp==NULL) {
//          DispError(1);
          return;
     }
     paththere=getPathThere();
     pathhere=getPathHere();
     file=m_Here.GetItemText((m_Here.GetSelectedItem()));
     FtpPutFile(iftp,(LPCTSTR)(pathhere+file),(LPCTSTR)(paththere+file),
	      FTP_TRANSFER_TYPE_BINARY,0);
     item=m_There.GetSelectedItem();
     if(m_There.GetItemData(item)==1)//this is a file and we need a dir
          item=m_There.GetParentItem(item);
     FillThereCurrent((LPCTSTR)(paththere+"*.*"),item);
}

CString CFTPDlg::getPathHere(){
     HTREEITEM item,selitem;
     CString pathhere="";
     item=m_Here.GetSelectedItem();
     if(item==NULL)
          return pathhere;
     if(m_Here.GetItemData(item)==1)//this is a file and we need a dir
          item=m_Here.GetParentItem(item);
     if((item!=NULL)&&(m_Here.GetItemData(item)==0)){//ok this is a file
          selitem=item;
          pathhere=m_Here.GetItemText(item);
          while((item=m_Here.GetParentItem(item))!=NULL){
               pathhere=m_Here.GetItemText(item)+'\\'+pathhere;
          }
          pathhere+='\\';
     }
     return pathhere;
}

CString CFTPDlg::getPathThere(){
     HTREEITEM item,selitem;
     CString paththere="";
     item=m_There.GetSelectedItem();
     if(item==NULL)
          return paththere;
     if(m_There.GetItemData(item)==1)
          item=m_There.GetParentItem(item);
     if((item!=NULL)&&(m_There.GetItemData(item)==0)){//ok this is a file
          selitem=item;
          paththere=m_There.GetItemText(item);
          while((item=m_There.GetParentItem(item))!=NULL){
               paththere=m_There.GetItemText(item)+'/'+paththere;
          }
          paththere='/'+paththere+'/';
     }
     return paththere;
}


void CFTPDlg::OnTheredelete() {
     HTREEITEM item=m_There.GetSelectedItem(),child1,child2,parent;
     parent=m_There.GetParentItem(item);
     CString paththere=getPathThere();
     if(m_There.GetItemData(item)==0){
          child1=m_There.GetNextItem(item,TVGN_CHILD);
          while(child1!=NULL){
               FtpDeleteFile(iftp,(LPCTSTR)(paththere+m_There.GetItemText(child1)));
               child2=m_Here.GetNextSiblingItem(child1);
               m_Here.DeleteItem(child1);
               child1=child2;
          }
          FtpRemoveDirectory(iftp,(LPCTSTR)(paththere));
     }else{
          FtpDeleteFile(iftp,(LPCTSTR)(paththere+m_There.GetItemText(item)));
     }
     item=m_There.GetSelectedItem();
     if(m_There.GetItemData(item)==1)//this is a file and we need a dir
          item=m_There.GetParentItem(item);
     m_There.SelectItem(parent);
     FillThereCurrent((LPCTSTR)(getPathThere()+"*.*"),parent);
}

void CFTPDlg::OnTheremkdir() {
     CString ndir;
     NewDir newdir(this,&ndir);
     if(newdir.DoModal()==IDOK){
          FtpCreateDirectory(iftp,(LPCTSTR)(getPathThere()+ndir));
          HTREEITEM item;
          item=m_There.GetSelectedItem();
          if(m_There.GetItemData(item)==1)//this is a file and we need a dir
               item=m_There.GetParentItem(item);
          FillThereCurrent((LPCTSTR)(getPathThere()+"*.*"),item);
     }
}

void CFTPDlg::OnThererename() 
{
     HTREEITEM item=m_There.GetSelectedItem();
     if(m_There.GetItemData(item)==0){
          MessageBox("Can not rename directories");
          return;
     }
     CString nname,paththere,file;
     file=m_There.GetItemText(m_There.GetSelectedItem());
     Rename rename(this,&nname);
     if(rename.DoModal()==IDOK){
          paththere=getPathThere();
          FtpRenameFile(iftp,(LPCTSTR)(paththere+file),(LPCTSTR)(paththere+nname));
          if(m_There.GetItemData(item)==1)//this is a file and we need a dir
               item=m_There.GetParentItem(item);
          FillThereCurrent((LPCTSTR)(getPathThere()+"*.*"),item);
     }
}


void CFTPDlg::OnHeredelete() {
     HTREEITEM item=m_Here.GetSelectedItem(),child1,child2,parent;
     parent=m_Here.GetParentItem(item);
     CString pathhere=getPathHere();
     if(m_Here.GetItemData(item)==0){
          child1=m_Here.GetNextItem(item,TVGN_CHILD);
          while(child1!=NULL){
               DeleteFile((LPCTSTR)(paththere+m_Here.GetItemText(child1)));
               child2=m_Here.GetNextSiblingItem(child1);
               m_Here.DeleteItem(child1);
               child1=child2;
          }
          RemoveDirectory((LPCTSTR)(pathhere));
     }else{
          DeleteFile((LPCTSTR)(pathhere+m_Here.GetItemText(item)));
     }
     item=m_Here.GetSelectedItem();
     if(m_Here.GetItemData(item)==1)//this is a file and we need a dir
          item=m_Here.GetParentItem(item);
     m_Here.SelectItem(parent);
     FillHereCurrent((LPCTSTR)(getPathHere()+"*.*"),parent);
}

void CFTPDlg::OnHeremkdir() {
     CString ndir;
     NewDir newdir(this,&ndir);
     if(newdir.DoModal()==IDOK){
          CreateDirectory((LPCTSTR)(getPathHere()+ndir),NULL);
          HTREEITEM item;
          item=m_Here.GetSelectedItem();
          if(m_Here.GetItemData(item)==1)//this is a file and we need a dir
               item=m_Here.GetParentItem(item);
          FillHereCurrent((LPCTSTR)(getPathHere()+"*.*"),item);
     }
}

void CFTPDlg::OnHererename() {
     HTREEITEM item=m_Here.GetSelectedItem();
     if(m_Here.GetItemData(item)==0){
          MessageBox("Can not rename directories");
          return;
     }
     CString nname,pathhere,file;
     file=m_Here.GetItemText(m_Here.GetSelectedItem());
     Rename rename(this,&nname);
     if(rename.DoModal()==IDOK){
          pathhere=getPathHere();
          MoveFile((LPCTSTR)(pathhere+file),(LPCTSTR)(pathhere+nname));
          if(m_Here.GetItemData(item)==1)//this is a file and we need a dir
               item=m_Here.GetParentItem(item);
          FillHereCurrent((LPCTSTR)(getPathHere()+"*.*"),item);
     }
}

void CFTPDlg::OnButtonconnect() {
     FillThere();
}


Listing 4.3 FTPDlg.h
// FTPDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CFTPDlg dialog
//{{AFX_INCLUDES()
//}}AFX_INCLUDES
class CFTPDlg : public CDialog
{
// Construction
public:
     CFTPDlg(CWnd* pParent = NULL);     // standard constructor

// Dialog Data
     //{{AFX_DATA(CFTPDlg)
     enum { IDD = IDD_FTP_DIALOG };
     CTreeCtrl     m_There;
     CTreeCtrl     m_Here;
     //}}AFX_DATA

     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CFTPDlg)
     public:
     virtual BOOL DestroyWindow();
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);     // DDX/DDV support
     //}}AFX_VIRTUAL

// Implementation
protected:
     HICON m_hIcon;
     HINTERNET iconnection,iftp;
     void FillHere();
     void FillThere();
     void FillHereCurrent(const TCHAR*,HTREEITEM);
     void FillThereCurrent(const TCHAR*,HTREEITEM);
     int pathlength;
     CTreeCtrl *here,*there;
     CString paththere,pathhere;
     CImageList ilist;
     CBitmap openB;
     CBitmap closeB;
     CBitmap fileB;
     CString getPathHere();
     CString getPathThere();

     // Generated message map functions
     //{{AFX_MSG(CFTPDlg)
     virtual BOOL OnInitDialog();
     afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
     afx_msg void OnPaint();
     afx_msg HCURSOR OnQueryDragIcon();
     afx_msg void OnDblclkTreehere(NMHDR* pNMHDR, LRESULT* pResult);
     afx_msg void OnDblclkTreethere(NMHDR* pNMHDR, LRESULT* pResult);
     afx_msg void OnButtontohere();
     afx_msg void OnButtontothere();
     afx_msg void OnTheredelete();
     afx_msg void OnTheremkdir();
     afx_msg void OnThererename();
     afx_msg void OnHeredelete();
     afx_msg void OnHeremkdir();
     afx_msg void OnHererename();
     afx_msg void OnButtonconnect();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

The other two source files for this application that we will show here include functions for the various FTP-related menu options, such as renaming or deleting files and making directories. These two files are in Listings 4.4 and 4.5. The remaining files in the VC++ project are all the default AppWizard-generated files.


Listing 4.4 Login.cpp
// Login.cpp : implementation file
//

#include "stdafx.h"
#include "FTP.h"
#include "Login.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CLogin dialog


CLogin::CLogin(CWnd* pParent,CString *n,CString *p,CString *h)
     : CDialog(CLogin::IDD, pParent)
{
     //{{AFX_DATA_INIT(CLogin)
     m_Host = _T("");
     m_UName = _T("");
     m_Pass = _T("");
     //}}AFX_DATA_INIT
     name=n;
     pass=p;
     host=h;
}


void CLogin::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CLogin)
     DDX_Text(pDX, IDC_EDITHOST, m_Host);
     DDX_Text(pDX, IDC_EDITPAS, m_Pass);
     DDX_Text(pDX, IDC_EDITUNAME, m_UName);
     //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CLogin, CDialog)
     //{{AFX_MSG_MAP(CLogin)
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLogin message handlers
/////////////////////////////////////////////////////////////////////////////
// NewDir dialog


NewDir::NewDir(CWnd* pParent,CString *dir)
     : CDialog(NewDir::IDD, pParent)
{
     ndir=dir;
     //{{AFX_DATA_INIT(NewDir)
     m_Dir = _T("");
     //}}AFX_DATA_INIT
}


void NewDir::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(NewDir)
     DDX_Text(pDX, IDC_EDIT1, m_Dir);
     //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(NewDir, CDialog)
     //{{AFX_MSG_MAP(NewDir)
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// NewDir message handlers
/////////////////////////////////////////////////////////////////////////////
// Rename dialog


Rename::Rename(CWnd* pParent,CString *rename)
     : CDialog(Rename::IDD, pParent)
{
     nname=rename;
     //{{AFX_DATA_INIT(Rename)
     m_Rename = _T("");
     //}}AFX_DATA_INIT
}


void Rename::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(Rename)
     DDX_Text(pDX, IDC_EDIT1, m_Rename);
     //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(Rename, CDialog)
     //{{AFX_MSG_MAP(Rename)
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// Rename message handlers

void CLogin::OnOK() 
{
     UpdateData(1);
     *name=m_UName;
     *pass=m_Pass;
     *host=m_Host;
     CDialog::OnOK();
}

void NewDir::OnOK() 
{
     UpdateData(1);
     (*ndir)=m_Dir;
     // TODO: Add extra validation here
     CDialog::OnOK();
}

void Rename::OnOK() 
{
     UpdateData(1);
     (*nname)=m_Rename;
     // TODO: Add extra validation here
     CDialog::OnOK();
}


Listing 4.5 Login.h
// Login.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CLogin dialog

class CLogin : public CDialog
{
// Construction
public:
     CLogin(CWnd* pParent,CString*,CString*,CString*);   // standard constructor

// Dialog Data
     //{{AFX_DATA(CLogin)
     enum { IDD = IDD_DIALOGLOGIN };
     CString     m_Host;
     CString     m_UName;
     CString     m_Pass;
     //}}AFX_DATA

     CString *name,*pass,*host;
// Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CLogin)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
     //}}AFX_VIRTUAL

// Implementation
protected:

     // Generated message map functions
     //{{AFX_MSG(CLogin)
     virtual void OnOK();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
// NewDir dialog

class NewDir : public CDialog
{
// Construction
public:
     NewDir(CWnd* pParent,CString*);   // standard constructor
     CString *ndir;
// Dialog Data
     //{{AFX_DATA(NewDir)
     enum { IDD = IDD_DIALOGNEWDIR };
     CString     m_Dir;
     //}}AFX_DATA


// Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(NewDir)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
     //}}AFX_VIRTUAL

// Implementation
protected:

     // Generated message map functions
     //{{AFX_MSG(NewDir)
     virtual void OnOK();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
// Rename dialog

class Rename : public CDialog
{
// Construction
public:
     Rename(CWnd* pParent,CString*);   // standard constructor
     CString *nname;
// Dialog Data
     //{{AFX_DATA(Rename)
     enum { IDD = IDD_DIALOGRENAME };
     CString     m_Rename;
     //}}AFX_DATA


// Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(Rename)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
     //}}AFX_VIRTUAL

// Implementation
protected:

     // Generated message map functions
     //{{AFX_MSG(Rename)
     virtual void OnOK();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

HTML Dump

Our second sample application is an example of using the asynchronous method of transfer. This is a dialog-based application, in which the user can input an HTTP reference and the application will attempt to find the specified page and display the first chunk of HTML data in the display window. The progress of the transaction is displayed in a status line at the bottom of the screen while the transaction takes place. Figure 4.7 shows a sample screen after retrieving the Microsoft home page.

Figure 4.7 : HTML data from http://www.microsoft.com/.

To use the asynchronous method, you need to do several things which you can see in our source code: 1) set the flag INTERNET_FLAG_ASYNC on the Internet handle (and its children), that you wish to use asynch-ronously; 2) set the Context value for the handle to greater than zero; 3) establish the OnStatusCallback function. In our program, items 1 and 2 are accomplished when we call the InternetOpen function. For the callback function we created a separate class, CSession which is derived from CWinApp and is used to hold not only the callback function but the function which opens our Internet connection.

Having created a callback function, the next thing to do is develop a method for interacting with it. While it is simple enough to create the callback function, you should keep in mind that this function executes in its own thread. This means that you cannot simply start up a file transfer with, for instance HttpSendRequest, and then wait for the callback function to return information.

For example, if we called HttpSendRequest, and then in the next program statement issued some command that depended on the status information returned by the callback function, we would never get reliable information. Since the callback is in its own thread, that next program statement issued will start executing immediately. Either the callback function will never return, or the program will not get to that next statement, depending on how you code things.

The solution then is to try to get messages out of the callback function that will trigger some other function in the program. In our example we created an OnNotify type of function, called OnMyNotify. In the OnStatusCallback function we check the Status parameter and when it is INTERNET_STATUS_REQUEST_COMPLETE we use the PostMessage function to send a message to the dialog application window. This message triggers the OnMyNotify function which in turn calls our ReadHttpFile function at the right time. The ReadHttpFile function then retrieves the first chunk of data available and displays it in a text box control in the dialog application window.

Listing 4.6 shows the source code for AsyncDlg.cpp.


Listing 4.6 AsyncDlg.cpp.
// AsyncDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Async.h"
#include "AsyncDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define WM_MYNOTIFY   (WM_USER + 1)


HWND CSession::myDialog=NULL;


/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
     CAboutDlg();

// Dialog Data
     //{{AFX_DATA(CAboutDlg)
     enum { IDD = IDD_ABOUTBOX };
     //}}AFX_DATA

     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CAboutDlg)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
     //}}AFX_VIRTUAL

// Implementation
protected:
     //{{AFX_MSG(CAboutDlg)
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
     //{{AFX_DATA_INIT(CAboutDlg)
     //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CAboutDlg)
     //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
     //{{AFX_MSG_MAP(CAboutDlg)
          // No message handlers
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAsyncDlg dialog

CAsyncDlg::CAsyncDlg(CWnd* pParent /*=NULL*/)
     : CDialog(CAsyncDlg::IDD, pParent)
{
     //{{AFX_DATA_INIT(CAsyncDlg)
     m_url = _T("");
     //}}AFX_DATA_INIT
     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

     
}

void CAsyncDlg::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CAsyncDlg)
     DDX_Text(pDX, IDC_URL, m_url);
     //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAsyncDlg, CDialog)
     //{{AFX_MSG_MAP(CAsyncDlg)
     ON_WM_SYSCOMMAND()
     ON_WM_PAINT()
     ON_WM_QUERYDRAGICON()
     ON_BN_CLICKED(IDC_LOADPAGE, OnLoadpage)
    ON_MESSAGE(WM_MYNOTIFY, OnMyNotify)
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

     
//////////////////////////////////////////////////////////////////////////
// CAsyncDlg message handlers

BOOL CAsyncDlg::OnInitDialog()
{
     CDialog::OnInitDialog();

     // Add "About..." menu item to system menu.

     // IDM_ABOUTBOX must be in the system command range.
     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
     ASSERT(IDM_ABOUTBOX < 0xF000);

     CMenu* pSysMenu = GetSystemMenu(FALSE);
     CString strAboutMenu;
     strAboutMenu.LoadString(IDS_ABOUTBOX);
     if (!strAboutMenu.IsEmpty())
     {
          pSysMenu->AppendMenu(MF_SEPARATOR);
          pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
     }

     // Set the icon for this dialog. The framework does this automatically
     //  when the application's main window is not a dialog
     SetIcon(m_hIcon, TRUE);               // Set big icon
     SetIcon(m_hIcon, FALSE);          // Set small icon
     
     // TODO: Add extra initialization here
//     mySession= new CSession(this);
     CSession::myDialog=m_hWnd;
     return TRUE;  // return TRUE  unless you set the focus to a control
}

void CAsyncDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
     if ((nID & 0xFFF0) == IDM_ABOUTBOX)
     {
          CAboutDlg dlgAbout;
          dlgAbout.DoModal();
     }
     else
     {
          CDialog::OnSysCommand(nID, lParam);
     }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon. For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CAsyncDlg::OnPaint() 
{
     if (IsIconic())
     {
          CPaintDC dc(this); // device context for painting

          SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

          // Center icon in client rectangle
          int cxIcon = GetSystemMetrics(SM_CXICON);
          int cyIcon = GetSystemMetrics(SM_CYICON);
          CRect rect;
          GetClientRect(&rect);
          int x = (rect.Width() - cxIcon + 1) / 2;
          int y = (rect.Height() - cyIcon + 1) / 2;

          // Draw the icon
          dc.DrawIcon(x, y, m_hIcon);
     }
     else
     {
          CDialog::OnPaint();
     }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CAsyncDlg::OnQueryDragIcon()
{
     return (HCURSOR) m_hIcon;
}

////////////////////////////////////////////////////////////////
// CSession

CSession::CSession(LPCTSTR pstrAgent, 
                       DWORD dwContext, DWORD dwAccessType, 
                       LPCTSTR pstrProxyName,LPCTSTR pstrProxyBypass, 
                       DWORD dwFlags):CWinApp("Async"){

}

void CAsyncDlg::UpdateOutput(CString csOutputData)
{
     ::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_STATUS, csOutputData);          
}


void CSession::OnStatusCallback(HINTERNET hInternet,
                         DWORD dwContext,
                         DWORD dwInternetStatus,
                         LPVOID lpvStatusInformation,
                         DWORD dwStatusInformationLength)
{
     CString csStatus;
     switch (dwInternetStatus)
     {
     case INTERNET_STATUS_RESOLVING_NAME:
          TRACE1("resolving name for %s\n", lpvStatusInformation);
          csStatus="INTERNET_STATUS_RESOLVING_NAME...";
          break;

     case INTERNET_STATUS_NAME_RESOLVED:
          TRACE1("resolved name for %s\n", lpvStatusInformation);
          csStatus="INTERNET_STATUS_NAME_RESOLVED...";
          break;

     case INTERNET_STATUS_HANDLE_CREATED:
          TRACE0("handle created...\n");
          csStatus="INTERNET_STATUS_HANDLE_CREATED...";
          break;

     case INTERNET_STATUS_CONNECTING_TO_SERVER:
          TRACE0("connecting to socket address...\n");
          csStatus="INTERNET_STATUS_CONNECTING_TO_SERVER...";
          break;

     case INTERNET_STATUS_REQUEST_SENT:
          TRACE0("sending request...\n");
          csStatus="INTERNET_STATUS_REQUEST_SENT...";
          break;

     case INTERNET_STATUS_SENDING_REQUEST:
          TRACE0("request sent...\n");
          csStatus="INTERNET_STATUS_SENDING_REQUEST...";
          break;

     case INTERNET_STATUS_CONNECTED_TO_SERVER:
          TRACE0("connected to socket address...\n");
          csStatus="INTERNET_STATUS_CONNECTED_TO_SERVER...";
          break;

     case INTERNET_STATUS_RECEIVING_RESPONSE:
          TRACE0("receiving response...\n");
          csStatus="INTERNET_STATUS_RECEIVING_RESPONSE...";
          break;

     case INTERNET_STATUS_RESPONSE_RECEIVED:
          TRACE0("response received!\n");
          csStatus="INTERNET_STATUS_RESPONSE_RECEIVED...";
          break;

     case INTERNET_STATUS_CLOSING_CONNECTION:
          TRACE1("closing connection... %8.8X\n", hInternet);
          csStatus="INTERNET_STATUS_CLOSING_CONNECTION...";
          break;

     case INTERNET_STATUS_CONNECTION_CLOSED:
          TRACE1("connection closed! %8.8X\n", hInternet);
          csStatus="INTERNET_STATUS_CONNECTION_CLOSED...";
          break;

     case INTERNET_STATUS_HANDLE_CLOSING:
          TRACE1("handle closed! %8.8X\n", hInternet);
          csStatus="INTERNET_STATUS_HANDLE_CLOSING...";
          CAsyncDlg::UpdateOutput(csStatus);
          return;
          break;

     case INTERNET_STATUS_REQUEST_COMPLETE:
          TRACE1("request complete, status = %d\n", dwStatusInformationLength);
          csStatus="INTERNET_STATUS_REQUEST_COMPLETE...";
          break;

     case INTERNET_STATUS_CTL_RESPONSE_RECEIVED:
          TRACE("INTERNET_STATUS_CTL_RESPONSE_RECEIVED...");
          csStatus="INTERNET_STATUS_CTL_RESPONSE_RECEIVED...";

     case INTERNET_STATUS_REDIRECT:
          TRACE("INTERNET_STATUS_REDIRECT...");
          csStatus="INTERNET_STATUS_REDIRECT...";

     default:
          TRACE1("Unknown status: %d\n", dwInternetStatus);
          csStatus="Unknown status...";
          break;
     } // END Switch

     CAsyncDlg::UpdateOutput(csStatus);

     if(dwInternetStatus==100){ 
          TRACE("Request complete\n");
          PostMessage(myDialog,WM_MYNOTIFY, 100, 100);
          return;
     }
}



void CAsyncDlg::OnLoadpage() 
{
     SetDlgItemText(IDC_OUTPUT, "");
     SetDlgItemText(IDC_STATUS, "");
     UpdateData(TRUE);

     bFileRead=FALSE;
     hReturnOpenHandle = CSession::LoadPage(m_url);
     if(hReturnOpenHandle==NULL)
     {
          SetDlgItemText(IDC_LASTERROR, "Invalid Session Handle");
          return;
     }
     
}

HINTERNET CSession::LoadPage(CString csUrl)
{
     LPTSTR lpMsgBuf; // for GetLastError
     CString csRawHtml;

     char Scheme[100];
     char Host[255];
     char Path[255];
     char ExtraInfo[255];
     URL_COMPONENTS uc;
     memset(&uc, 0, sizeof(uc));
     uc.dwStructSize = sizeof(uc);
     uc.lpszScheme = Scheme;
     uc.dwSchemeLength = 100;
     uc.lpszHostName = Host;
     uc.dwHostNameLength = 255;
     uc.lpszUrlPath = Path;
     uc.dwUrlPathLength = 255;
     uc.lpszExtraInfo = ExtraInfo;
     uc.dwExtraInfoLength = 255;
     
     BOOL bCrack = InternetCrackUrl(csUrl, 
     csUrl.GetLength(), ICU_ESCAPE, &uc);

     if(!bCrack)
          {     csRawHtml=csRawHtml + "Unable to Crack URL";
               CAsyncDlg::UpdateOutput(csRawHtml);
               return NULL;
               TRACE("bCrack returned false\n");
          }

     HINTERNET hInternet =     InternetOpen(AfxGetAppName(),
                              INTERNET_OPEN_TYPE_DIRECT, 
                              NULL,
                              NULL,
                              INTERNET_FLAG_ASYNC);
     if(!hInternet) 
          {     csRawHtml=csRawHtml + "Unable to access Internet";
               CAsyncDlg::UpdateOutput(csRawHtml);
               return NULL;
               TRACE("InternetOpen failed\n");
          }

     INTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTP_PORT;
     DWORD dwService = INTERNET_SERVICE_HTTP;
     DWORD dwFlags = NULL;
     DWORD dwContext = 1; // random

     HINTERNET hConnect =     InternetConnect(hInternet,
                         uc.lpszHostName,
                         nServerPort,
                         NULL,
                         NULL,
                         dwService,
                         dwFlags,
                         dwContext);
     
     if(!hConnect) 
          {     csRawHtml=csRawHtml + "InternetConnect failed";
               CAsyncDlg::UpdateOutput(csRawHtml);
               return NULL;
               TRACE("InternetConnect failed\n");
          }

     HINTERNET hHttpOpen = HttpOpenRequest(hConnect,
                                "GET",
                                uc.lpszUrlPath,
                                NULL,
                                NULL,
                                NULL,
                                INTERNET_FLAG_RELOAD|
                                INTERNET_FLAG_DONT_CACHE,
                                dwContext);

     if(!hHttpOpen) 
          {     csRawHtml=csRawHtml + "HttpOpenRequest failed";
               CAsyncDlg::UpdateOutput(csRawHtml);
               return NULL;
               TRACE("HttpOpenRequest failed\n");
          }


     INTERNET_STATUS_CALLBACK iCallback = InternetSetStatusCallback(
          hHttpOpen,OnStatusCallback);


     BOOL bSendRequest=FALSE;

     LPCTSTR lpszHeaders=NULL;
     DWORD dwHeadersLength;
     LPVOID lpOptional=NULL;
     DWORD dwOptionalLength;

     bSendRequest = HttpSendRequest(hHttpOpen,
                         lpszHeaders,
                         dwHeadersLength,
                         lpOptional,
                         dwOptionalLength);

               FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
               FORMAT_MESSAGE_FROM_SYSTEM, 
               NULL,
               GetLastError(),
               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
               (LPTSTR) &lpMsgBuf,
               256,NULL);
               TRACE("HttpSendRequest:  %s\n", lpMsgBuf);

     if(!bSendRequest) {TRACE("bSendRequest FAILED\n");};


     // for HttpQueryInfo
     // 
     DWORD dwInfoLevel = HTTP_QUERY_STATUS_CODE;
     CHAR * lpvBuffer[16];
     DWORD lpvBufferLength = 16;
     LPDWORD lpdwIndex = NULL;
     int iStat=999;
     
     BOOL bQuery = HttpQueryInfo(hHttpOpen,
                    dwInfoLevel,
                    lpvBuffer,
                    &lpvBufferLength,
                    lpdwIndex);

     iStat = atoi((const char *)lpvBuffer);
     TRACE("iStat=%d\n", iStat);
     return hHttpOpen;
}

LRESULT CAsyncDlg::OnMyNotify(WPARAM wParam, LPARAM lParam){
     TRACE("CAsyncDlg::OnNotify\n");
     if(bFileRead==FALSE)
     {
          ReadHttpFile(hReturnOpenHandle);
     }
     return 1;
}

//////////////////////////////////////////////////////////////
///////////
void CAsyncDlg::ReadHttpFile(HINTERNET hHttpOpen) 
{


     DWORD dwInfoLevel = HTTP_QUERY_STATUS_CODE;
     CHAR * lpvBuffer[16];
     DWORD lpvBufferLength = sizeof(lpvBuffer);
     LPDWORD lpdwIndex = NULL;
     int iStat=0;
     BOOL bQuery=FALSE;

     DWORD lpdwNumberOfBytesAvailable;
     BOOL bData;
     BOOL bRead=TRUE;
        char lpBuffer[4096];
     DWORD dwNumberOfBytesToRead=4096;
     DWORD lpNumberOfBytesRead;
     CString csRawHtml;

     while(iStat==0)
     {
          bQuery = HttpQueryInfo(hHttpOpen,
          dwInfoLevel,
          lpvBuffer,
          &lpvBufferLength,
          lpdwIndex);
          iStat = atoi((const char *)lpvBuffer);
          TRACE("iStat=%d\n", iStat);
     }

     switch(iStat)
     {
     case 200:
          break;
     default:
          csRawHtml = csRawHtml + "HTTP Error:  " + (LPSTR)lpvBuffer;
          UpdateOutput(csRawHtml);          
          return;
     }

     bQuery=FALSE;
     while(!bQuery)
     {
     bQuery = InternetQueryDataAvailable( 
          hHttpOpen,
          &lpdwNumberOfBytesAvailable,
          0,0);
     GetError();
     Sleep(500);      //cheap hack to get around the 
               //overlapped i/o problem...
     }

     while(bRead)
     {
          bData = InternetQueryDataAvailable( 
               hHttpOpen,
               &lpdwNumberOfBytesAvailable,
               0,0);
               GetError();

     if(!bData){TRACE("bData failed\n");}
     TRACE("%d bytes available\n", lpdwNumberOfBytesAvailable);

     TRACE("dwNumberOfBytesToRead=%d\n", dwNumberOfBytesToRead);
     bRead = InternetReadFile(hHttpOpen,
                    lpBuffer,
                    dwNumberOfBytesToRead,
                    &lpNumberOfBytesRead);

     if(lpNumberOfBytesRead==0 && bRead==TRUE)
     {
          TRACE("end of file...\n");
          bFileRead=TRUE;
          csRawHtml=csRawHtml + (LPSTR)lpBuffer;    
          SetDlgItemText(IDC_OUTPUT, csRawHtml);          
          InternetCloseHandle(hHttpOpen);
          break;
     }

     if(!bRead)
     {
          GetError();
          dwNumberOfBytesToRead = lpdwNumberOfBytesAvailable;     
          TRACE("bRead returned False\n");
          bRead=TRUE;
     }
     else
     {

     csRawHtml=csRawHtml + (LPSTR)lpBuffer;    
     }//end if
     }//end while

     TRACE("read loop done\n");

     bFileRead=TRUE;
     SetDlgItemText(IDC_OUTPUT, csRawHtml);          

}//END ReadHttpFile


void CAsyncDlg::GetError()
{
     LPTSTR lpMsgBuf; 
          FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
          FORMAT_MESSAGE_FROM_SYSTEM, 
          NULL,
          GetLastError(),
          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
          (LPTSTR) &lpMsgBuf,
          256,NULL);
          TRACE("Last Error:  %s\n", lpMsgBuf);
          ::SetDlgItemText(AfxGetMainWnd()->m_hWnd,
                              IDC_LASTERROR, lpMsgBuf);          
}

Listing 4.7 shows the header file, AsyncDlg.h. The remainder of the files in the project are all the default AppWizard-generated files.


Listing 4.7 AsyncDlg.h
// AsyncDlg.h : header file
//
class CAsyncDlg;

class CSession : public CWinApp{
public:
     static HWND myDialog;
     CSession(LPCTSTR pstrAgent = NULL, 
          DWORD dwContext = 1, 
          DWORD dwAccessType = PRE_CONFIG_INTERNET_ACCESS, 
          LPCTSTR pstrProxyName = NULL, 
          LPCTSTR pstrProxyBypass = NULL, 
          DWORD dwFlags = 0 );
     CSession(CAsyncDlg*);

     static void CALLBACK OnStatusCallback(HINTERNET hInternet,
                              DWORD dwContext,
                              DWORD dwInternetStatus,
                              LPVOID lpvStatusInformation,
                              DWORD dwStatusInformationLength);
     static HINTERNET LoadPage(CString csUrl);

};

/////////////////////////////////////////////////////////////////////////////
// CAsyncDlg dialog

class CAsyncDlg : public CDialog{
// Construction
public:
     CAsyncDlg(CWnd* pParent = NULL);     // standard constructor
     HINTERNET hReturnOpenHandle;
     void ReadHttpFile(HINTERNET);
     BOOL bFileRead;
     static void UpdateOutput(CString csOutputData);
     void GetError();

// Dialog Data
     //{{AFX_DATA(CAsyncDlg)
     enum { IDD = IDD_ASYNC_DIALOG };
     CString     m_url;
     //}}AFX_DATA

     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CAsyncDlg)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);     // DDX/DDV support
     //}}AFX_VIRTUAL

// Implementation
protected:
     HICON m_hIcon;

     // Generated message map functions
     //{{AFX_MSG(CAsyncDlg)
     virtual BOOL OnInitDialog();
     afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
     afx_msg void OnPaint();
     afx_msg HCURSOR OnQueryDragIcon();
     afx_msg void OnLoadpage();
     afx_msg LRESULT OnMyNotify(WPARAM wParam, LPARAM lParam);
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

What's in WinInet's Future

The WinInet API, with support for the HTTP, FTP, and Gopher protocols, makes programming Internet applications for the Windows operating systems a much simpler task. It frees you from having to use Windows socket calls and from the mechanics of the underlying Internet protocols. As we mentioned at the outset of this chapter, WinInet is still growing, and we can look forward to not only extensions to the functions now provided for supported protocols, but to additional support for other Internet protocols, including SMTP and NNTP.



HomeAbout UsSearchSubscribeAdvertising InfoContact UsFAQs
Use of this site is subject to certain Terms & Conditions.
Copyright (c) 1996-1998 EarthWeb, Inc.. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited.
Please read the Acceptable Usage Statement.
Contact reference@developer.com with questions or comments.
Copyright 1998 Macmillan Computer Publishing. All rights reserved.

Click here for more info

Click here for more info