C++ Builder Snippets  

Fortschritt beim Kopieren von Dateien anzeigen:
 
Nicht selten ist es sinnvoll, beim Kopieren von Dateien den Fortschritt anzuzeigen. Insbesondere bei großen Dateien und langsamen Laufwerken oder Netzwerkverbindungen könnte der Anwender anderenfalls den Eindruck bekommen, dass das Programm sich aufgehängt hat.

Da die VCL- und API-Umsetzungen der Kopierfunktion für Dateien keine flexible Möglichkeit bieten, den Fortschritt beim Kopiervorgang zu visualisieren, muss in diesem Fall eine eigene Funktion zum Kopieren der Dateien eingesetzt werden.



Die Implementierung könnte z.B. folgendermassen aussehen:

Die Kopierfunktion ExtFileCopy() (s. unten) erwartet ausser der üblichen Parameter einen Zeiger auf die Funktion, die sich um die eigentliche Visualisierung kümmert. Dieser wird folgendermaßen definiert:
// Definition des Zeigertyps für die Fortschrittsaktualiserungsfunktion:
typedef void __fastcall (__closure * pfUpdateStatus)(double, DWORD, DWORD);
In diesem Zeiger wird der Kopierfunktion die Addresse der "Visualisierungsroutine" übergeben. Sobald eine Aktualisierung der Fortschrittsanzeige fällig ist, ruft die Kopierfunktion diese Routine auf und übergibt ihr im 1. Parameter den Fortschritt in Prozent, im 2. die Anzahl der bereits kopierten Bytes und im 3. die Dateigröße. Was (und ob) mit diesen Daten angestellt werden soll, wird in der "Visualisierungsroutine" festgelegt, z.B. so:
//---------------------------------------------------------------------------
// Funktion für die Aktualisierung der Fortschrittsanzeigen (wird in
// definierten Abständen von der Funktion ExtFileCopy() aufgerufen.
//---------------------------------------------------------------------------
void __fastcall TForm1::UpdateCopyStatus(double dlPercent, DWORD ilBytesCopied,
  DWORD ilBytesTotal)
{
  lblPercent->Caption = FloatToStrF(dlPercent, ffFixed, 15,0) + " % ("
    + IntToStr(ilBytesCopied) + " von " + IntToStr(ilBytesTotal) +
    " Bytes) kopiert";
  ProgressBar1->Position = int(dlPercent);
  ProgressBar2->Position = int(dlPercent);
}

ExtFileCopy() löst während des Kopierens eine Art "FortschrittUpdate-Ereignis" aus und die Funktion TForm1::UpdateCopyStatus() ist die Ereignisbehandlungsroutine dafür.

Die eigentliche Kopierfunktion sieht folgendermaßen aus:
//---------------------------------------------------------------------------
// Funktion ExtFileCopy() kopiert Datei slSourceFile nach slDestFile
//---------------------------------------------------------------------------
// Parameter:
//
// AnsiString slSourceFile -> Name der zu kopierenden Datei (mit Pfad !)
// AnsiString slDestFile   -> Name der Zieldatei mit Pfad (wird hier ein
//                            Verzeichnis angegeben, wird die Datei in dieses
//                            Verzeichnis unter dem der Quelldatei kopiert
// double dlUpdateInterval -> "Schrittweite" für die Aktualiserung der
//                            Fortschrittsanzeige in Prozent von der
//                            Dateigrösse.
// pfUpdateStatus plFunc   -> Zeiger auf die Fortschritts-Aktualisierungs-
//                            funktion
//---------------------------------------------------------------------------
// Rückgabewert:
//
// true  -> bei erfolgreicher Ausführung
// false -> bei Ausführungsfehlern
//---------------------------------------------------------------------------
//
// Definition des Zeigertyps für die Statusaktualiserungsfunktion:
//
// typedef void __fastcall (__closure * pfUpdateStatus)(double, DWORD, DWORD);
//
// Parameter 1: Kopier-Fortschritt in Prozent
// Parameter 2: Anzahl der bereits kopierten Bytes
// Parameter 1: Die Dateigröße in Bytes
//---------------------------------------------------------------------------
int ExtFileCopy(AnsiString slSourceFile, AnsiString slDestFile,
  double dlUpdateInterval, pfUpdateStatus plFunc)
{
  int ilBufSize = 8192;                // Puffer-Grösse
  bool blRetVal = false;               // Rückgabewert
  int ilSourceHandle, ilDestHandle;    // Dateien-Handles
  DWORD ilFileSize = 0;                // Dateigrösse
  DWORD ilBytesTotal = 0;              // Anz. der bereits kopierten Bytes
  int ilBytesLastWrite = 0;            // Anz. der im aktuellen
                                       // Schleifendurchlauf kopierten Bytes
  double dlLastValue = 0;              // zuletzt ausgegebener Fortschrittswert
  int ilBytes = 0;                     // Anz. der in Lesepuffer eingelesenen
                                       // Bytes

  // Falls ein Verzeichnis als Name der Zieldatei übergeben, Namen der
  // Quelldatei an den Verzeichnisnamen anhängen:
  int ilAttr = FileGetAttr(slDestFile);
  if (ilAttr != -1 && ilAttr & faDirectory)
    slDestFile = IncludeTrailingBackslash(slDestFile) +
      ExtractFileName(slSourceFile);

  // Falls Namen der Ziel- und Quelldatei identisch, abbrechen:
  if(!slDestFile.AnsiCompareIC(slSourceFile)) return false;

  // Quelldatei öffnen:
  ilSourceHandle = FileOpen(slSourceFile.c_str(),fmOpenRead);
  if(ilSourceHandle >= 0)
  {
    // Die Dateigrösse ermitteln:
    ilFileSize = GetFileSize((HANDLE)ilSourceHandle, NULL);
    if(ilFileSize != 0xFFFFFFFF)
    {
      // Anzeige zurücksetzen:
      if(plFunc != NULL)
      {
        plFunc(0.0, 0, ilFileSize);
        Application->ProcessMessages();
      }

      // Zieldatei öffnen:
      ilDestHandle = FileCreate(slDestFile.c_str());
      if(ilDestHandle >= 0)
      {
        // Schreib- / Lesepuffer erzeugen:
        char *cpBuffer = new char[ilBufSize];
        if(cpBuffer)
        {
          blRetVal = true;

          while(ilBytesTotal < ilFileSize)
          {
            // Daten aus der Quelldatei in den Puffer einlesen:
            ilBytes = FileRead(ilSourceHandle, cpBuffer, ilBufSize);

            // Fehler aufgetreten:
            if (ilBytes == -1) blRetVal = false;
            // Falls Daten eingelesen:
            else if (ilBytes != 0)
            {
              // Daten aus dem Puffer in die Zieldatei schreiben:
              ilBytesLastWrite = FileWrite(ilDestHandle, cpBuffer, ilBytes);
              ilBytesTotal += ilBytesLastWrite;
              // Falls Anz. der gelesenen Bytes != der Anz. der geschr:
              if(ilBytes != ilBytesLastWrite) blRetVal = false;
            }

            // Fortschritt anzeigen:
            if(plFunc != NULL && (double(ilBytesTotal)*100.0/
               double(ilFileSize)>=dlLastValue + dlUpdateInterval ||
               ilBytes != ilBytesLastWrite || ilBytesTotal == ilFileSize))
            {
              // Falls in Prozent von Dateigrösse ausgeben:
              plFunc(double(ilBytesTotal)*100.0/double(ilFileSize),
                ilBytesTotal, ilFileSize);
              dlLastValue = double(ilBytesTotal)*100.0/double(ilFileSize);
            }
            Application->ProcessMessages();
            if(!blRetVal) break;
          }
          delete [] cpBuffer;
        }

        // Falls keine Fehler aufgetreten
        if(blRetVal)
        {
          // Zeitstempel übernehmen:
          FileSetDate(ilDestHandle, FileGetDate(ilSourceHandle));

          // Dateien schliessen:
          FileClose(ilSourceHandle);
          FileClose(ilDestHandle);
        }
        else
        {
          // Dateien schliessen:
          FileClose(ilSourceHandle);
          FileClose(ilDestHandle);
          // Dateiteile ggf. entfernen:
          DeleteFile(slDestFile);
        }
      }
    }
    else FileClose(ilSourceHandle);
  }
  return blRetVal;
}

Folgende Deklarationen müssen z.B. in der Header-Datei des Formulars erfolgen:

- - - - - - - - - - - - - - - Form1.h - - - - - - - - - - - - - - - - -
// Definition des Zeigertyps für die Statusaktualiserungsfunktion:
typedef void __fastcall (__closure * pfUpdateStatus)(double, DWORD, DWORD);

// Kopierfunktion deklarieren:
int ExtFileCopy(AnsiString slSourceFile, AnsiString slDestFile,
double dlUpdateInterval, pfUpdateStatus plFunc);


class TForm1 : public TForm
{
  ...
  private:

  // Funktion für die "Fortschrittsvisualisierung"
  // (wird von der ExtFileCopy aufgerufen)
  void __fastcall UpdateCopyStatus(double dlPercent,
                                   DWORD ilBytesCopied,
                                   DWORD ilBytesTotal);
  ...
};
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Beispielaufruf der ExtFileCopy():
// Datei kopieren:
if(!ExtFileCopy(editSourceFile->Text, editTargetFile->Text, 1, UpdateCopyStatus))
  ShowMessage("Fehler beim Kopieren der Datei aufgetreten!");


  Download BCB5
Projekt-Quellcode
Download
Demo-Exe

© '99-2001 by S. Kreutzmann