我找到了一些使用SHFileOperation()
复制文件的代码,但我对SHFILEOPSTRUCT structure中的所有可用标记感到困惑。
这就是我在做什么。我有一个递归过程,根据我的set-mask填充TStringList
所有文件。我逐步完成TStringList
并传递FileFrom
和FileTo
的完整路径。
F.Wnd:=frmMain.Handle;
F.wFunc:=FO_COPY;
F.pFrom:=PChar(FileFrom+#0);
F.pTo:=PChar(FileTo+#0);
Err:=ShFileOperation(F);
这就是我想要做的事。
如果文件大于10mb,那么我想显示Windows'进度对话框,如果小则显示任何内容。
能够中止复制过程,无需需要单击“进度”对话框中的“X”,因为复制大量较小的文件时可能无法显示。我有一个“取消”按钮和一个布尔“取消点击”,但我看不到如何从SHFileOperation()
返回“中止”。
我知道我可以将整个文件夹传递给SHFileOperation()
并让它以递归方式运行,但我需要在每个文件上处理其他内容,所以踩TStringList
是我需要做的它,但我愿意接受建议。
最后问题:
我需要为10mb以下的尺寸设置fFlags
?
我需要为超过10mb的尺寸设置fFlags
?
if ThisFileSize < 10000000 then
F.fFlags:=F.fFlags or ... else
F.fFlags:= ...;
此代码不会复制,但注释行会复制。
lpCopyProgress:=@MyCopyProgressCallback;
Err:=0;
StopCopy:=False;
if not CopyFileEx(PChar(FileFrom),PChar(FileTo),lpCopyProgress,nil,@StopCopy,0) then
// if not CopyFile(PChar(FileFrom),PChar(FileTo),False) then
begin
Err:=GetLastError;
if Err = ERROR_REQUEST_ABORTED then
Break;
end;
答案 0 :(得分:3)
使用SHFileOperation()
时,您可以使用FOF_SILENT
标志来阻止显示标准进度对话框。
但是,一旦它开始运行,就没有可用于以可编程方式中止SHFileOperation()
的选项。为此,请改用CopyFileEx()
或CopyFile2()
。
这两个函数都允许您以两种不同的方式中止副本:
他们都接受指向BOOL
变量的指针作为输入。如果您的代码在函数运行时将BOOL
变量设置为TRUE
,则副本将被中止。
他们都接受指向进度报告的回调函数的指针。在复制的各个阶段调用回调。如果回调返回值CANCEL
或STOP
,则副本将中止(如果是CANCEL
,则删除目标文件,而STOP
允许文件将在以后恢复。)
无论哪种方式,这两个功能都不会显示Windows&#39;自动拥有进度对话框,但您可以使用display it manually界面IProgressDialog
。或者,您可以显示自己的自定义对话框。
在处理多个文件时,在每个单独的文件上显示/隐藏进度对话框并不是一个好的用户体验。在操作系统上浪费开销来创建和销毁对话框。潜在的闪烁对于用户在视觉上看是不是很有趣。您应该在需要时显示对话框,然后保持可见并更新,直到完成最后一个文件。
尝试这样的事情:
var
// this is redundant since IProgressDialog has its own
// Cancel button, this is just an example to demonstrate
// cancellation in code...
CancelClicked: BOOL = FALSE;
function MyCopyProgressCallback(TotalFileSize, TotalBytesTransferred, StreamSize, StreamBytesTransferred: LARGE_INTEGER; dwStreamNumber: DWORD; dwCallbackReason: DWORD; hSourceFile, hDestinationFile: THandle; lpData: Pointer): DWORD; stdcall;
var
msg: WideString;
begin
msg := WideFormat('Transferred %d of %d bytes', [TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart]);
IProgressDialog(lpData).SetLine(2, PWideChar(msg), False, PPointer(nil)^);
if IProgressDialog(lpData).HasUserCancelled then
Result := PROGRESS_CANCEL
else
Result := PROGRESS_CONTINUE;
end;
...
var
FileFrom: string;
FileTo: string;
I: Integer;
ProgressDialog: IProgressDialog;
begin
...
CancelClicked := FALSE;
OleCheck(CoCreateInstance(CLSID_ProgressDialog, nil, CLSCTX_INPROC_SERVER, IProgressDialog, ProgressDialog));
try
ProgressDialog.SetTitle('Processing files');
ProgressDialog.SetCancelMsg('Canceling, please wait...', PPointer(nil)^);
ProgressDialog.SetProgress(0, TheStringList.Count);
ProgressDialog.StartProgressDialog(frmMain.Handle, nil, PROGDLG_MODAL or PROGDLG_AUTOTIME or PROGDLG_NOMINIMIZE, PPointer(nil)^);
ProgressDialog.Timer(PDTIMER_RESET, PPointer(nil)^);
for I := 0 to TheStringList.Count-1 do
begin
FileFrom := ...;
FileTo := ...;
ProgressDialog.SetLine(1, PWideChar(WideString(FileFrom)), True, PPointer(nil)^);
ProgressDialog.SetLine(2, '', False, PPointer(nil)^);
if ProgressDialog.HasUserCancelled then
Break;
...
if not CopyFileEx(PChar(FileFrom), PChar(FileTo), @MyCopyProgressCallback, Pointer(ProgressDialog), @CancelClicked, 0) then
begin
if GetLastError = ERROR_REQUEST_ABORTED then
Break;
// something else happened during the copy, so
// you can decide whether to stop the loop here
// or just move on to the next file...
end;
...
ProgressDialog.SetProgress(I+1, TheStringList.Count);
end;
finally
ProgressDialog.StopProgressDialog;
ProgressDialog := nil;
end;
...
end;
或者,您可以使用IFileOperation
界面。这允许您:
使用CopyItem()
和CopyItems()
方法提前排队所有文件路径。
使用PerformOperations()
方法一次执行所有排队副本。
实施IFileOperationProgressSink
界面,Advise()
接口以接收进度更新。这包括在复制每个文件之前(PreCopyItem()
)和之后(PostCopyItem()
)执行您自己的操作的选项。从IFileOperationProgressSink
方法返回的任何错误都将中止整个复制序列。
让它自动显示标准的Windows进度对话框。它将延迟显示对话框,直到复制序列运行超过几秒钟。如果快速复制一堆小文件,则无需显示对话框,但一旦发生明显的延迟,将显示当前和后续文件的对话框,直到序列完成。如果要自定义行为,可以实现IOperationsProgressDialog
界面并将其传递给IFileOperation::SetProgressDialog()
方法。