| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977 | //---------------------------------------------------------------------------#include <vcl.h>#pragma hdrstop#include "Common.h"#include "FileOperationProgress.h"#include "CoreMain.h"//---------------------------------------------------------------------------#define TRANSFER_BUF_SIZE 32768//---------------------------------------------------------------------------TFileOperationStatistics::TFileOperationStatistics(){  memset(this, 0, sizeof(*this));}//---------------------------------------------------------------------------//---------------------------------------------------------------------------TFileOperationProgressType::TPersistence::TPersistence(){  FStatistics = NULL;  Clear(true, true);}//---------------------------------------------------------------------------bool TFileOperationProgressType::IsIndeterminateOperation(TFileOperation Operation){  return (Operation == foCalculateSize);}//---------------------------------------------------------------------------void TFileOperationProgressType::TPersistence::Clear(bool Batch, bool Speed){  if (Batch)  {    TotalTransferred = 0;    StartTime = Now();    SkipToAll = false;    BatchOverwrite = boNo;    CPSLimit = 0;    CounterSet = false;  }  if (Speed)  {    Ticks.clear();    TotalTransferredThen.clear();  }}//---------------------------------------------------------------------------__fastcall TFileOperationProgressType::TFileOperationProgressType(){  FOnProgress = NULL;  FOnFinished = NULL;  FParent = NULL;  Init();  Clear();}//---------------------------------------------------------------------------__fastcall TFileOperationProgressType::TFileOperationProgressType(  TFileOperationProgressEvent AOnProgress, TFileOperationFinished AOnFinished,  TFileOperationProgressType * Parent){  FOnProgress = AOnProgress;  FOnFinished = AOnFinished;  FParent = Parent;  FReset = false;  Init();  Clear();}//---------------------------------------------------------------------------__fastcall TFileOperationProgressType::~TFileOperationProgressType(){  DebugAssert(!InProgress || FReset);  DebugAssert(!Suspended || FReset);  SAFE_DESTROY(FSection);  SAFE_DESTROY(FUserSelectionsSection);}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Init(){  FSection = new TCriticalSection();  FUserSelectionsSection = new TCriticalSection();  FRestored = false;  FPersistence.Side = osCurrent; // = undefined value}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Assign(const TFileOperationProgressType & Other){  TValueRestorer<TCriticalSection *> SectionRestorer(FSection);  TValueRestorer<TCriticalSection *> UserSelectionsSectionRestorer(FUserSelectionsSection);  TGuard Guard(FSection);  TGuard OtherGuard(Other.FSection);  *this = Other;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::AssignButKeepSuspendState(const TFileOperationProgressType & Other){  TGuard Guard(FSection);  TValueRestorer<unsigned int> SuspendTimeRestorer(FSuspendTime);  TValueRestorer<bool> SuspendedRestorer(FSuspended);  Assign(Other);}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::DoClear(bool Batch, bool Speed){  FFileName = L"";  FFullFileName = L"";  FDirectory = L"";  FAsciiTransfer = false;  FCount = -1;  FFilesFinished = 0;  FFilesFinishedSuccessfully = 0;  FSuspended = false;  FSuspendTime = 0;  FInProgress = false;  FDone = false;  FFileInProgress = false;  FTotalSkipped = 0;  FTotalSize = 0;  FSkippedSize = 0;  FTotalSizeSet = false;  FOperation = foNone;  FTemp = false;  FPersistence.Clear(Batch, Speed);  // to bypass check in ClearTransfer()  FTransferSize = 0;  ClearTransfer();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Clear(){  DoClear(true, true);}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::ClearTransfer(){  if ((TransferSize > 0) && (TransferredSize < TransferSize))  {    TGuard Guard(FSection);    __int64 RemainingSize = (TransferSize - TransferredSize);    AddSkipped(RemainingSize);  }  FLocalSize = 0;  FTransferSize = 0;  FLocallyUsed = 0;  FSkippedSize = 0;  FTransferredSize = 0;  FTransferringFile = false;  FLastSecond = 0;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,  TOperationSide ASide, int ACount){  Start(AOperation, ASide, ACount, false, L"", 0);}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Start(TFileOperation AOperation,  TOperationSide ASide, int ACount, bool ATemp,  const UnicodeString ADirectory, unsigned long ACPSLimit){  {    TGuard Guard(FSection); // not really needed, just for consistency    DoClear(!FRestored, (FPersistence.Side != osCurrent) && (FPersistence.Side != ASide));    FTotalTransferBase = FPersistence.TotalTransferred;    FOperation = AOperation;    FPersistence.Side = ASide;    FCount = ACount;    FInProgress = true;    FCancel = csContinue;    FDirectory = ADirectory;    FTemp = ATemp;    FPersistence.CPSLimit = ACPSLimit;  }  try  {    DoProgress();  }  catch (...)  {    // connection can be lost during progress callbacks    ClearTransfer();    FInProgress = false;    throw;  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Reset(){  FReset = true;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Stop(){  // added to include remaining bytes to TotalSkipped, in case  // the progress happens to update before closing  ClearTransfer();  FInProgress = false;  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetDone(){  FDone = true;  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Suspend(){  {    TGuard Guard(FSection);    DebugAssert(!Suspended);    FSuspended = true;    FSuspendTime = GetTickCount();  }  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Resume(){  {    TGuard Guard(FSection);    DebugAssert(Suspended);    FSuspended = false;    // shift timestamps for CPS calculation in advance    // by the time the progress was suspended    unsigned long Stopped = (GetTickCount() - FSuspendTime);    size_t i = 0;    while (i < FPersistence.Ticks.size())    {      FPersistence.Ticks[i] += Stopped;      ++i;    }  }  DoProgress();}//---------------------------------------------------------------------------int __fastcall TFileOperationProgressType::OperationProgress() const{  int Result;  if (FCount > 0)  {    Result = (FFilesFinished * 100)/FCount;  }  else  {    Result = 0;  }  return Result;}//---------------------------------------------------------------------------int __fastcall TFileOperationProgressType::TransferProgress(){  int Result;  if (TransferSize)  {    Result = (int)((TransferredSize * 100)/TransferSize);  }  else  {    Result = 0;  }  return Result;}//---------------------------------------------------------------------------int __fastcall TFileOperationProgressType::TotalTransferProgress() const{  TGuard Guard(FSection);  DebugAssert(TotalSizeSet);  int Result;  if (FTotalSize > 0)  {    Result = (int)(((FPersistence.TotalTransferred - FTotalTransferBase + FTotalSkipped) * 100)/FTotalSize);  }  else  {    Result = 0;  }  return Result < 100 ? Result : 100;}//---------------------------------------------------------------------------int __fastcall TFileOperationProgressType::OverallProgress() const{  if (TotalSizeSet)  {    DebugAssert((Operation == foCopy) || (Operation == foMove));    return TotalTransferProgress();  }  else  {    return OperationProgress();  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Progress(){  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::DoProgress(){  SetThreadExecutionState(ES_SYSTEM_REQUIRED);  FOnProgress(*this);}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Finish(UnicodeString FileName,  bool Success, TOnceDoneOperation & OnceDoneOperation){  DebugAssert(InProgress);  // Cancel reader is guarded  FOnFinished(Operation, Side, Temp, FileName,    Success && (Cancel == csContinue), OnceDoneOperation);  FFilesFinished++;  if (Success)  {    FFilesFinishedSuccessfully++;  }  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Succeeded(int Count){  if (FPersistence.Statistics != NULL)  {    if ((Operation == foCopy) || (Operation == foMove))    {      __int64 Transferred = FTransferredSize - FSkippedSize;      if (Side == osLocal)      {        FPersistence.Statistics->FilesUploaded += Count;        FPersistence.Statistics->TotalUploaded += Transferred;      }      else      {        FPersistence.Statistics->FilesDownloaded += Count;        FPersistence.Statistics->TotalDownloaded += Transferred;      }    }    else if (Operation == foDelete)    {      if (Side == osLocal)      {        FPersistence.Statistics->FilesDeletedLocal += Count;      }      else      {        FPersistence.Statistics->FilesDeletedRemote += Count;      }    }  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetFile(UnicodeString AFileName, bool AFileInProgress){  FFullFileName = AFileName;  if (Side == osRemote)  {    // historically set were passing filename-only for remote site operations,    // now we need to collect a full paths, so we pass in full path,    // but still want to have filename-only in FileName    AFileName = UnixExtractFileName(AFileName);  }  FFileName = AFileName;  FFileInProgress = AFileInProgress;  ClearTransfer();  FFileStartTime = Now();  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetFileInProgress(){  DebugAssert(!FileInProgress);  FFileInProgress = true;  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetLocalSize(__int64 ASize){  FLocalSize = ASize;  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::AddLocallyUsed(__int64 ASize){  FLocallyUsed += ASize;  if (LocallyUsed > LocalSize)  {    FLocalSize = LocallyUsed;  }  DoProgress();}//---------------------------------------------------------------------------bool __fastcall TFileOperationProgressType::IsLocallyDone(){  DebugAssert(LocallyUsed <= LocalSize);  return (LocallyUsed == LocalSize);}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetSpeedCounters(){  if ((CPSLimit > 0) && !FPersistence.CounterSet)  {    FPersistence.CounterSet = true;    Configuration->Usage->Inc(L"SpeedLimitUses");  }}//---------------------------------------------------------------------------// Used in WebDAV and S3 protocolsvoid __fastcall TFileOperationProgressType::ThrottleToCPSLimit(  unsigned long Size){  unsigned long Remaining = Size;  while (Remaining > 0)  {    Remaining -= AdjustToCPSLimit(Remaining);  }}//---------------------------------------------------------------------------unsigned long __fastcall TFileOperationProgressType::AdjustToCPSLimit(  unsigned long Size){  SetSpeedCounters();  // CPSLimit reader is guarded, we cannot block whole method as it can last long.  if (CPSLimit > 0)  {    // we must not return 0, hence, if we reach zero,    // we wait until the next second    do    {      unsigned int Second = (GetTickCount() / MSecsPerSec);      if (Second != FLastSecond)      {        FRemainingCPS = CPSLimit;        FLastSecond = Second;      }      if (FRemainingCPS == 0)      {        SleepEx(100, true);        DoProgress();      }    }    while ((CPSLimit > 0) && (FRemainingCPS == 0));    // CPSLimit may have been dropped in DoProgress    if (CPSLimit > 0)    {      if (FRemainingCPS < Size)      {        Size = FRemainingCPS;      }      FRemainingCPS -= Size;    }  }  return Size;}//---------------------------------------------------------------------------// Use in SCP protocol onlyunsigned long __fastcall TFileOperationProgressType::LocalBlockSize(){  unsigned long Result = TRANSFER_BUF_SIZE;  if (LocallyUsed + Result > LocalSize)  {    Result = (unsigned long)(LocalSize - LocallyUsed);  }  Result = AdjustToCPSLimit(Result);  return Result;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetTotalSize(__int64 ASize){  TGuard Guard(FSection); // not really needed, just for consistency  FTotalSize = ASize;  FTotalSizeSet = true;  // parent has its own totals  if (FParent != NULL)  {    DebugAssert(FParent->TotalSizeSet);  }  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetTransferSize(__int64 ASize){  FTransferSize = ASize;  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetTransferringFile(bool ATransferringFile){  FTransferringFile = ATransferringFile;}//---------------------------------------------------------------------------bool __fastcall TFileOperationProgressType::PassCancelToParent(TCancelStatus ACancel){  bool Result;  if (ACancel < csCancel)  {    // do not propagate csCancelFile,    // though it's not supported for queue atm, so we do not expect it here    DebugFail();    Result = false;  }  else if (ACancel == csCancel)  {    Result = true;  }  else  {    // csCancelTransfer and csRemoteAbort are used with SCP only, which does not use parallel transfers    DebugFail();    Result = false;  }  return Result;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetCancel(TCancelStatus ACancel){  TGuard Guard(FSection);  FCancel = ACancel;  if ((FParent != NULL) && PassCancelToParent(ACancel))  {    FParent->SetCancel(ACancel);  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetCancelAtLeast(TCancelStatus ACancel){  TGuard Guard(FSection);  if (FCancel < ACancel)  {    FCancel = ACancel;  }  if ((FParent != NULL) && PassCancelToParent(ACancel))  {    FParent->SetCancelAtLeast(ACancel);  }}//---------------------------------------------------------------------------TCancelStatus __fastcall TFileOperationProgressType::GetCancel(){  TCancelStatus Result = FCancel;  if (FParent != NULL)  {    TGuard Guard(FSection);    TCancelStatus ParentCancel = FParent->Cancel;    if (ParentCancel > Result)    {      Result = ParentCancel;    }  }  return Result;}//---------------------------------------------------------------------------bool __fastcall TFileOperationProgressType::ClearCancelFile(){  TGuard Guard(FSection);  // Not propagated to parent, as this is local flag, see also PassCancelToParent  bool Result = (Cancel == csCancelFile);  if (Result)  {    FCancel = csContinue;  }  return Result;}//---------------------------------------------------------------------------unsigned long __fastcall TFileOperationProgressType::GetCPSLimit(){  unsigned int Result;  if (FParent != NULL)  {    Result = FParent->CPSLimit;  }  else  {    TGuard Guard(FSection);    Result = FPersistence.CPSLimit;  }  return Result;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetCPSLimit(unsigned long ACPSLimit){  if (FParent != NULL)  {    FParent->SetCPSLimit(ACPSLimit);  }  else  {    TGuard Guard(FSection);    FPersistence.CPSLimit = ACPSLimit;  }}//---------------------------------------------------------------------------TBatchOverwrite __fastcall TFileOperationProgressType::GetBatchOverwrite(){  TBatchOverwrite Result;  if (FParent != NULL)  {    Result = FParent->BatchOverwrite;  }  else  {    TGuard Guard(FSection); // not really needed    Result = FPersistence.BatchOverwrite;  }  return Result;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetBatchOverwrite(TBatchOverwrite ABatchOverwrite){  if (FParent != NULL)  {    FParent->SetBatchOverwrite(ABatchOverwrite);  }  else  {    TGuard Guard(FSection); // not really needed    FPersistence.BatchOverwrite = ABatchOverwrite;;  }}//---------------------------------------------------------------------------bool __fastcall TFileOperationProgressType::GetSkipToAll(){  bool Result;  if (FParent != NULL)  {    Result = FParent->SkipToAll;  }  else  {    TGuard Guard(FSection); // not really needed    Result = FPersistence.SkipToAll;  }  return Result;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetSkipToAll(){  if (FParent != NULL)  {    FParent->SetSkipToAll();  }  else  {    TGuard Guard(FSection); // not really needed    FPersistence.SkipToAll = true;  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::ChangeTransferSize(__int64 ASize){  // reflect change on file size (due to text transfer mode conversion particulary)  // on total transfer size  if (TotalSizeSet)  {    AddTotalSize(ASize - TransferSize);  }  FTransferSize = ASize;  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::RollbackTransferFromTotals(__int64 ATransferredSize, __int64 ASkippedSize){  TGuard Guard(FSection);  DebugAssert(ATransferredSize <= FPersistence.TotalTransferred - FTotalTransferBase);  DebugAssert(ASkippedSize <= FTotalSkipped);  FPersistence.TotalTransferred -= ATransferredSize;  FPersistence.Ticks.clear();  FPersistence.TotalTransferredThen.clear();  FTotalSkipped -= ASkippedSize;  if (FParent != NULL)  {    FParent->RollbackTransferFromTotals(ATransferredSize, ASkippedSize);  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::RollbackTransfer(){  FTransferredSize -= FSkippedSize;  RollbackTransferFromTotals(FTransferredSize, FSkippedSize);  FSkippedSize = 0;  FTransferredSize = 0;  FTransferSize = 0;  FLocallyUsed = 0;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::AddTransferredToTotals(__int64 ASize){  TGuard Guard(FSection);  FPersistence.TotalTransferred += ASize;  if (ASize >= 0)  {    unsigned long Ticks = GetTickCount();    if (FPersistence.Ticks.empty() ||        (FPersistence.Ticks.back() > Ticks) || // ticks wrap after 49.7 days        ((Ticks - FPersistence.Ticks.back()) >= MSecsPerSec))    {      FPersistence.Ticks.push_back(Ticks);      FPersistence.TotalTransferredThen.push_back(FPersistence.TotalTransferred);    }    if (FPersistence.Ticks.size() > 10)    {      FPersistence.Ticks.erase(FPersistence.Ticks.begin());      FPersistence.TotalTransferredThen.erase(FPersistence.TotalTransferredThen.begin());    }  }  else  {    FPersistence.Ticks.clear();  }  if (FParent != NULL)  {    FParent->AddTransferredToTotals(ASize);  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::AddTotalSize(__int64 ASize){  if (ASize != 0)  {    TGuard Guard(FSection);    FTotalSize += ASize;    if (FParent != NULL)    {      FParent->AddTotalSize(ASize);    }  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::AddTransferred(__int64 ASize,  bool AddToTotals){  FTransferredSize += ASize;  if (TransferredSize > TransferSize)  {    // this can happen with SFTP when downloading file that    // grows while being downloaded    if (TotalSizeSet)    {      // we should probably guard this with AddToTotals      AddTotalSize(TransferredSize - TransferSize);    }    FTransferSize = TransferredSize;  }  if (AddToTotals)  {    AddTransferredToTotals(ASize);  }  DoProgress();}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::AddSkipped(__int64 ASize){  TGuard Guard(FSection);  FTotalSkipped += ASize;  if (FParent != NULL)  {    FParent->AddSkipped(ASize);  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::AddResumed(__int64 ASize){  AddSkipped(ASize);  FSkippedSize += ASize;  AddTransferred(ASize, false);  AddLocallyUsed(ASize);}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::AddSkippedFileSize(__int64 ASize){  AddSkipped(ASize);  DoProgress();}//---------------------------------------------------------------------------// Use in SCP protocol onlyunsigned long __fastcall TFileOperationProgressType::TransferBlockSize(){  unsigned long Result = TRANSFER_BUF_SIZE;  if (TransferredSize + Result > TransferSize)  {    Result = (unsigned long)(TransferSize - TransferredSize);  }  Result = AdjustToCPSLimit(Result);  return Result;}//---------------------------------------------------------------------------unsigned long __fastcall TFileOperationProgressType::StaticBlockSize(){  return TRANSFER_BUF_SIZE;}//---------------------------------------------------------------------------bool __fastcall TFileOperationProgressType::IsTransferDone(){  DebugAssert(TransferredSize <= TransferSize);  return (TransferredSize == TransferSize);}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::SetAsciiTransfer(bool AAsciiTransfer){  FAsciiTransfer = AAsciiTransfer;  DoProgress();}//---------------------------------------------------------------------------TDateTime __fastcall TFileOperationProgressType::TimeElapsed(){  return Now() - StartTime;}//---------------------------------------------------------------------------unsigned int __fastcall TFileOperationProgressType::CPS(){  TGuard Guard(FSection);  return GetCPS();}//---------------------------------------------------------------------------// Has to be called from a guarded methodunsigned int __fastcall TFileOperationProgressType::GetCPS(){  unsigned int Result;  if (FPersistence.Ticks.empty())  {    Result = 0;  }  else  {    unsigned long Ticks = (Suspended ? FSuspendTime : GetTickCount());    unsigned long TimeSpan;    if (Ticks < FPersistence.Ticks.front())    {      // clocks has wrapped, guess 10 seconds difference      TimeSpan = 10000;    }    else    {      TimeSpan = (Ticks - FPersistence.Ticks.front());    }    if (TimeSpan == 0)    {      Result = 0;    }    else    {      __int64 Transferred = (FPersistence.TotalTransferred - FPersistence.TotalTransferredThen.front());      Result = (unsigned int)(Transferred * MSecsPerSec / TimeSpan);    }  }  return Result;}//---------------------------------------------------------------------------TDateTime __fastcall TFileOperationProgressType::TimeExpected(){  unsigned int CurCps = CPS();  if (CurCps)  {    return TDateTime((double)(((double)(TransferSize - TransferredSize)) / CurCps) / SecsPerDay);  }  else  {    return 0;  }}//---------------------------------------------------------------------------TDateTime __fastcall TFileOperationProgressType::TotalTimeLeft(){  TGuard Guard(FSection);  DebugAssert(FTotalSizeSet);  unsigned int CurCps = GetCPS();  // sanity check  __int64 Processed = FTotalSkipped + FPersistence.TotalTransferred - FTotalTransferBase;  if ((CurCps > 0) && (FTotalSize > Processed))  {    return TDateTime((double)((double)(FTotalSize - Processed) / CurCps) / SecsPerDay);  }  else  {    return 0;  }}//---------------------------------------------------------------------------__int64 __fastcall TFileOperationProgressType::GetTotalTransferred(){  TGuard Guard(FSection);  return FPersistence.TotalTransferred;}//---------------------------------------------------------------------------__int64 __fastcall TFileOperationProgressType::GetOperationTransferred() const{  TGuard Guard(FSection);  return FPersistence.TotalTransferred - FTotalTransferBase + FTotalSkipped;}//---------------------------------------------------------------------------__int64 __fastcall TFileOperationProgressType::GetTotalSize(){  TGuard Guard(FSection);  return FTotalSize;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::LockUserSelections(){  if (FParent != NULL)  {    FParent->LockUserSelections();  }  else  {    FUserSelectionsSection->Enter();  }}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::UnlockUserSelections(){  if (FParent != NULL)  {    FParent->UnlockUserSelections();  }  else  {    FUserSelectionsSection->Leave();  }}//---------------------------------------------------------------------------UnicodeString __fastcall TFileOperationProgressType::GetLogStr(bool Done){  UnicodeString Transferred = FormatSize(TotalTransferred);  UnicodeString Left;  TDateTime Time;  UnicodeString TimeLabel;  if (!Done && TotalSizeSet)  {    Time = TotalTimeLeft();    TimeLabel = L"Left";  }  else  {    Time = TimeElapsed();    TimeLabel = L"Elapsed";  }  UnicodeString TimeStr = FormatDateTimeSpan(Configuration->TimeFormat, Time);  UnicodeString CPSStr = FormatSize(CPS());  return FORMAT(L"Transferred: %s, %s: %s, CPS: %s/s", (Transferred, TimeLabel, TimeStr, CPSStr));}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Store(TPersistence & Persistence){  // in this current use, we do not need this to be guarded actually, as it's used only for a single-threaded synchronization  TGuard Guard(FSection);  Persistence = FPersistence;}//---------------------------------------------------------------------------void __fastcall TFileOperationProgressType::Restore(TPersistence & Persistence){  TGuard Guard(FSection);  FPersistence = Persistence;  FRestored = true;}
 |