📄 ch25.htm
字号:
is shown earlier
in the <TT>GetCurrentDirectory</TT> method. (Notice the call to
<TT>SetLength</TT>. C++Builder requires that you allocate memory for the new long
strings in situations like this. The issue here is that the string will be assigned
a value inside the
operating system, not inside your C++Builder application. As a
result, C++Builder can't perform its usual surreptitious string allocations in these
circumstances.)</P>
<P>The following set of functions returns the currently available files in a
particular
directory:</P>
<PRE><FONT COLOR="#0066FF">Classes::TStringList* __fastcall TMyFtp::FindFiles(void)
{
WIN32_FIND_DATA FindData;
HINTERNET FindHandle;
AnsiString Temp;
FCurFiles->Clear();
FindHandle =
FtpFindFirstFile(FFtpHandle, "*.*", &FindData, 0, 0);
if (FindHandle == NULL)
{
return FCurFiles;
}
GetFindDataStr(FindData, &Temp);
FCurFiles->Add(Temp);
while (InternetFindNextFile(FindHandle,
&FindData))
{
GetFindDataStr(FindData, &Temp);
FCurFiles->Add(Temp);
}
InternetCloseHandle(FindHandle);
return FCurFiles;
}
</FONT></PRE>
<P>The key functions to notice here are <TT>FtpFindFirstFile</TT>,
<TT>InternetFindNextFile</TT>,
and <TT>InternetCloseHandle</TT>. You use these functions in a manner similar to
that employed when calling the C++Builder functions <TT>FindFirst</TT>, <TT>FindNext</TT>,
and <TT>FindClose</TT>. In particular, you use
<TT>FtpFindFirstFile</TT> to get the
first file in a directory. You then call <TT>InternetFindNextFile</TT> repeatedly,
until the function returns <TT>False</TT>. After finishing the session, call <TT>InternetCloseHandle</TT>
to inform the operating
system that it can deallocate the memory associated with
this process.</P>
<P>The following function returns a simple string designating what type of file is
retrieved by a call to <TT>ftpFindFirstFile</TT> or <TT>InternetFindNextFile</TT>:</P>
<PRE><FONT COLOR="#0066FF">AnsiString *GetFindDataStr(WIN32_FIND_DATA FindData, AnsiString *S)
{
AnsiString Temp;
switch (FindData.dwFileAttributes)
{
case FILE_ATTRIBUTE_ARCHIVE:
{
*S = `A';
break;
}
case
FILE_ATTRIBUTE_COMPRESSED:
*S = `C';
break;
case FILE_ATTRIBUTE_DIRECTORY:
*S = `D';
break;
case FILE_ATTRIBUTE_HIDDEN:
*S = `H';
break;
case FILE_ATTRIBUTE_NORMAL:
*S = `N';
break;
case FILE_ATTRIBUTE_READONLY:
*S = `R';
break;
case FILE_ATTRIBUTE_SYSTEM:
*S = `S';
break;
case FILE_ATTRIBUTE_TEMPORARY:
*S = `T';
break;
default:
*S =
IntToStr(FindData.dwFileAttributes);
}
*S = *S + GetDots(75);
S->Insert(FindData.cFileName, 6);
Temp = IntToStr(FindData.nFileSizeLow);
S->Insert(Temp, 25);
return S;
}
</FONT></PRE>
<P>I use this information to create a
simple string I can show to the user explaining
the type of file currently under examination. For example, if I find a directory,
the string might look like this:</P>
<PRE><FONT COLOR="#0066FF">D WINDOWS
</FONT></PRE>
<P>If I find a file, the string
might look like this:</P>
<PRE><FONT COLOR="#0066FF">F AUTOEXEC.BAT
</FONT></PRE>
<P>One final note: Unlike the functions and structures mentioned in the preceding
few paragraphs, <TT>WIN32_FIND_DATA</TT> is not defined in <TT>WININET.h</TT>, but
instead can be found in <TT>WinBase.h</TT> and other standard Windows files. Detailed
information on this structure is available in the WIN32 help file that ships with
C++Builder. A second constant called <TT>TWin32FindData</TT> mapped to the same
value
is declared in the <TT>\include\vcl\Windows.hpp</TT> file that ships with C++Builder.
<H3><A NAME="Heading15"></A><FONT COLOR="#000077">Retrieving a File</FONT></H3>
<P>You can use the <TT>ftpGetFile</TT> function from <TT>WININET.h</TT> to
retrieve
a file via FTP:</P>
<PRE><FONT COLOR="#0066FF">BOOL FtpGetFile(
IN HINTERNET hFtpSession, // Returned by InternetConnect
IN LPCTSTR lpszRemoteFile, // File to get
IN LPCTSTR lpszNewFile, // Where to put it on your PC
IN
BOOL fFailIfExists, // Overwrite existing files?
IN DWORD dwFlagsAndAttributes, // File attribute-See CreateFile.
IN DWORD dwFlags, // Binary or ASCII transfer
IN DWORD dwContext// Usually zero
); // True on success
</FONT></PRE>
<P>The following is an example of how to use this call:</P>
<PRE><FONT COLOR="#0066FF">bool __fastcall TMyFtp::GetFile(System::AnsiString FTPFile, System::AnsiStringNewFile)
{
return FtpGetFile(FFtpHandle, FTPFile.c_str(), NewFile.c_str(),
False, FILE_ATTRIBUTE_NORMAL,
FTP_TRANSFER_TYPE_BINARY, 0);
}
</FONT></PRE>
<P>To learn about the parameters that can be passed in the <TT>dwFlagsAndAttributes</TT>
parameter, look up <TT>CreateFile</TT> in the WIN32 help file
that ships with C++Builder.
The <TT>dwFlags</TT> parameter can be set to either <TT>FTP_TRANSFER_TYPE_BINARY</TT>
or <TT>FTP_TRANSFER_TYPE_ASCII</TT>.
<H3><A NAME="Heading16"></A><FONT COLOR="#000077">Sending Files to an FTP Server</FONT></H3>
<P>When
you're sending files to an NT site, remember that you probably don't have
rights in the default FTP directory. Instead, you should change to another directory
where your user has rights. You can usually configure what rights a particular user
has on a
server through the server- side tools provided for administrating user accounts.</P>
<P>This function copies a file to a server:</P>
<PRE><FONT COLOR="#0066FF">bool __fastcall TMyFtp::SendFile1(System::AnsiString FTPFile,
System::AnsiString NewFile)
{
DWORD Size = 3000;
AnsiString S;
BOOL Transfer = FtpPutFile(FFtpHandle,
FTPFile.c_str(),
NewFile.c_str(),
FTP_TRANSFER_TYPE_BINARY, 0);
if (!Transfer)
{
int Error = GetLastError();
S = Format("Error Number: %d. Hex: %x", OPENARRAY(TVarRec, (Error, Error)));
ShowMessage(S);
S.SetLength(Size);
if (!InternetGetLastResponseInfo(&(DWORD)Error,
S.c_str(), &Size))
{
Error = GetLastError();
ShowMessage(Format("Error Number: %d. Hex: %x", OPENARRAY(TVarRec, (Error,Error))));
}
ShowMessage(Format("Error Number: %d. Hex: %x Info: %s",
OPENARRAY(TVarRec, (Error, Error, S))));
}
else
ShowMessage("Success");
return Transfer;
}
</FONT></PRE>
<P>The core function looks like this:</P>
<PRE><FONT COLOR="#0066FF">BOOL Transfer = FtpPutFile(FFtpHandle,
FTPFile.c_str(),
NewFile.c_str(),
FTP_TRANSFER_TYPE_BINARY, 0);
</FONT></PRE>
<P><TT>FtpPutFile</TT> takes
<UL>
<LI>The session handle in the first parameter.
<P>
<LI>The file to copy from your hard
drive in the second parameter.
<P>
<LI>The name the file will have on the server in the third parameter.
<P>
<LI>Whether to conduct a binary or ASCII transfer in the fourth parameter.
<P>
<LI>Information about the context of the transfer. You
can usually set this parameter
to zero.
</UL>
<P>The rest of the code in the <TT>SendFile1</TT> function is dedicated to error
handling. Call <TT>GetLastError</TT> to retrieve the error code, and call <TT>InternetGetLastResponseInfo</TT>
to retrieve
a human-readable description of the error.
<H3><A NAME="Heading17"></A><FONT COLOR="#000077">Deleting Files</FONT></H3>
<P>The act of deleting a file on a server is extremely simple:</P>
<PRE><FONT COLOR="#0066FF">bool __fastcall
TMyFtp::DeleteFile(System::AnsiString S)
{
return FtpDeleteFile(FFtpHandle, S.c_str());
}
</FONT></PRE>
<P><TT>FtpDeleteFile</TT> takes a handle to the current FTP session in the first
parameter and a string specifying the file to delete in the
second parameter. I find
it hard to imagine how the call could be much simpler.
<H3><A NAME="Heading18"></A><FONT COLOR="#000077">Creating and Removing Directories</FONT></H3>
<P>WININET makes the process of creating and deleting directories trivial.
Each purpose
has one function, and each takes <TT>HINTERNET</TT> for your connection in the first
parameter and the name of the directory you want to create or destroy in the second
parameter:</P>
<PRE><FONT COLOR="#0066FF">BOOL FtpCreateDirectory(
HINTERNET hFtpSession, // Handle to session
LPCTSTR lpszDirectory // Name of directory
);
BOOL FtpRemoveDirectory(
HINTERNET hFtpSession, // Handle to session
LPCTSTR lpszDirectory // Name of directory
</FONT></PRE>
<PRE><FONT COLOR="#0066FF">
);
</FONT></PRE>
<P>The following two simple functions demonstrate how to use the routines:</P>
<PRE><FONT COLOR="#0066FF">
bool __fastcall TMyFtp::CreateDirectory(AnsiString S)
{
return
FtpCreateDirectory(FFtpHandle, S.c_str());
}
bool __fastcall TMyFtp::RemoveDir(System::AnsiString S)
{
return FtpRemoveDirectory(FFtpHandle, S.c_str());
}
</FONT></PRE>
<P>Assuming the presence of these routines, you can then write a function
like the
following to provide an interface with which the user can interact:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::RemoveDirectory1Click(TObject *Sender)
{
AnsiString Title("Delete Directory?");
AnsiString S =
ListBox1->Items->Strings[ListBox1->ItemIndex];
S = *Ftp->CustomToFileName(&S);
if (MessageBox((HWND)Handle, S.c_str(), Title.c_str(),
MB_YESNO | MB_ICONQUESTION) == ID_YES)
{
Ftp->RemoveDir(S);
NewDirClick(NULL);
}
}
</FONT></PRE>
<P>This routine first retrieves the name of the directory you want to delete from
a list box. It then calls the <TT>CustomToFileName</TT> routine, which converts the
string shown in the list box into a simple
directory name by stripping off information
about the size of the directory and the time it was created. The <TT>MessageBox</TT>
function is then used to check with the user to be sure that this action is really
what he or she wants to do. If the user
replies in the affirmative, the directory
is deleted, and the new state of the directory is shown to the user.</P>
<P>Here is a similar function used to create a directory:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall
TForm1::CreateDirectory1Click(TObject *Sender)
{
AnsiString S;
if (InputQuery("Create Directory", "Directory Name", S))
{
Ftp->CreateDirectory(S);
NewDirClick(NULL);
}
}
</FONT></PRE>
<P>In this case,
the VCL InputQuery dialog is invoked. This function takes a title
in the first parameter, a prompt in the second parameter, and the string you want
the user to edit in the third parameter. If the user clicks the OK button in the
dialog, then the
directory is created, and the user's view of the directory is refreshed
by a call to <TT>NewDirClick</TT>.
<H3><A NAME="Heading19"></A><FONT COLOR="#000077">A Sample FTP Control</FONT></H3>
<P>You now know the basics about the process of writing an
FTP control with WININET.
Listings 25.1 and 25.2 show the complete code to a simple control that can set up
an FTP session for you. As is, the control lets you use the Object Inspector to define
the <TT>RemoteServer</TT>, <TT>UserID</TT>, and
<TT>Password</TT>. The code also
automatically returns the current directory in a <TT>TStringList</TT> and allows
you to perform file transfers.</P>
<P>In Listings 25.3 through 25.6, you will find a sample program that uses the control.
The main
screen for the program is shown in Figure 25.3, and a form in which users
can select FTP connections is shown in Figure 25.4.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B><TT>FTP2.CPP</TT> has a
dependency
on <TT>CODEBOX.CPP</TT>. Both files are stored in the <TT>UTILS</TT> directory on
the CD-ROM that accompanies this book. Due to the nature of the C++ linker, <TT>CODEBOX.CPP</TT>
must be compiled to binary form before you can link the
control into the Component
Library. To build <TT>CODEBOX.CPP</TT>, compile the <TT>BUILDOBJS.MAK</TT> project
in the <TT>UTILS</TT> directory. If you do this, and the linker still complains about
<TT>CODEBOX</TT>, then make a small change to
<TT>FTP2.CPP</TT>, such as adding and
then deleting a space, then save your changes and try to compile the component library
again. See the readme file on the CD-ROM that accompanies this book for additional
information. <BR>
<BR>
<TT>FTP2.CPP</TT> needs the <TT>CUNLEASHED</TT> alias set up in the database tools.
The readme file on the disc discusses this alias at some length. Basically, it's
a Paradox alias pointing at the <TT>Data</TT> directory from the CD that accompanies
this book. As always, make sure the source code and data are copied from the CD to
your hard drive before trying to compile or run this program.
<HR>
</BLOCKQUOTE>
<P><A NAME="Heading21"></A><A HREF="25ebu03.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/25/25ebu03.jpg">FIGURE 25.3.</A><FONT
COLOR="#000077">
</FONT><I>The main form for the FTPWININET program. Here I'm connected to <TT>ftp.download.com</TT>,
in the all-important <TT>Games</TT> directory.</I></P>
<P><A NAME="Heading22"></A><A HREF="25ebu04.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/25/25ebu04.jpg">FIGURE 25.4.</A><FONT
COLOR="#000077">
</FONT><I>A form used by the FTPWININET program to allow users to select an FTP connection
from a table.</I></P>
<P><A NAME="Heading23"></A><FONT COLOR="#000077"><B>Listing 25.1. The header file
for the FTP component.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// FTP.h
// FTP Component
// Copyright (c) 1997 by Charlie Calvert
//
#ifndef Ftp2H
#define Ftp2H
class TMyFtp : public Classes::TComponent
{
typedef Classes::TComponent*
inherited;
private:
int FContext;
bool FConnected;
void *FINet;
void *FFtpHandle;
Classes::TStringList* FCurFiles;
System::AnsiString FServer;
Classes::TNotifyEvent FOnNewDir;
System::AnsiString FCurDir;
System::AnsiString
FUserID;
System::AnsiString FPassword;
System::AnsiString __fastcall GetCurrentDirectory(void);
void __fastcall SetUpNewDir(void);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -