TSHFileOpStruct
把一个文件从一个目录拷贝到另一个目录
标 题: Re: 把一个文件从一个目录拷贝到另一个目录
发信站: BBS 水木清华站 (Tue Mar 27 14:38:58 2001)
我来贴个shellAPI解决这类问题的通用办法吧
3.1 整个目录的复制、移动、删除
实例说明
Delphi的用户都知道,删除目录可用RemoveDir函数,但此函数只能删除空目录,如果目
录中有子目录或其它文件,那么此函数就会失败;而删除文件可用DeleteFile函数,但
此函数只能删除单个文件;而且,Delphi并没有提供与空目录或单个文件的复制或移动
相关的函数,更不用说要对整个非空目录以及多个文件的复制、移动和删除了。但通过
本例的分析,读者就能够自由地对任何目录(空目录及非空目录)或多(包括单个)个
文件进行复制、移动、删除了;而且,在上述操作过程中,我们还可以加上进度条,使
整个操作就和在Windows的资源管理器中所作的复制、移动和删除操作完全一样。程序执
行结果如图3.1.1、图3.1.2和图3.1.3所示。
图3.1.1 程序运行主界面
图3.1.2 复制文件过程中的进程条
图3.1.3 复制完毕的提示对话框
设计思想
读者可能想用循环来实现上述功能,但这没有必要,因为有现成的API函数SHFileOpera
tion可以实现上述功能,只需作相应的设置即可。MSDN中此函数的声明为:
WINSHELLAPI int WINAPI SHFileOperation(
LPSHFILEOPSTRUCT lpFileOp //指向SHFILEOPSTRUCT结构体的指针
);
函数执行成功返回0,否则如有错误返回值非0。
SHFILEOPSTRUCT结构体的定义如下:
typedef struct _SHFILEOPSTRUCT { // shfos
HWND hwnd; //显示状态信息窗口的句柄,一般设为主窗体的句柄
UINT wFunc; //要执行的操作
LPCSTR pFrom; //源文件或目录
LPCSTR pTo; //目标文件或目录
FILEOP_FLAGS fFlags; //控制文件操作的标志
BOOL fAnyOperationsAborted; //操作是否放弃
LPVOID hNameMappings; //文件名映射对象的句柄,很少用
LPCSTR lpszProgressTitle; //进度条标题,仅在fFlags标志中指定了//FO
F_SIMPLEPROGRESS时有效
} SHFILEOPSTRUCT, FAR *LPSHFILEOPSTRUCT;
Delphi用记录TSHFileOpStruct对此结构做了两种封装:ANSI字符和宽字符版本。读者可
从ShellAPI单元的第265行看到如下代码:
_SHFILEOPSTRUCTA = packed record //ANSI版本
Wnd: HWND;
wFunc: UINT;
pFrom: PAnsiChar;
pTo: PAnsiChar;
fFlags: FILEOP_FLAGS;
fAnyOperationsAborted: BOOL;
hNameMappings: Pointer;
lpszProgressTitle: PAnsiChar; { only used if FOF_SIMPLEPROGRESS }
end;
{$EXTERNALSYM _SHFILEOPSTRUCTW}
_SHFILEOPSTRUCTW = packed record //宽字符版本
Wnd: HWND;
wFunc: UINT;
pFrom: PWideChar;
pTo: PWideChar;
fFlags: FILEOP_FLAGS;
fAnyOperationsAborted: BOOL;
hNameMappings: Pointer;
lpszProgressTitle: PWideChar; { only used if FOF_SIMPLEPROGRESS }
end;
{$EXTERNALSYM _SHFILEOPSTRUCT}
_SHFILEOPSTRUCT = _SHFILEOPSTRUCTA;
TSHFileOpStructA = _SHFILEOPSTRUCTA;
TSHFileOpStructW = _SHFILEOPSTRUCTW;
TSHFileOpStruct = TSHFileOpStructA;
//默认情况下TSHFileOpStruct为ANSI版本
{$EXTERNALSYM SHFILEOPSTRUCTA}
SHFILEOPSTRUCTA = _SHFILEOPSTRUCTA;
{$EXTERNALSYM SHFILEOPSTRUCTW}
SHFILEOPSTRUCTW = _SHFILEOPSTRUCTW;
{$EXTERNALSYM SHFILEOPSTRUCT}
SHFILEOPSTRUCT = SHFILEOPSTRUCTA;
//默认情况下SHFILEOPSTRUCT为ANSI版本
在ShellAPI单元的第200行对wFunc参数的取值定义了4种操作:
{$EXTERNALSYM FO_MOVE}
FO_MOVE = $0001; //移动操作,从pFrom到pTo
{$EXTERNALSYM FO_COPY}
FO_COPY = $0002; //复制操作,从pFrom到pTo
{$EXTERNALSYM FO_DELETE}
FO_DELETE = $0003; //删除操作,删除pFrom中指定的目录或文件(忽略//
pTo参数)
{$EXTERNALSYM FO_RENAME}
FO_RENAME = $0004; //重命名操作,重命名pFrom中指定的目录或文件。
在ShellAPI单元的第210行对fFlags参数定义了如下可能取值:
{$EXTERNALSYM FOF_MULTIDESTFILES}
FOF_MULTIDESTFILES = $0001; //表明pTo 参数是多个文件而不是一个//目
录
{$EXTERNALSYM FOF_CONFIRMMOUSE}
FOF_CONFIRMMOUSE = $0002; //目前没有实现
{$EXTERNALSYM FOF_SILENT}
FOF_SILENT = $0004; //不创建进度条/报告
{$EXTERNALSYM FOF_RENAMEONCOLLISION}
FOF_RENAMEONCOLLISION = $0008; //当目标文件已存在时,将源文件改
//名再复制或移动
{$EXTERNALSYM FOF_NOCONFIRMATION}
FOF_NOCONFIRMATION = $0010; //操作过程中不显示确认信息,相当//于用
户选择了“yes to all”
{$EXTERNALSYM FOF_WANTMAPPINGHANDLE}
FOF_WANTMAPPINGHANDLE = $0020; //填充hNameMappings成员
{$EXTERNALSYM FOF_ALLOWUNDO}
FOF_ALLOWUNDO = $0040; //允许撤销操作
{$EXTERNALSYM FOF_FILESONLY}
FOF_FILESONLY = $0080; //只操作文件
{$EXTERNALSYM FOF_SIMPLEPROGRESS}
FOF_SIMPLEPROGRESS = $0100; //显示进度条对话框但不显示文件名
{$EXTERNALSYM FOF_NOCONFIRMMKDIR}
FOF_NOCONFIRMMKDIR = $0200; //新建目录时不提示确认
{$EXTERNALSYM FOF_NOERRORUI}
FOF_NOERRORUI = $0400; //如果操作出错,不显示用户借口
设计步骤
新建一应用程序,按照图3.1.1所示加入2个TStaticText组件、2个TEdit组件和5个TBut
ton组件;TstaticText和TButton组件的Caption属性设置为如图所示,TEdit组件的Tex
t属性清空。
在uses中加入FileCtrl,shellapi两单元。SelectDirectory函数用到FileCtrl单元,而
SHFileOperation函数及TSHFileOpStruct记录用到shellapi单元。
代码分析
在第一个“…”按钮的OnClick事件中加入如下代码,用于选择操作的源目录:
procedure TForm1.Button4Click(Sender: TObject);
var
Dir: string;
begin
Dir := 'D:';
if SelectDirectory(Dir, [sdAllowCreate, sdPerformCreate, sdPrompt],0) then
//函数执行成功
// sdAllowCreate:允许创建目录
// sdPerformCreate:执行创建目录
// sdPrompt:显示提示信息
edit1.text:= Dir;
end;
在第二个“…”按钮的OnClick事件中加入如下代码,用于选择操作的目标目录:
procedure TForm1.Button5Click(Sender: TObject);
var
Dir: string;
begin
Dir := 'D:';
if SelectDirectory(Dir, [sdAllowCreate, sdPerformCreate, sdPrompt],0) then
//解释同上
edit2.text:= Dir;
end;
在“复制”按钮的OnClick事件中加入如下代码,用于执行复制操作:
procedure TForm1.Button1Click(Sender: TObject);
var
OpStruc:TSHFileOpStruct;
FromBuf,ToBuf:Array[0..128] of Char;
begin
FillChar(FromBuf,Sizeof(FromBuf),0);
FillChar(ToBuf,Sizeof(ToBuf),0);
//用0初始化FromBuf和ToBuf数组
StrPCopy(FromBuf,Pchar(Edit1.Text));
StrPCopy(ToBuf,Pchar(Edit2.Text));
//分别在 FromBuf和ToBuf数组中填入操作的源目录及目标目录
//开始填充OpStruc记录
with OpStruc do
begin
Wnd:=Handle;
wFunc:=FO_COPY;
//复制操作
pFrom:=@FromBuf;
pTo:=@ToBuf;
fFlags:=FOF_NOCONFIRMATION or FOF_RENAMEONCOLLISION;
fAnyOperationsAborted:=False;
hNameMappings:=nil;
lpszProgressTitle:=nil;
end;
if SHFileOperation(OpStruc)=0 then
//函数执行成功
MessageBox(Handle,'复制完毕。','复制信息',MB_OK+MB_ICONINFORMATION);
end;
在“移动”按钮的OnClick事件中加入如下代码,用于执行移动操作:
procedure TForm1.Button2Click(Sender: TObject);
var
OpStruc:TSHFileOpStruct;
FromBuf,ToBuf:Array[0..128] of Char;
begin
FillChar(FromBuf,Sizeof(FromBuf),0);
FillChar(ToBuf,Sizeof(ToBuf),0);
StrPCopy(FromBuf,Pchar(Edit1.Text));
StrPCopy(ToBuf,Pchar(Edit2.Text));
//开始填充OpStruc记录
with OpStruc do
begin
Wnd:=Handle;
wFunc:=FO_MOVE;
//移动操作
pFrom:=@FromBuf;
pTo:=@ToBuf;
fFlags:=FOF_NOCONFIRMATION or FOF_RENAMEONCOLLISION;
fAnyOperationsAborted:=False;
hNameMappings:=nil;
lpszProgressTitle:='正在文件';
end;
if SHFileOperation(OpStruc)=0 then
//执行成功
MessageBox(Handle,'移动完毕。','移动信息',MB_OK+MB_ICONINFORMATION);
end;
在“删除”按钮的OnClick事件中加入如下代码,用于执行删除操作:
procedure TForm1.Button3Click(Sender: TObject);
var
OpStruc:TSHFileOpStruct;
FromBuf:Array[0..128] of Char;
begin
FillChar(FromBuf,Sizeof(FromBuf),0);
StrPCopy(FromBuf,Pchar(Edit1.Text));
//开始填充OpStruc记录
with OpStruc do
begin
Wnd:=Handle;
wFunc:=FO_DELETE;
pFrom:=@FromBuf;
pTo:=nil;
fFlags:=FOF_NOCONFIRMATION;
lpszProgressTitle:='正在删除';
end;
if SHFileOperation(OpStruc)=0 then
//执行成功
MessageBox(Handle,'删除完毕。','删除信息',MB_OK+MB_ICONINFORMATION);
end;
总结
注意:删除时(wFunc参数设为FO_DELETE)如果想将文件或目录放到回收站(fFlags参
数设置为FOF_ALLOWUNDO)则应该给出文件的绝对路径名,否则可能无法恢复。对于多个
文件的操作,文件名之间要以#0)字符分隔,整个字符串以两个# 0 结束。