用 Windows Internet 函数进行 FTP 操作的简介?
--------------------------------------------------------------------------------
目录:
初步内容与INET句柄
打开进程取得操作句柄
开始连接
获取当前路径
获取文件/目录信息
文件的传输
范例
--------------------------------------------------------------------------------
1、初步内容与INET句柄...
让我们先来对您在FTP部分使用WININETDLL时需要编写的代码作一个做一个概括的了解。这并不是一个详尽的学习,但却能够让您进门。为了知晓这项技术,您要做的第一件事情是明白WININET.PAS中的一些函数返回的是一个叫做HINTERNET类的指针变量:
var
HINTERNET : Pointer;
这个指针扮演一个您正在使用的不同的因特网服务的句柄的角色。获得了这个句柄之后,你应当把它作为第一个参数传递给在这个进程周期[注:指FTP的整个存在时间(译者)]中调用的其他WININET函数。
您要记住的适当您在使用它的时间内要把句柄返回给系统,通常是通过调用WININET函数InternetCloseHandle来实现:
function InternetCloseHandle(hInet : HINTERNET) : BOOL; stdcall;
2、打开进程取得操作句柄...
为了让一个WININET进程开始,您可以调用InternetOpen:
function InternetOpen(
lpszCallerName : PChar;
dwAccessType : DWORD;
lpszServerName : PChar;
nServerPort : INTERNET_PORT;
dwFlags : DWORD) : HINTERNET; stdcall;
第一个参数时打开这个进程的应用程序的名字。您可以在这个参数中传递任何您所要的任意符串。微软公司的文献声称"这个名字作为HTTP协议中的用户代理器的名字而被使用"。这个保留的参数可以设为0或空。
var
MyHandle : HINTERNET;
…
begin
MyHandle := InternetOpen('MyApp', 0, nil, 0, 0);
end;
如果您想要关于这个函数的更多信息,从
www.microsoft.com
那里下载 WININET.HLP。
3、开始连接...
打开了这这个进程之后,下一步是通过 InternetConnect 函数来连接到服务器上。
function InternetConnect(
hInet : HINTERNET; //Handle from InternetOpen
lpszServerName : PChar; //Server, i.e.
www.borland.com
nServerPort : INTERNET_PORT; //Usually 0
lpszUsername : PChar; //usually anonymous
lpszPassword : PChar; //usually your email address
dwService : DWORD; //FTP, HTTP, or Gopher?
dwFlags : DWORD; //Usually 0
dwContext : DWORD): //User defined number for callback
HINTERNET; stdcall;
这里有三个可能的可以通过dwService参数传递的自说明旗标,它们是互斥的:
INTERNET_SERVICE_FTP
INTERNET_SERVICE_GOPHER
INTERNET_SERVICE_HTTP
下面是dwFlags参数的选择:
INTERNET_CONNECT_FLAG_PASSIVE
这个选项仅当您在前一个参数中传递了 INTERNET_SERVER_FTP 才有效。这时候这个参数没有其他有效的选项。
如果这个进程成功的话会返回一个有效的指针,否则它返回空。
4、获取当前路径...
当您连接上之后,您可以调用来 GetCurrentDirectory 获得当前的路径的名字:
function TMyFtp.GetCurrentDirectory : string;
var
Len:Integer;
S:string;
begin
Len:=0;
ftpGetCurrentDirectory(FFTPHandle, PChar(S), Len);
SetLength(S, Len);
ftpGetCurrentDirectory(FFTPHandle, PChar(S), Len);
Result := S;
end;
这个函数声明如下:
function FtpGetCurrentDirectory(
hFtpSession : HINTERNET; //handle from InternetConnect
lpszCurrentDirectory : PChar; //directory returned here
varlpdwCurrentDirectory : DWORD): //buf size of 2nd parameter
BOOL; stdcall; //Trueonsuccess
如果您把最后一个参数设为 0,那么 WININET 会使用这个参数来返回路径字符串的长度。接着您可以为您的字符串分配内存,也可以在调用一次这个函数来获得路径的名字。这个过程在上面的方法中已经演示过了。(注意到*设定长度*的那个调用,Delphi 要求您在类似这样的情况下为新的长字符串分配内存!这是因为这个字符串必须在操作系统中指定值,而不是在 Delphi 应用程序中指定。结果就是Delphi不能在类似的情况下像它通常那样悄悄地为字符串分配内存)
5、获取文件/目录信息...
下面是返回在特定路径下当前可用的文件的一系列函数:
function GetFindDataStr(FindData:TWin32FindData) : string;
var
S : string;
Temp : string;
begin
case FindData.dwFileAttributes of
FILE_ATTRIBUTE_ARCHIVE : S := 'A';
// FILE_ATTRIBUTE_COMPRESSED : S := 'C';
FILE_ATTRIBUTE_DIRECTORY : S := 'D';
FILE_ATTRIBUTE_HIDDEN : S := 'H';
FILE_ATTRIBUTE_NORMAL : S := 'N';
FILE_ATTRIBUTE_READONLY : S := 'R';
FILE_ATTRIBUTE_SYSTEM : S := 'S';
FILE_ATTRIBUTE_TEMPORARY : S := 'T';
else
S := IntToStr(FindData.dwFileAttributes);
end;
S := S+GetDots(75);
Move(FindData.CFilename[0], S[6], StrLen(FindData.CFileName));
Temp := IntToStr(FindData.nFileSizeLow);
Move(Temp[1], S[25], Length(Temp));
Result := S;
end;
function TMyFtp.FindFiles : TStringList;
var
FindData : TWin32FindData;
FindHandle : HInternet;
begin
FindHandle := FtpFindFirstFile(FFtphandle, '*.*', FindData, 0, 0);
if FindHandle = nil then
begin
Result := nil;
Exit;
end;
FCurFiles.Clear;
FCurFiles.Add(GetFindDataStr(FindData));
while InternetFindnextFile(FindHandle,@FindData) do
FCurFiles.Add(GetFindDataStr(FindData));
InternetCloseHandle(Findhandle);
GetCurrentDirectory;
Result := FCurFiles;
end;
这里需要注意的关键函数是 ftpFindFirstFile, InternetFindNextFile & InternetCloseHandle。您可以像调用 Delphi 函数FindFirst、FindNext & FinClose 一样调用这些函数。特别的是,您使用函数ftpFindFirstFile来取得这个路径下的第一个函数。您可以不断地调用 InternetFindNextFile,直到函数返回"False"为止。当这个进程结束时,调用InternetCloseHandle来通知操作系统回收与这个进程相关的内存。
如果您想要更多的信息,您可以在 Delphi 帮助中查找 FindFirst。最后提醒一句:并不向前文提及的函数,TWin32FindData 并不是在 WININET.PAS 中定义的,但可以在随 Delphi 分发的 WIN32 帮助文件中找到它。它在随 Delphi 分发的WINDOWS.PAS文件中被定义。
6、文件的传输...
您可以使用 WININET.PAS 文件中的 ftpGetFile 函数来从 FTP 取回一个文件:
function FtpGetFile(
hFtpSession : HINTERNET; //Returned by InternetConnect
lpszRemoteFile : PChar; //File to get
lpszNewFile : PChar; //Where to put it on your PC
fFailIfExists : BOOL; //Over write existing files?
dwFlagsAndAttributes : DWORD; //File attribute-See Create File.
dwFlags : DWORD; //Binaryor ASCII transfer
dwContext : DWORD): //Usually zero
BOOL; stdcall; //Trueonsuccess
下面是一个如何使用该函数的例子:
function TMyFtp.GetFile(FTPFile, NewFile : string) : Boolean;
begin
Result := FtpGetFile(
FFTPHandle,
PChar(FTPFile),
PChar(NewFile),
False,
File_Attribute_Normal,
Ftp_Transfer_Type_Binary,
0);
end;
如果要知道 dwFlagsAndAttributes 参数中的变量是怎样传递的,请查阅随 Delphi 附送的WIN32帮助文件。
7、范例...
下面的 Delphi 控制给了你一个通过 WININETFTP 部分建立可视工具的起点。只是因为,这个控制可以让您是用 ObjectInspector 来定义远程服务器(RemoteServer)、用户身份(UserID)和密码(Password)。
unit Ftp1;
{ FTP example using WININET.PAS rather than
an ACTIVEX control. Requires WININET.PAS and
WININET.DLL. WININET.DLL you can get from
Microsoft, WININET.PAS is available from
www.borland.com
, or with some versions of
Delphi 2.0.
You might Respond to OnNewDir events as follows:
procedure TForm1.FTP1NewDir(Sender: TObject);
begin
ListBox1.Items := MyFtp1.FindFiles; // Get the directory list
end;
}
interface
uses
Windows,Classes,WinINet,SysUtils;
type
TMyFtp = class(TComponent)
private
FContext: Integer;
FINet: HInternet;
FFtpHandle: HInternet;
FCurFiles: TStringList;
FServer: string;
FOnNewDir: TNotifyEvent;
FCurDir: string;
FUserID: string;
FPassword: string;
function GetCurrentDirectory: string;
procedure SetUpNewDir;
protected
destructor Destroy; override;
public
constructor Create(AOwner: TComponent); override;
function Connect: Boolean;
function FindFiles: TStringList;
function ChangeDirExact(S: string): Boolean;
function ChangeDirCustom(S: string): Boolean;
function BackOneDir: Boolean;
function GetFile(FTPFile, NewFile: string): Boolean;
function SendFile1(FTPFile, NewFile: string): Boolean;
function SendFile2(FTPFile, NewFile: string): Boolean;
function CustomToFileName(S: string): string;
published
property CurFiles: TStringList read FCurFiles;
property CurDir: string read FCurDir;
property UserID: string read FUserID write FUserID;
property Password: string read FPassword write FPassword;
property Server: string read FServer write FServer;
property OnNewDir: TNotifyEvent read FOnNewDir write FOnNewDir;
end;
procedure Register;
implementation
uses
Dialogs;
// A few utility functions
function GetFirstToken(S: string; Token: Char): string;
var
Temp: string;
Index: INteger;
begin
Index := Pos(Token, S);
if Index < 1 then begin
GetFirstToken := '';
Exit;
end;
Dec(Index);
SetLength(Temp, Index);
Move(S[1], Temp[1], Index);
GetFirstToken := Temp;
end;
function StripFirstToken(S: string; Ch: Char): string;
var
i, Size: Integer;
begin
i := Pos(Ch, S);
if i = 0 then begin
StripFirstToken := S;
Exit;
end;
Size := (Length(S) - i);
Move(S[i + 1], S[1], Size);
SetLength(S, Size);
StripFirstToken := S;
end;
function ReverseStr(S: string): string;
var
Len: Integer;
Temp: String;
i,j: Integer;
begin
Len := Length(S);
SetLength(Temp, Len);
j := Len;
for i := 1 to Len do begin
Temp[i] := S[j];
dec(j);
end;
ReverseStr := Temp;
end;
function StripLastToken(S: string; Token: Char): string;
var
Temp: string;
Index: INteger;
begin
SetLength(Temp, Length(S));
S := ReverseStr(S);
Index := Pos(Token, S);
Inc(Index);
Move(S[Index], Temp[1], Length(S) - (Index - 1));
SetLength(Temp, Length(S) - (Index - 1));
StripLastToken := ReverseStr(Temp);
end;
procedure Register;
begin
RegisterComponents('Unleash', [TMyFtp]);
end;
constructor TMyFtp.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCurFiles := TStringList.Create;
FINet := InternetOpen('WinINet1', 0, nil, 0, 0);
end;
destructor TMyFtp.Destroy;
begin
if FINet <> nil then
InternetCloseHandle(FINet);
if FFtpHandle <> nil then
InternetCloseHandle(FFtpHandle);
inherited Destroy;
end;
function TMyFtp.Connect: Boolean;
begin
FContext := 255;
FftpHandle := InternetConnect(FINet, PChar(FServer), 0,
PChar(FUserID), PChar(FPassWord),
Internet_Service_Ftp, 0, FContext);
if FFtpHandle = nil then
Result := False
else begin
SetUpNewDir;
Result := True;
end;
end;
function TMyFtp.GetCurrentDirectory: string;
var
Len: Integer;
S: string;
begin
Len := 0;
ftpGetCurrentDirectory(FFTPHandle, PChar(S), Len);
SetLength(S, Len);
ftpGetCurrentDirectory(FFTPHandle, PChar(S), Len);
Result := S;
end;
procedure TMyFtp.SetUpNewDir;
begin
FCurDir := GetCurrentDirectory;
if Assigned(FOnNewDir) then
FOnNewDir(Self);
end;
function GetDots(NumDots: Integer): string;
var
S: string;
i: Integer;
begin
S := '';
for i := 1 to NumDots do
S := S + ' ';
Result := S;
end;
function GetFindDataStr(FindData: TWin32FindData): string;
var
S: string;
Temp: string;
begin
case FindData.dwFileAttributes of
FILE_ATTRIBUTE_ARCHIVE: S := 'A';
// FILE_ATTRIBUTE_COMPRESSED: S := 'C';
FILE_ATTRIBUTE_DIRECTORY: S := 'D';
FILE_ATTRIBUTE_HIDDEN: S := 'H';
FILE_ATTRIBUTE_NORMAL: S := 'N';
FILE_ATTRIBUTE_READONLY: S := 'R';
FILE_ATTRIBUTE_SYSTEM: S := 'S';
FILE_ATTRIBUTE_TEMPORARY: S := 'T';
else
S := IntToStr(FindData.dwFileAttributes);
end;
S := S + GetDots(75);
Move(FindData.CFilename[0], S[6], StrLen(FindData.CFileName));
Temp := IntToStr(FindData.nFileSizeLow);
Move(Temp[1], S[25], Length(Temp));
Result := S;
end;
function TMyFtp.FindFiles: TStringList;
var
FindData: TWin32FindData;
FindHandle: HInternet;
begin
FindHandle := FtpFindFirstFile(FFtphandle, '*.*',
FindData, 0, 0);
if FindHandle = nil then begin
Result := nil;
Exit;
end;
FCurFiles.Clear;
FCurFiles.Add(GetFindDataStr(FindData));
while InternetFindnextFile(FindHandle, @FindData) do
FCurFiles.Add(GetFindDataStr(FindData));
InternetCloseHandle(Findhandle);
GetCurrentDirectory;
Result := FCurFiles;
end;
function TMyFtp.CustomToFileName(S: string): string;
const
PreSize = 6;
var
Temp: string;
TempSize: Integer;
begin
Temp := '';
TempSize := Length(S) - PreSize;
SetLength(Temp, TempSize);
Move(S[PreSize], Temp[1], TempSize);
Temp := GetFirstToken(Temp, ' ');
Result := Temp;
end;
function TMyFtp.BackOneDir: Boolean;
var
S: string;
begin
S := FCurDir;
S := StripLastToken(S, '/');
if S = '/' then begin
Result := False;
Exit;
end;
if S <> '' then begin
ChangeDirExact(S);
Result := True;
end else begin
ChangeDirExact('/');
Result := True;
end;
end;
// Changes to specific directory in S
function TMyFtp.ChangeDirExact(S: string): Boolean;
begin
if S <> '' then
FtpSetCurrentDirectory(FFTPHandle, PChar(S));
Result := True;
FindFiles;
SetUpNewDir;
end;
// Assumes S has been returned by GetFindDataString;
function TMyFtp.ChangeDirCustom(S: string): Boolean;
begin
S := CustomToFileName(S);
if S <> '' then
FtpSetCurrentDirectory(FFTPHandle, PChar(S));
Result := True;
FindFiles;
SetUpNewDir;
end;
function TMyFtp.GetFile(FTPFile, NewFile: string): Boolean;
begin
Result := FtpGetFile(FFTPHandle, PChar(FTPFile), PChar(NewFile),
False, File_Attribute_Normal, Ftp_Transfer_Type_Binary, 0);
end;
function TMyFtp.SendFile1(FTPFile, NewFile: string): Boolean;
const
Size:DWord = 3000;
var
Transfer: Bool;
Error: DWord;
S: string;
begin
Transfer := FtpPutFile(FFTPHandle, PChar(FTPFile),
PChar(NewFile), Ftp_Transfer_Type_Binary, 0);
if not Transfer then begin
Error := GetLastError;
ShowMessage(Format('Error Number: %d. Hex: %x',
[Error, Error]));
SetLength(S, Size);
if not InternetGetLastResponseInfo(Error, PChar(S), Size) then
begin
Error := GetLastError;
ShowMessage(Format('Error Number: %d. Hex: %x', [Error, Error]));
end;
ShowMessage(Format('Error Number: %d. Hex: %x Info: %s', [Error, Error, S]));
end else
ShowMessage('Success');
Result := Transfer;
end;
function TMyFtp.SendFile2(FTPFile, NewFile: string): Boolean;
var
FHandle: HInternet;
begin
FHandle := FtpOpenFile(FFTPHandle, 'sam.txt', GENERIC_READ, FTP_TRANSFER_TYPE_BINARY, 0);
if FHandle <> nil then
InternetCloseHandle(FHandle)
else
ShowMessage('Failed');
Result := True;
end;
end