developer.com
developerdirect.com
htmlgoodies.com
javagoodies.com
jars.com
intranetjournal.com
javascripts.com
|
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.
|
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.
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.
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.
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.
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.
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);
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/.
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
|
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.
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.
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.
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
Command | Description
|
bin | Specifies 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.
|
Pwd | Displays the current working directory on the FTP host.
|
Bye | Ends 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);
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.
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()
};
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()
};
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.
|
|