浏览代码

Reimplementing operation status centering (b38cf104) using background thread

As the primary thread can stuck during the operation, particularly when copying large file

Source commit: a91f5426d39f3bd9d0f3b859ef2f6f1b78525404
Martin Prikryl 2 周之前
父节点
当前提交
ea45332b44

+ 0 - 62
source/forms/CustomScpExplorer.cpp

@@ -280,11 +280,6 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   // (necessary for "explorer" only, as "commander" loads it for its drive menu)
   DriveInfo->NeedData();
   UsesCustomColorMode(this);
-
-  FOperationStatusWindow = nullptr;
-  FOperationStatusWindowTimer = nullptr;
-  DebugAssert(OnActiveFormChange == nullptr);
-  OnActiveFormChange = ActiveFormChange;
 }
 //---------------------------------------------------------------------------
 __fastcall TCustomScpExplorerForm::~TCustomScpExplorerForm()
@@ -12387,60 +12382,3 @@ void TCustomScpExplorerForm::ChangeDirViewStyle(TOperationSide Side, TDirViewSty
     UpdateControls();
   }
 }
-//---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::ActiveFormChange(TObject *)
-{
-  HWND WindowHandle = GetForegroundWindow();
-  if ((WindowHandle != nullptr) && (WindowHandle != FOperationStatusWindow))
-  {
-    DWORD ProcessId;
-    if ((GetWindowThreadProcessId(WindowHandle, &ProcessId) != 0) &&
-        (ProcessId == GetCurrentProcessId()))
-    {
-      UnicodeString ClassName;
-      ClassName.SetLength(256);
-      GetClassName(WindowHandle, ClassName.c_str(), ClassName.Length());
-      PackStr(ClassName);
-      if ((ClassName == L"OperationStatusWindow") &&
-          !IsIconic(WindowHandle))
-      {
-        FOperationStatusWindow = WindowHandle;
-        PlaceOperationStatusWindow();
-        FOperationStatusWindowIterations = 0;
-        if (FOperationStatusWindowTimer == nullptr)
-        {
-          FOperationStatusWindowTimer = new TTimer(this);
-        }
-        FOperationStatusWindowTimer->OnTimer = OperationStatusWindowTimer;
-        FOperationStatusWindowTimer->Interval = 50;
-        FOperationStatusWindowTimer->Enabled = true;
-      }
-    }
-  }
-}
-//---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::OperationStatusWindowTimer(TObject *)
-{
-  FOperationStatusWindowIterations++;
-  PlaceOperationStatusWindow();
-  if (FOperationStatusWindowIterations == 5)
-  {
-    SAFE_DESTROY(FOperationStatusWindowTimer);
-  }
-}
-//---------------------------------------------------------------------------
-void TCustomScpExplorerForm::PlaceOperationStatusWindow()
-{
-  TRect CurRect;
-  if ((Screen->ActiveForm != nullptr) &&
-      GetWindowRect(FOperationStatusWindow, &CurRect))
-  {
-    TRect Rect = CurRect;
-    CenterFormOn(Rect, Screen->ActiveForm, nullptr);
-    if (Rect != CurRect)
-    {
-      // What TWinControl.SetBounds does
-      SetWindowPos(FOperationStatusWindow, 0, Rect.Left, Rect.Top, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
-    }
-  }
-}

+ 0 - 6
source/forms/CustomScpExplorer.h

@@ -321,9 +321,6 @@ private:
   bool FUpdatingSessionTabs;
   TCalculateSizeOperation * FCalculateSizeOperation;
   TTBXTheme * FHiContrastTheme;
-  HWND FOperationStatusWindow;
-  TTimer * FOperationStatusWindowTimer;
-  int FOperationStatusWindowIterations;
 
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side, int FilesOnly);
   bool __fastcall GetEnableSelectedOperation(TOperationSide Side, int FilesOnly);
@@ -384,9 +381,6 @@ private:
   void __fastcall StoreTransitionCloseClick(TObject * Sender);
   void __fastcall StoreTransitionLinkClick(TObject * Sender);
   void InitializeRemoteThumbnailMask();
-  void __fastcall OperationStatusWindowTimer(TObject *);
-  void __fastcall ActiveFormChange(TObject *);
-  void PlaceOperationStatusWindow();
 
 protected:
   TOperationSide FCurrentSide;

+ 3 - 1
source/forms/SelectMask.cpp

@@ -177,7 +177,9 @@ void __fastcall TSelectMaskDialog::FormShow(TObject * /*Sender*/)
   if (FParent != NULL)
   {
     // Only now it is scaled
-    CenterFormOn(this, FParent);
+    TRect Bounds = BoundsRect;
+    CenterFormOn(Bounds, FParent, nullptr);
+    BoundsRect = Bounds;
   }
   UpdateControls();
 }

+ 152 - 78
source/windows/GUITools.cpp

@@ -138,7 +138,30 @@ bool __fastcall ExportSessionToPutty(TSessionData * SessionData, bool ReuseExist
   return Result;
 }
 //---------------------------------------------------------------------------
-class TPuttyCleanupThread : public TSimpleThread
+//---------------------------------------------------------------------------
+class TStandaloneThread : public TSimpleThread
+{
+public:
+  virtual void __fastcall Terminate();
+protected:
+  virtual bool __fastcall Finished();
+};
+//---------------------------------------------------------------------------
+void __fastcall TStandaloneThread::Terminate()
+{
+  // noop - the thread always self-terminates
+  DebugFail();
+}
+//---------------------------------------------------------------------------
+bool __fastcall TStandaloneThread::Finished()
+{
+  // self-destroy
+  return TSimpleThread::Finished() || true;
+}
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+template<typename T, int TT, int I>
+class TSingletonThread : public TStandaloneThread
 {
 public:
   static void Schedule();
@@ -146,25 +169,26 @@ public:
 
 protected:
   virtual void __fastcall Execute();
-  virtual void __fastcall Terminate();
-  bool __fastcall Finished();
   void DoSchedule();
+  virtual void DoExecute(bool HasExpired) = 0;
 
-private:
   TDateTime FTimer;
-  static TPuttyCleanupThread * FInstance;
+  static T * FInstance;
   static std::unique_ptr<TCriticalSection> FSection;
 };
 //---------------------------------------------------------------------------
-std::unique_ptr<TCriticalSection> TPuttyCleanupThread::FSection(TraceInitPtr(new TCriticalSection()));
-TPuttyCleanupThread * TPuttyCleanupThread::FInstance;
+template<typename T, int TT, int I>
+std::unique_ptr<TCriticalSection> TSingletonThread<T, TT, I>::FSection(TraceInitPtr(new TCriticalSection()));
+template<typename T, int TT, int I>
+T * TSingletonThread<T, TT, I>::FInstance;
 //---------------------------------------------------------------------------
-void TPuttyCleanupThread::Schedule()
+template<typename T, int TT, int I>
+void TSingletonThread<T, TT, I>::Schedule()
 {
   TGuard Guard(FSection.get());
   if (FInstance == NULL)
   {
-    FInstance = new TPuttyCleanupThread();
+    FInstance = new T();
     FInstance->DoSchedule();
     FInstance->Start();
   }
@@ -174,7 +198,8 @@ void TPuttyCleanupThread::Schedule()
   }
 }
 //---------------------------------------------------------------------------
-void TPuttyCleanupThread::Finalize()
+template<typename T, int TT, int I>
+void TSingletonThread<T, TT, I>::Finalize()
 {
   while (true)
   {
@@ -189,64 +214,25 @@ void TPuttyCleanupThread::Finalize()
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TPuttyCleanupThread::Execute()
+template<typename T, int TT, int I>
+void __fastcall TSingletonThread<T, TT, I>::Execute()
 {
   try
   {
-    std::unique_ptr<TStrings> Sessions(new TStringList());
-    bool Continue = true;
-
+    bool HasExpired;
     do
     {
       {
         TGuard Guard(FSection.get());
-        std::unique_ptr<TRegistryStorage> Storage(new TRegistryStorage(Configuration->PuttySessionsKey));
-        Storage->AccessMode = smReadWrite;
-        Storage->ConfigureForPutty();
-
-        std::unique_ptr<TStringList> Sessions2(new TStringList());
-        if (Storage->OpenRootKey(true))
-        {
-          std::unique_ptr<TStrings> SubKeys(new TStringList());
-          Storage->GetSubKeyNames(SubKeys.get());
-          for (int Index = 0; Index < SubKeys->Count; Index++)
-          {
-            UnicodeString SessionName = SubKeys->Strings[Index];
-            if (StartsStr(GUIConfiguration->PuttySession, SessionName))
-            {
-              Sessions2->Add(SessionName);
-            }
-          }
-
-          Sessions2->Sort();
-          if (!Sessions->Equals(Sessions2.get()))
-          {
-            // Just in case new sessions from another WinSCP instance are added, delay the cleanup
-            // (to avoid having to implement some inter-process communication).
-            // Both instances will attempt to do the cleanup, but that not a problem
-            Sessions->Assign(Sessions2.get());
-            DoSchedule();
-          }
-        }
-
-        if (FTimer < Now())
-        {
-          for (int Index = 0; Index < Sessions->Count; Index++)
-          {
-            UnicodeString SessionName = Sessions->Strings[Index];
-            Storage->RecursiveDeleteSubKey(SessionName);
-          }
-
-          Continue = false;
-        }
+        HasExpired = (FTimer < Now());
       }
-
-      if (Continue)
+      DoExecute(HasExpired);
+      if (!HasExpired)
       {
-        Sleep(1000);
+        Sleep(I);
       }
     }
-    while (Continue);
+    while (!HasExpired);
   }
   __finally
   {
@@ -255,23 +241,65 @@ void __fastcall TPuttyCleanupThread::Execute()
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TPuttyCleanupThread::Terminate()
+template<typename T, int TT, int I>
+void TSingletonThread<T, TT, I>::DoSchedule()
 {
-  DebugFail();
+  FTimer = IncSecond(Now(), TT);
 }
 //---------------------------------------------------------------------------
-bool __fastcall TPuttyCleanupThread::Finished()
+//---------------------------------------------------------------------------
+class TPuttyCleanupThread : public TSingletonThread<TPuttyCleanupThread, 10, 1000>
 {
-  // self-destroy
-  return TSimpleThread::Finished() || true;
-}
+protected:
+  virtual void DoExecute(bool HasExpired);
+};
 //---------------------------------------------------------------------------
-void TPuttyCleanupThread::DoSchedule()
+void TPuttyCleanupThread::DoExecute(bool HasExpired)
 {
-  FTimer = IncSecond(Now(), 10);
+  std::unique_ptr<TStrings> Sessions(new TStringList());
+
+  TGuard Guard(FSection.get());
+  std::unique_ptr<TRegistryStorage> Storage(new TRegistryStorage(Configuration->PuttySessionsKey));
+  Storage->AccessMode = smReadWrite;
+  Storage->ConfigureForPutty();
+
+  std::unique_ptr<TStringList> Sessions2(new TStringList());
+  if (Storage->OpenRootKey(true))
+  {
+    std::unique_ptr<TStrings> SubKeys(new TStringList());
+    Storage->GetSubKeyNames(SubKeys.get());
+    for (int Index = 0; Index < SubKeys->Count; Index++)
+    {
+      UnicodeString SessionName = SubKeys->Strings[Index];
+      if (StartsStr(GUIConfiguration->PuttySession, SessionName))
+      {
+        Sessions2->Add(SessionName);
+      }
+    }
+
+    Sessions2->Sort();
+    if (!Sessions->Equals(Sessions2.get()))
+    {
+      // Just in case new sessions from another WinSCP instance are added, delay the cleanup
+      // (to avoid having to implement some inter-process communication).
+      // Both instances will attempt to do the cleanup, but that not a problem
+      Sessions->Assign(Sessions2.get());
+      DoSchedule();
+    }
+  }
+
+  if (HasExpired)
+  {
+    for (int Index = 0; Index < Sessions->Count; Index++)
+    {
+      UnicodeString SessionName = Sessions->Strings[Index];
+      Storage->RecursiveDeleteSubKey(SessionName);
+    }
+
+  }
 }
 //---------------------------------------------------------------------------
-class TPuttyPasswordThread : public TSimpleThread
+class TPuttyPasswordThread : public TStandaloneThread
 {
 public:
   TPuttyPasswordThread(const UnicodeString & Password, const UnicodeString & PipeName);
@@ -279,8 +307,6 @@ public:
 
 protected:
   virtual void __fastcall Execute();
-  virtual void __fastcall Terminate();
-  virtual bool __fastcall Finished();
 
 private:
   HANDLE FPipe;
@@ -311,16 +337,6 @@ __fastcall TPuttyPasswordThread::~TPuttyPasswordThread()
   CloseHandle(FPipe);
 }
 //---------------------------------------------------------------------------
-void __fastcall TPuttyPasswordThread::Terminate()
-{
-  // noop - the thread always self-terminates
-}
-//---------------------------------------------------------------------------
-bool __fastcall TPuttyPasswordThread::Finished()
-{
-  return true;
-}
-//---------------------------------------------------------------------------
 void TPuttyPasswordThread::DoSleep(int & Timeout)
 {
   unsigned int Step = 50;
@@ -601,6 +617,64 @@ void OpenSessionInPutty(TSessionData * SessionData)
   }
 }
 //---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
+static HWND OperationStatusWindow = 0;
+static TRect OperationStatusCenterRect;
+//---------------------------------------------------------------------------
+void PlaceOperationStatusWindow()
+{
+  TRect CurRect;
+  if (GetWindowRect(OperationStatusWindow, &CurRect))
+  {
+    TRect Rect = CurRect;
+    CenterFormOn(Rect, OperationStatusCenterRect);
+    if (Rect != CurRect)
+    {
+      // What TWinControl.SetBounds does
+      SetWindowPos(OperationStatusWindow, 0, Rect.Left, Rect.Top, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
+    }
+  }
+}
+//---------------------------------------------------------------------------
+class TOperationStatusWindowMonitorThread : public TSingletonThread<TOperationStatusWindowMonitorThread, 3, 25>
+{
+protected:
+  virtual void DoExecute(bool HasExpired);
+};
+//---------------------------------------------------------------------------
+void TOperationStatusWindowMonitorThread::DoExecute(bool)
+{
+  PlaceOperationStatusWindow();
+}
+//---------------------------------------------------------------------------
+void CheckOperationStatusWindow()
+{
+  if (Screen->ActiveForm != nullptr)
+  {
+    OperationStatusCenterRect = GetCenterRect(Screen->ActiveForm, nullptr);
+    HWND WindowHandle = GetForegroundWindow();
+    if ((WindowHandle != nullptr) && (WindowHandle != OperationStatusWindow))
+    {
+      DWORD ProcessId;
+      if ((GetWindowThreadProcessId(WindowHandle, &ProcessId) != 0) &&
+          (ProcessId == GetCurrentProcessId()))
+      {
+        UnicodeString ClassName;
+        ClassName.SetLength(256);
+        GetClassName(WindowHandle, ClassName.c_str(), ClassName.Length());
+        PackStr(ClassName);
+        if ((ClassName == L"OperationStatusWindow") &&
+            !IsIconic(WindowHandle))
+        {
+          OperationStatusWindow = WindowHandle;
+          PlaceOperationStatusWindow();
+          TOperationStatusWindowMonitorThread::Schedule();
+        }
+      }
+    }
+  }
+}
+//---------------------------------------------------------------------------
 bool __fastcall FindTool(const UnicodeString & Name, UnicodeString & Path)
 {
   UnicodeString AppPath = IncludeTrailingBackslash(ExtractFilePath(Application->ExeName));

+ 1 - 0
source/windows/GUITools.h

@@ -85,6 +85,7 @@ TComponent * __fastcall FindComponentRecursively(TComponent * Root, const Unicod
 void __fastcall GetInstrutionsTheme(
   TColor & MainInstructionColor, HFONT & MainInstructionFont, HFONT & InstructionFont);
 bool CanShowTimeEstimate(TDateTime StartTime);
+void CheckOperationStatusWindow();
 //---------------------------------------------------------------------------
 class TLocalCustomCommand : public TFileCustomCommand
 {

+ 0 - 7
source/windows/Tools.cpp

@@ -136,13 +136,6 @@ TColor __fastcall GetNonZeroColor(TColor Color)
   return Color;
 }
 //---------------------------------------------------------------------------
-void __fastcall CenterFormOn(TForm * Form, TControl * CenterOn)
-{
-  TPoint ScreenPoint = CenterOn->ClientToScreen(TPoint(0, 0));
-  Form->Left = ScreenPoint.x + (CenterOn->Width / 2) - (Form->Width / 2);
-  Form->Top = ScreenPoint.y + (CenterOn->Height / 2) - (Form->Height / 2);
-}
-//---------------------------------------------------------------------------
 UnicodeString __fastcall GetListViewStr(TCustomListView * ListView)
 {
   UnicodeString Result;

+ 0 - 1
source/windows/Tools.h

@@ -9,7 +9,6 @@
 #include <SessionData.h>
 #include <Vcl.Graphics.hpp>
 //---------------------------------------------------------------------------
-void __fastcall CenterFormOn(TForm * Form, TControl * CenterOn);
 void ExecuteProcessAndReadOutput(const UnicodeString & Command, UnicodeString & Output, DWORD & ExitCode, bool ReadStdErr);
 void __fastcall ExecuteProcessChecked(
   const UnicodeString & Command, const UnicodeString & HelpKeyword, UnicodeString * Output);

+ 26 - 16
source/windows/VCLCommon.cpp

@@ -14,10 +14,10 @@
 #include <WinApi.h>
 #include <vssym32.h>
 #include <ComboEdit.hpp>
+#include <GUITools.h>
 //---------------------------------------------------------------------------
 const UnicodeString ContextSeparator(TraceInitStr(L"\x00BB"));
 const UnicodeString LinkAppLabelMark(TraceInitStr(UnicodeString(L" ") + ContextSeparator));
-TNotifyEvent OnActiveFormChange = nullptr;
 //---------------------------------------------------------------------------
 class TFormCustomizationComponent : public TComponent
 {
@@ -962,10 +962,7 @@ static void __fastcall FormWindowProc(void * Data, TMessage & Message)
   }
   else if (Message.Msg == WM_ACTIVATE)
   {
-    if (OnActiveFormChange != nullptr)
-    {
-      OnActiveFormChange(nullptr);
-    }
+    CheckOperationStatusWindow();
     WndProc(Message);
   }
   else
@@ -1761,26 +1758,39 @@ void __fastcall CutFormToDesktop(TForm * Form)
   }
 }
 //---------------------------------------------------------------------------
-void CenterFormOn(TRect & Bounds, TCustomForm * CenterForm, Forms::TMonitor * CenterMonitor)
+TRect GetCenterRect(TControl * CenterControl, Forms::TMonitor * CenterMonitor)
 {
-  int X, Y;
-  if (CenterForm != NULL)
+  TRect Result;
+  if (CenterControl != NULL)
   {
-    TForm * PublicCenterForm = reinterpret_cast<TForm *>(CenterForm);
-    X = ((PublicCenterForm->Width - Bounds.Width()) / 2) + PublicCenterForm->Left;
-    Y = ((PublicCenterForm->Height - Bounds.Height()) / 2) + PublicCenterForm->Top;
+    Result = CenterControl->BoundsRect;
+    TPoint P = CenterControl->BoundsRect.TopLeft();
+    if (CenterControl->Parent != nullptr)
+    {
+      P = CenterControl->Parent->ClientToScreen(P);
+    }
+    Result.SetLocation(P);
   }
   else
   {
-    X = (CenterMonitor->Width - Bounds.Width()) / 2;
-    Y = (CenterMonitor->Height - Bounds.Height()) / 2;
+    Result = CenterMonitor->BoundsRect;
   }
-
-  X = std::max(X, 0);
-  Y = std::max(Y, 0);
+  return Result;
+}
+//---------------------------------------------------------------------------
+void CenterFormOn(TRect & Bounds, const TRect & CenterRect)
+{
+  TRect DesktopRect = Screen->DesktopRect;
+  int X = std::max(DesktopRect.Left, ((CenterRect.Width() - Bounds.Width()) / 2) + CenterRect.Left);
+  int Y = std::max(DesktopRect.Top, ((CenterRect.Height() - Bounds.Height()) / 2) + CenterRect.Top);
   Bounds.SetLocation(X, Y);
 }
 //---------------------------------------------------------------------------
+void CenterFormOn(TRect & Bounds, TControl * CenterControl, Forms::TMonitor * CenterMonitor)
+{
+  CenterFormOn(Bounds, GetCenterRect(CenterControl, CenterMonitor));
+}
+//---------------------------------------------------------------------------
 void __fastcall UpdateFormPosition(TCustomForm * Form, TPosition Position)
 {
   if ((Position == poScreenCenter) ||

+ 3 - 2
source/windows/VCLCommon.h

@@ -9,7 +9,6 @@
 #include <HistoryComboBox.hpp>
 //---------------------------------------------------------------------------
 extern const UnicodeString ContextSeparator;
-extern TNotifyEvent OnActiveFormChange;
 //---------------------------------------------------------------------------
 void __fastcall FixListColumnWidth(TListView * TListView, int Index);
 void __fastcall AutoSizeListColumnsWidth(TListView * ListView, int ColumnToShrinkIndex = -1);
@@ -60,7 +59,9 @@ void __fastcall SetVerticalControlsOrder(TControl ** ControlsOrder, int Count);
 void __fastcall SetHorizontalControlsOrder(TControl ** ControlsOrder, int Count);
 void __fastcall MakeNextInTabOrder(TWinControl * Control, TWinControl * After);
 void __fastcall CutFormToDesktop(TForm * Form);
-void CenterFormOn(TRect & Bounds, TCustomForm * CenterForm, Forms::TMonitor * CenterMonitor);
+TRect GetCenterRect(TControl * CenterControl, Forms::TMonitor * CenterMonitor);
+void CenterFormOn(TRect & Bounds, const TRect & CenterRect);
+void CenterFormOn(TRect & Bounds, TControl * CenterControl, Forms::TMonitor * CenterMonitor);
 void __fastcall UpdateFormPosition(TCustomForm * Form, TPosition Position);
 void __fastcall ResizeForm(TCustomForm * Form, int Width, int Height);
 TComponent * __fastcall GetFormOwner();