2016-04-20 61 views
2

我發現了一些使用SHFileOperation()來複制文件的代碼,但我對SHFILEOPSTRUCT structure中可用的所有標誌感到困惑。通過SHFileOperation()進行文件複製 - 使用基本標誌

這是我正在做的。我有一個遞歸過程,根據我的設置掩碼填充所有文件的TStringList。我逐步通過該TStringList並通過完整路徑爲FileFromFileTo

F.Wnd:=frmMain.Handle; 
F.wFunc:=FO_COPY; 
F.pFrom:=PChar(FileFrom+#0); 
F.pTo:=PChar(FileTo+#0); 
Err:=ShFileOperation(F); 

這就是我想要做的。

  1. 如果一個文件大於10MB,那麼我想顯示Windows的進度對話框,如果更小,則不顯示任何內容。

  2. 能夠中止複製過程沒有需要點擊進度對話框中的「X」,因爲當複製大量小文件時它可能不會顯示。我有一個「取消」按鈕和一個布爾型「CancelClicked」,但我看不到如何從SHFileOperation()回到「中止」。

我知道我可以只通過整個文件夾SHFileOperation()並將其遞歸地工作,但我需要處理的每個文件的其他東西,所以步進雖然TStringList是我需要怎麼做,但我我接受建議。

最後的問題:

什麼fFlags,我需要設置下,10MB大小?

什麼fFlags我需要設置超過10MB大小?

if ThisFileSize < 10000000 then 
    F.fFlags:=F.fFlags or ... else 
    F.fFlags:= ...; 

這個代碼不復制,但註釋行所做的副本。

lpCopyProgress:[email protected]; 
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

對於小文件使用FOF_SILENT,所有標誌在文檔中都有解釋。 –

+0

歡迎來到Stack Overflow。你已經證明你知道文檔的位置。你讀過了嗎?你看到了關於'fof_Silent'的部分,對吧?你試過了嗎?你還有什麼問題?請[編輯]你的問題來澄清。 –

+0

@Rob Kennedy:謝謝,我想知道我是否應該有更多,比如「FOF_FILESONLY」和/或「FOF_NOCONFIRMATION」和/或「FOF_NOERRORUI」和/或等等。只是假設(默認)沒有標誌和我需要定義的內容。 – user3272241

回答

3

使用SHFileOperation(),您可以使用FOF_SILENT標誌,以防止標準進程對話框被顯示。

但是,一旦開始運行,就沒有可用於可編程地中止SHFileOperation()的選項。爲此,請改爲使用CopyFileEx()CopyFile2()

兩個功能允許您以兩種不同的方式中止副本:

  • 他們都接受一個指向BOOL變量作爲輸入。如果您的代碼在函數運行時將BOOL變量設置爲TRUE,則副本將中止。

  • 它們都接受指向進度報告的回調函數的指針。回調在複製的各個階段被調用。如果回調的返回值爲CANCELSTOP,則複製被中止(並且在CANCEL的情況下,目標文件被刪除,而STOP允許稍後恢復文件)。

無論哪種方式,這兩種功能不會自動顯示Windows自帶的進度對話框,但您使用IProgressDialog接口可以display it manually。或者,您可以改爲顯示自己的自定義對話框。

處理多個文件時,在每個單獨文件上顯示/隱藏進度對話框的用戶體驗不是很好。它在操作系統上被浪費在創建和銷燬對話框上。潛在的閃爍對於用戶直觀地查看並不有趣。在需要時應該顯示對話框一次,然後保持可見並更新,直到完成最後一個文件。

嘗試這樣:

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()方法。

+0

謝謝,但沒有編譯如圖所示,所以快速搜索提示「lpCopyProgress:應該是」@lpCopyProgress「,然後編譯它。但是,它不是複製並且LastError = 0. – user3272241

+0

不,它不應該是@ lpCopyProgress'。我在我的答案中更新了示例,並且,如果'CopyFileEx()'失敗,'GetLastError()'不可能返回0,這意味着你的代碼沒有正確地進行錯誤處理。 –

+0

我改變了如果我使用這個'如果不是CopyFile(PChar(FileFrom),PChar(FileTo),False)然後'它複製這些文件,我將在上面的OP中添加實際代碼 – user3272241