⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch25.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>

<HEAD>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">
	<META NAME="Author" Content="Steph Mineart">
	<TITLE>Ch 25 -- Using WININET to Create FTP Applications</TITLE>

</HEAD>

<BODY BACKGROUND="bg1.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/bg1.gif" BGCOLOR="#FFFFFF">

<P ALIGN="CENTER"><IMG SRC="sams.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/sams.gif" WIDTH="75" HEIGHT="24" ALIGN="BOTTOM"
BORDER="0"><BR>
<BR>
<A HREF="index-3.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/index.htm"><IMG SRC="toc.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/toc.gif" WIDTH="40" HEIGHT="40" 
ALIGN="BOTTOM"
ALT="TOC" BORDER="0" NAME="toc4"></A><A HREF="ch24.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/ch24.htm"><IMG SRC="back-1.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/back.gif"
WIDTH="40" HEIGHT="40" ALIGN="BOTTOM" ALT="BACK" BORDER="0" NAME="toc1"></A><A HREF="ch26.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/ch26.htm"><IMG
SRC="forward.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/forward.gif" WIDTH="40" HEIGHT="40" 
ALIGN="BOTTOM" ALT="FORWARD" BORDER="0"
NAME="toc2"></A></P>
<H2 ALIGN="CENTER"><FONT COLOR="#000077">Charlie Calvert's C++ Builder Unleashed</FONT></H2>
<P>
<H2 ALIGN="CENTER"><A NAME="Heading1"></A><FONT COLOR="#000077">- 25 -</FONT></H2>
<H2 
ALIGN="CENTER"><A NAME="Heading2"></A><FONT COLOR="#000077">Using WININET to
Create FTP Applications</FONT></H2>
<H3><A NAME="Heading4"></A><FONT COLOR="#000077">Overview</FONT></H3>
<H3 ALIGN="CENTER"><FONT COLOR="#000077"></FONT></H3>
<P>In this 
chapter, you will look at techniques for building FTP clients. Most of
the chapter will be dedicated to a discussion of WININET, which is a relatively simple
Windows API for creating FTP, Gopher, and HTTP applications. This API is especially
appealing 
because it is built into the operating system, ships with all versions
of Windows after Windows NT 4.0, and allows you to create small, fast applications.
If you don't have <TT>WININET.DLL</TT> on your system, you can download it for free
from 
Microsoft's Web site. It works with all 32-bit versions of Windows.</P>
<P>I will also briefly discuss the FTP ActiveX component that ships with BCB. The
FTP component provides a wide range of services that are easily accessible. However,
it is not as 
light, nor as flexible, a tool as WININET.</P>
<P>A standard FTP component wrapped around WININET will be the central focus of this
chapter. As a result, you will get a chance to take another look at building components.
Included in the chapter will 
be some discussion about how to create components that
are easy to use.</P>
<P>Another topic I touch on in this chapter is how to make an owner draw list box.
This subject comes up in the course of creating an application that can display the
files 
shown in an FTP directory search.
<H3><A NAME="Heading5"></A><FONT COLOR="#000077">Requirements</FONT></H3>
<P>BCB ships with <TT>WININET.h</TT> in the <TT>Include\Win32</TT> directory. <TT>WININET.h</TT>
contains manifests, functions, types, and 
prototypes for the Microsoft Windows Internet
extensions. Therefore, you can now easily add FTP, Gopher, and HTTP support to your
programs. Microsoft's <TT>WININET.DLL</TT> is freely distributable, and is available
from Microsoft if it is not already 
installed in your <TT>Windows/System</TT> or
<TT>Windows/System32</TT> directory. Windows NT 4.0 ships with <TT>WININET.DLL</TT>,
as will Windows 97. <TT>WININET.DLL</TT> runs fine on Windows 95.</P>
<P>One of the best places to get help on this 
subject is in the ActiveX SDK that
ships as part of the MSDN and that has frequently been available for downloading
from <A HREF="javascript:if(confirm('http://www.microsoft.com/  \n\nThis file was not retrieved by Teleport Pro, because it is addressed on a domain or path outside the boundaries set for its Starting Address.  \n\nDo you want to open it from the server?'))window.location='http://www.microsoft.com/'" tppabs="http://www.microsoft.com/"><TT>www.microsoft.com</TT></A>. Here is how,
at the time of this writing, to get the Windows 
help file for <TT>WININET.h</TT>:</P>
<PRE><A HREF="javascript:if(confirm('http://www.microsoft.com/intdev/sdk/docs/WININET/default.htm  \n\nThis file was not retrieved by Teleport Pro, because it is addressed on a domain or path outside the boundaries set for its Starting Address.  \n\nDo you want to open it from the server?'))window.location='http://www.microsoft.com/intdev/sdk/docs/WININET/default.htm'" tppabs="http://www.microsoft.com/intdev/sdk/docs/WININET/default.htm">http://www.microsoft.com/intdev/sdk/docs/WININET/default.htm

</A></PRE>
<P>In general, the <TT>INTDEV</TT> section of the Microsoft 
Web site is home ground
for MS Internet developers.

<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>When working with <TT>WININET.h</TT>,
	you might find that you need to edit some of the code in the 
file. You should try
	simply including the file in your project first, and if you have problems, then consider
	making the following changes. <BR>
	<BR>
	If you come across a <TT>#pragma pack</TT>, you can replace it with the <TT>pshpack4.h</TT>
	
file:</P>
	<PRE><FONT COLOR="#0066FF">// #pragma pack(push, WININET, 4)

#include &lt;pshpack4.h&gt;</FONT></PRE>

</BLOCKQUOTE>

<PRE><FONT COLOR="#0066FF"></FONT></PRE>


<BLOCKQUOTE>
	<P>At the end of the file, you will have to make the following 
substitutions:</P>
	<PRE><FONT COLOR="#0066FF">// #pragma pack(pop, WININET)

#include &lt;poppack.h&gt;</FONT></PRE>
	<P>
<HR>


</BLOCKQUOTE>

<PRE></PRE>
<P>You will need an FTP server of some kind to be able to use this control. Windows
NT 4.0 
comes with an excellent server, or you can usually download the Personal Web
Server from the Microsoft Web site or get it with a copy of FrontPage. Personal Web
Server supports FTP and ISAPI, and it runs on Windows 95. I have heard numerous complaints

about the Personal Web Server's robustness. These may or may not be well founded,
but there is no denying the usefulness of the tool when you are developing an application
on a Win95 machine. If need be, you can copy your finished files to a more 
robust
server after you complete the development cycle.
<H3><A NAME="Heading7"></A><FONT COLOR="#000077">Making Sure FTP Is Working on Your
System</FONT></H3>
<P>If you're connected to the Internet, you should be able to FTP into various sites.
For 
example, to connect to the Borland FTP site, type the following at the command
prompt:</P>
<PRE><A HREF="javascript:if(confirm('ftp://ftp.borland.com/  \n\nThis file was not retrieved by Teleport Pro, because it did not meet the project\'s file type specifications.  \n\nDo you want to open it from the server?'))window.location='ftp://ftp.borland.com/'" tppabs="ftp://ftp.borland.com/">ftp ftp.borland.com

</A></PRE>
<P>When the system asks for a username, type in <TT>anonymous</TT>. When it requests
a 
password, type in your e-mail address. Figure 25.1 shows a screen shot of a typical
old-fashioned, command-line-based FTP session.<BR>
<BR>
<A NAME="Heading8"></A><A HREF="25ebu01.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/25/25ebu01.jpg">FIGURE 25.1.</A><FONT COLOR="#000077">
</FONT><I>FTP from 
the command line. <BR>
</I><BR>
This chapter will show how to do the same thing in a Windows program that uses graphical
controls.</P>
<P>If you can't FTP into <TT>borland.com</TT>, <TT>microsoft.com</TT>, or some site
on your intranet, something is 
wrong with your Windows setup. You should clear up
that problem first, before tackling the material in this chapter. I provide some
help in this regard in Chapter 8, &quot;Database Basics and Database Tools,&quot;
in the section called &quot;Some 
Notes on Installing TCP/IP.&quot;</P>
<P>Figure 25.2 shows a commercial program that works from inside Windows. The program
you will create in this chapter, called WININET, is not quite this fancy, but it
does allow you to use standard Windows 
controls to make and maintain your connections.
FTPWININET has provisions for copying files from and to FTP sites, and for using
the mouse to navigate through directories.<BR>
<BR>
<A NAME="Heading9"></A><A HREF="25ebu02.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/25/25ebu02.jpg">FIGURE 
25.2.</A><FONT COLOR="#000077">
</FONT><I>An example of a shareware FTP program logging onto the Borland FTP site.</I></P>
<P>
<H3><A NAME="Heading10"></A><FONT COLOR="#000077">FTP Using WININET</FONT></H3>
<P>Now you're ready to look at the code 
needed to use the WININET DLL in an FTP session.
This study will not be exhaustive, but it should help to get you up and running.
The first fact you need to know about this technology is that some of the functions
in <TT>WININET.h</TT> return a 
pointer variable declared to be of type <TT>HINTERNET</TT>:</P>
<PRE><FONT COLOR="#0066FF">typedef LPVOID HINTERNET;

</FONT></PRE>
<P>This pointer acts as a handle to the various Internet services you employ. After
retrieving the handle, you will 
pass it in as the first parameter to many of the
other WININET functions you call throughout the life of a single session.</P>
<P>You need to remember to return the handle to the system when you're through using
it, usually by calling the WININET 
function called <TT>InternetCloseHandle</TT>:</P>
<PRE><FONT COLOR="#0066FF">BOOL InternetCloseHandle(

  HINTERNET hInet  // Valid Internet handle to be closed.

);

</FONT></PRE>

<DL>
	<DT><FONT COLOR="#0066FF"></FONT></DT>
</DL>



<BLOCKQUOTE>
	
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Just hearing this much information
	should tip you off to the fact that you ought to have an object to use this material
	and should consider creating a component. The tip-off here is the need 
to perform
	housekeeping chores with pointers. <BR>
	<BR>
	I no longer believe in trying to write complex cleanup code on-the-fly. Most of the
	time I remember to deallocate memory that I have allocated, and I almost always remember
	to allocate 
memory before trying to use it. However, computers aren't very considerate
	about human weaknesses, even infrequent weaknesses. You want to get these things
	right all the time, and almost just isn't good enough! <BR>
	<BR>
	Possible solutions involve 
using a language such as Java or Visual Basic. These languages
	generally take allocation chores out of your hands. Of course, you almost always
	have to pay a price for using tools of that kind, and it generally involves a severe
	performance 
penalty. If you want speed and flexibility, working in a language such
	as C++ is always better. <BR>
	<BR>
	Objects and components are the tools you can use to make C++ safe. If you build an
	object properly, it will always take care of chores such 
as allocating and deallocating
	memory for you. You get the job done right once or find someone who has done it right
	once, and then you can reuse the object over and over without concern for petty housekeeping
	chores. <BR>
	<BR>
	The key point to 
absorb is that some developers, myself included, believe that almost
	any moderately complicated chore that involves allocating and deallocating handles
	is a strong candidate for wrapping in an object. Even better, put the code in a component;
	then 
there is almost no chance you will misuse it. The great thing about BCB components
	is that they are only marginally larger than regular objects, and they're every bit
	as fast. 
<HR>


</BLOCKQUOTE>

<P>In the next few pages, you will learn how to 
create a component that wraps the
FTP calls found in WININET. I will present the WININET calls to you at the same time
that I slowly construct the pieces of a component called <TT>TMyFTP</TT>.
<H4><A NAME="Heading12"></A><FONT COLOR="#000077">Using 
InternetOpen</FONT></H4>
<P>To get a WININET session started, you call <TT>InternetOpen</TT>:</P>
<PRE><FONT COLOR="#0066FF">HINTERNET InternetOpen(

    LPCTSTR lpszAgent,        // Name of app opening the session

    DWORD dwAccessType,       // 
The access type, usually set to 0

    LPCTSTR lpszProxyName,    // For use in specifying a proxy, pass 0

    LPCSTR lpszProxyBypass,   // For use in specifying a proxy, pass NULL

    DWORD dwFlags             // You can set up a callback here.

);


</FONT></PRE>
<P>The first parameter is the name of the application opening the session. You can
pass in any string you want in this parameter. Microsoft documentation states &quot;This
name is used as the user agent in the HTTP protocol.&quot; The 
remaining parameters
can be set to <TT>0</TT> or <TT>NULL</TT>.</P>
<P>The following are some options for use in the <TT>dwAccessType</TT> parameter:
<TT>LOCAL_INTERNET_ACCESS</TT> Connects only to local Internet sites.</P>

<P><TT>GATEWAY_INTERNET_ACCESS</TT> Allows connections to any site on the Web.


<BLOCKQUOTE>
	<P><TT>CERN_PROXY_INTERNET_ACCESS</TT> Uses a CERN proxy to access the Web.

</BLOCKQUOTE>

<P>Here are the options as they appear in 
<TT>WININET.h</TT>:</P>
<PRE><FONT COLOR="#0066FF">#define INTERNET_OPEN_TYPE_PRECONFIG    0   // use registry configuration

#define INTERNET_OPEN_TYPE_DIRECT       1   // direct to net

#define INTERNET_OPEN_TYPE_PROXY        3   // via named proxy


#define PRE_CONFIG_INTERNET_ACCESS      INTERNET_OPEN_TYPE_PRECONFIG

#define LOCAL_INTERNET_ACCESS           INTERNET_OPEN_TYPE_DIRECT

#define GATEWAY_INTERNET_ACCESS         2   // Internet via gateway

#define CERN_PROXY_INTERNET_ACCESS      
INTERNET_OPEN_TYPE_PROXY

</FONT></PRE>
<P>As you can see, passing in zero means that you will use information already stored
in the Registry. The rest of the parameters are involved with setting up a proxy
server, except for the last one, which can 
be used to set up a callback if you need
it. The last parameter has only one possible flag:</P>
<PRE><FONT COLOR="#0066FF">INTERNET_FLAG_ASYNC

</FONT></PRE>
<P>Refer to the Microsoft documentation for additional information.</P>
<P>Here is an example 
of a typical call to <TT>InternetOpen</TT>:</P>
<PRE><FONT COLOR="#0066FF">FINet = InternetOpen(&quot;WININET1&quot;, 0, NULL, 0, 0);

</FONT></PRE>
<H4><A NAME="Heading13"></A><FONT COLOR="#000077">Using InternetConnect</FONT></H4>
<P>After you open 
the session, the next step is to connect to the server using <TT>InternetConnect</TT>:</P>
<PRE><FONT COLOR="#0066FF">HINTERNET InternetConnect(

  HINTERNET hInternetSession, // Handle from InternetOpen

  LPCTSTR lpszServerName,     // Server: e.g., 
www.borland.com

  INTERNET_PORT nServerPort,  // Usually 0

  LPCTSTR lpszUsername,       // usually anonymous

  LPCTSTR lpszPassword,       // usually your email address

  DWORD dwService,            // FTP, HTTP, or Gopher?

  DWORD dwFlags,              
// Usually 0

  DWORD dwContext             // User defined number for callback

);

</FONT></PRE>
<P>Here are the three possible self-explanatory and mutually exclusive flags that
can be passed in the <TT>dwService</TT> parameter:</P>
<PRE><FONT 
COLOR="#0066FF">INTERNET_SERVICE_FTP

INTERNET_SERVICE_GOPHER

INTERNET_SERVICE_HTTP

</FONT></PRE>
<P>Here is the option for the <TT>dwFlags</TT> parameter:</P>
<PRE><FONT COLOR="#0066FF">INTERNET_CONNECT_FLAG_PASSIVE

</FONT></PRE>
<P>This option is 
valid only if you passed <TT>INTERNET_SERVICE_FTP</TT> in the previous
parameter. At this time, no other flags are valid for this parameter.</P>
<P>If the session succeeds, <TT>InternetOpen</TT> returns a valid pointer; otherwise,
it returns 
<TT>NULL</TT>. Remember that you will have to deallocate this memory later.
Doing so in the destructor for an object that takes control of the entire FTP session
is probably best.</P>
<P>Here are the two sections' methods in <TT>TMyFTP</TT> that use 
<TT>InternetOpen</TT>
and <TT>InternetConnect</TT>:</P>
<PRE><FONT COLOR="#0066FF">__fastcall TMyFtp::TMyFtp(Classes::TComponent* AOwner): TComponent(AOwner)

{

  FCurFiles = new TStringList();

  FINet = InternetOpen(&quot;WinINet1&quot;, 0, NULL, 
0, 0);

}

bool __fastcall TMyFtp::Connect(void)

{

  AnsiString S;

  AnsiString CR1(&quot;\x00D\x00A&quot;);

  FContext = 255;

  FFtpHandle = InternetConnect(FINet, FServer.c_str(), 0,

   FUserID.c_str(), FPassword.c_str(),

   
INTERNET_SERVICE_FTP, 0, FContext);

  if (FFtpHandle == NULL)

  {

    S = &quot;Connection failed&quot; + CR1 +

         &quot;Server: &quot; + FServer + CR1 +

         &quot;UserID: &quot; + FUserID + CR1 +

         &quot;Password: &quot; + 
FPassword;

    ShowMessage(S);

    return FALSE;

  }

  else

    SetUpNewDir();

  return TRUE;

}

</FONT></PRE>
<P>Besides calling <TT>InternetOpen</TT>, the constructor also allocates memory for
a <TT>TStringList</TT>. This list will be used to 
hold the names of the files in
the directories visited by the FTP session. The <TT>connect</TT> method provides
code that pops up a message explaining exactly what went wrong in case of error.
This function would probably be stronger if it contained 
exception-handling code:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TMyFtp::Connect(void)

{

  AnsiString S;

  AnsiString CR1(&quot;\x00D\x00A&quot;);

  FContext = 255;

  FFtpHandle = InternetConnect(FINet, FServer.c_str(), 0,

   
FUserID.c_str(), FPassword.c_str(),

   INTERNET_SERVICE_FTP, 0, FContext);

  if (FFtpHandle == NULL)

  {

    S = &quot;Connection failed&quot; + CR1 +

         &quot;Server: &quot; + FServer + CR1 +

         &quot;UserID: &quot; + FUserID + CR1 
+

         &quot;Password: &quot; + FPassword;

    throw Exception(S);

  }

  else

    SetUpNewDir();

}

</FONT></PRE>
<P>The key difference to note about this new version of the function is that it does
not return a value. You don't have to 
concern yourself with whether the function
succeeds because none of the code after the exception is raised will be executed.
Your program itself won't end, but you will automatically be popped out of the current
process and sent back to the message 
loop if something goes wrong. The only way to
stop that process is to catch the exception. As you learned in Chapter 5, &quot;Exceptions,&quot;
it is usually best not to try to handle the exception in a <TT>catch</TT> block,
but instead to let the 
exception-handling process resolve the problem for you automatically.</P>
<P>When you call the <TT>Connect</TT> function, you might want to do so in a function
that looks like this:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall 
TForm1::ConnectBtnClick(TObject *Sender)

{

  if (FTPNames-&gt;GetConnectionData())

  {

    Application-&gt;ProcessMessages();

    Ftp-&gt;Server = FTPNames-&gt;Server;

    Ftp-&gt;UserID = FTPNames-&gt;UserID;

    Ftp-&gt;Password = 
FTPNames-&gt;Password;

    Screen-&gt;Cursor = TCursor(crHourGlass);

    Ftp-&gt;Connect();

    Screen-&gt;Cursor = TCursor(crDefault);

  }

}

</FONT></PRE>
<P>The <TT>GetConnectionData</TT> function makes sure that the <TT>Server</TT>, 
<TT>UserID</TT>,
and <TT>Password</TT> properties are filled in correctly. Notice that the function
calls <TT>ProcessMessages</TT> to be sure that the screen is properly redrawn before
handing control over to the system. Care is also taken to put up a 
cursor that asks
the user to wait. Especially if something goes wrong, the system could take a minute
or more to return from a call to <TT>InternetConnect</TT>. While you're waiting for
the system to either time out or resolve the call, you want the 
screen to look right,
and you want to tell users that all is well and that they should sit tight.
<H3><A NAME="Heading14"></A><FONT COLOR="#000077">After Connecting</FONT></H3>
<P>After you are connected, you can call the <TT>GetCurrentDirectory</TT> 
to retrieve
the name of the current directory:</P>
<PRE><FONT COLOR="#0066FF">System::AnsiString __fastcall TMyFtp::GetCurrentDirectory(void)

{

  DWORD Len = 0;

  FtpGetCurrentDirectory(FFtpHandle, FCurDir.c_str(), &amp;Len);

  
FCurDir.SetLength(Len);

  FtpGetCurrentDirectory(FFtpHandle, FCurDir.c_str(), &amp;Len);

  return FCurDir;

}

</FONT></PRE>
<P>This function is declared as follows:</P>
<PRE><FONT COLOR="#0066FF">BOOL FtpGetCurrentDirectory(

    IN HINTERNET 
hFtpSession,           // handle from InternetConnect

    OUT LPCTSTR lpszCurrentDirectory,   // directory returned here

    IN OUT LPDWORD lpdwCurrentDirectory // buf size of 2nd parameter

); // True on success

</FONT></PRE>
<P>If you set the 
last parameter to zero, then WININET will use this parameter to
return the length of the directory string. You can then allocate memory for your
string and call the function a second time to retrieve the directory name. This process

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -