2016-04-20 1 views
2

Ich habe einige Code zum Kopieren von Dateien mit SHFileOperation() gefunden, aber ich bin verwirrt durch alle Flags in der SHFILEOPSTRUCT structure.Datei kopieren über SHFileOperation() - grundlegende Flags zu verwenden

Hier ist, was ich tue. Ich habe einen rekursiven Prozess, der eine TStringList mit allen Dateien gemäß meiner Set-Maske füllt. Ich gehe durch das TStringList und überlasse vollständige Pfade für FileFrom und FileTo.

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

Folgendes möchte ich tun.

  1. Wenn eine Datei größer als 10 MB ist, dann möchte ich Windows 'Progress Dialog anzeigen, wenn kleiner dann nichts anzeigen.

  2. Lage sein, den Kopiervorgang ohne benötigen die „X“ im Dialogfenster zu klicken, da es nicht angezeigt werden kann, um den Vorgang abzubrechen, wenn eine Menge von kleineren Dateien zu kopieren. Ich habe einen "Cancel" -Button und einen booleschen "CancelClicked", aber ich kann nicht sehen, wie man einen "Abort" von der SHFileOperation() zurückbekommt.

Ich weiß, dass ich nur den gesamten Ordner zu SHFileOperation() passieren könnte und habe es rekursiv arbeiten, aber ich muß auf jede Datei andere Sachen verarbeiten, so wenn die TStringList Schritt ist, wie ich es tun müssen, aber ich bin offen für Vorschläge.

Schließlich werden die Fragen:

Welche fFlags Benötige ich für die unter-10 MB Größe festgelegt?

Was fFlags brauche ich für die über-10mb Größe eingestellt?

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

Dieser Code nicht kopiert wird, aber die Kommentarzeile kopiert.

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

Verwenden Sie FOF_SILENT für kleine Dateien, alle Flags werden in der Dokumentation erklärt. –

+0

Willkommen bei Stack Overflow. Du hast bewiesen, dass du weißt, wo die Dokumentation ist. Hast du es gelesen? Du hast den Teil über 'fof_Silent' gesehen, richtig? Hast Du es versucht? Welche weitere Frage hast du? Bitte [bearbeiten] Sie Ihre Frage zur Klärung. –

+0

@Rob Kennedy: Danke, ich frage mich, ob ich mehr als nur das haben sollte, wie "FOF_FILESONLY" und/oder "FOF_NOCONFIRMATION" und/oder "FOF_NOERRORUI" und/oder ... etc. Genau was angenommen wird (Standard) ohne Flags und was ich definieren muss. – user3272241

Antwort

3

Wenn SHFileOperation() verwenden, können Sie die FOF_SILENT Flags verwenden, um den Standard-Fortschrittsdialog aus wird gezeigt zu verhindern.

Es ist jedoch keine Option zum programmierbaren Abbruch SHFileOperation() verfügbar, sobald es gestartet wird. Verwenden Sie dafür stattdessen CopyFileEx() oder CopyFile2().

Beide Funktionen können Sie eine Kopie auf zwei verschiedene Arten abbrechen:

  • sie beide akzeptieren einen Zeiger auf eine BOOL Variable als Eingabe. Wenn Ihr Code die BOOL Variable auf TRUE setzt, während die Funktion ausgeführt wird, wird die Kopie abgebrochen.

  • beide akzeptieren einen Zeiger auf eine Callback-Funktion für Fortschrittsberichte. Der Rückruf wird während verschiedener Phasen der Kopie aufgerufen. Wenn der Callback einen Wert von CANCEL oder STOP zurückgibt, wird die Kopie abgebrochen (und im Fall von CANCEL wird die Zieldatei gelöscht, während STOP die Datei zu einem späteren Zeitpunkt wieder aufnehmen kann).

So oder so, beide Funktionen nicht angezeigt eigenen Fortschritt Dialog von Windows automatisch, aber Sie können display it manually mit der IProgressDialog Schnittstelle. Oder Sie können stattdessen Ihren eigenen benutzerdefinierten Dialog anzeigen.

Es ist keine gute Benutzererfahrung, einen Fortschrittsdialog für jede einzelne Datei anzuzeigen/auszublenden, wenn mehrere Dateien verarbeitet werden. Es ist überflüssiger Aufwand für das Betriebssystem, die Dialoge zu erstellen und zu zerstören. Das potenzielle Flackern ist für den Benutzer kein Spaß, um visuell zu sehen. Sie sollten den Dialog einmal anzeigen, wenn er benötigt wird, und ihn dann sichtbar und aktualisiert halten, bis die letzte Datei fertig ist.

versuchen, etwas wie folgt aus:

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; 

Alternativ können Sie die IFileOperation Schnittstelle stattdessen verwenden. Dies ermöglicht Ihnen:

  • Warteschlange alle Dateipfade vor der Zeit seiner CopyItem() und CopyItems() Methoden.

  • führen Sie alle in der Warteschlange befindlichen Kopien gleichzeitig mit der Methode PerformOperations() aus.

  • implementieren Sie die IFileOperationProgressSink Schnittstelle und Advise() es, um Fortschrittsupdates zu erhalten. Dies beinhaltet die Möglichkeit, eigene Operationen durchzuführen (PreCopyItem()) und nach (PostCopyItem()) jede einzelne Datei zu kopieren. Jeder Fehler, den Sie von einer IFileOperationProgressSink-Methode zurückgeben, bricht die gesamte Kopiersequenz ab.

  • Lassen Sie es den Standard-Windows-Fortschrittsdialog automatisch anzeigen lassen. Das Anzeigen des Dialogfelds wird verzögert, bis die Kopiersequenz länger als einige Sekunden dauert. Wenn Sie eine Menge kleiner Dateien schnell kopieren, muss der Dialog nicht angezeigt werden. Sobald jedoch eine merkliche Verzögerung auftritt, wird der Dialog für die aktuelle und die folgenden Dateien angezeigt, bis die Sequenz beendet ist. Wenn Sie das Verhalten anpassen möchten, können Sie die Schnittstelle IOperationsProgressDialog implementieren und an die Methode IFileOperation::SetProgressDialog() übergeben.

+0

Danke, aber das kompilierte nicht wie gezeigt, so dass eine schnelle Suche vorgeschlagen, dass "lpCopyProgress: sollte" @lpCopyProgress "und dann kompiliert werden. Aber es ist nicht kopieren und LastError = 0. – user3272241

+0

Nein, es sollte nicht' @ lpCopyProgress'. Ich habe das Beispiel in meiner Antwort aktualisiert. Es ist auch nicht möglich, dass 'GetLastError()' 0 zurückgibt, wenn 'CopyFileEx() 'fehlschlägt, was bedeutet, dass der Code keine Fehlerbehandlung korrekt durchführt. –

+0

Ich habe mich geändert zu der angepassten Quelle, aber es kopiert immer noch nicht.Wenn ich dieses 'falls nicht CopyFile (PChar (FileFrom), PChar (FileTo), False) 'verwende, dann kopiert es die Dateien. Ich werde dem obigen OP tatsächlichen Code hinzufügen. – user3272241