Martin Prikryl 17 years ago
parent
commit
9ff1612c74
72 changed files with 1441 additions and 487 deletions
  1. 4 4
      Console.rc
  2. 4 4
      DragExt.rc
  3. 4 4
      DragExt64.rc
  4. 1 1
      WinSCP.bpr
  5. 5 5
      WinSCP.rc
  6. 1 0
      console/Console.h
  7. 49 14
      console/Main.cpp
  8. 21 6
      core/Common.cpp
  9. 1 1
      core/Common.h
  10. 15 15
      core/Configuration.cpp
  11. 23 0
      core/Exceptions.cpp
  12. 8 0
      core/Exceptions.h
  13. 4 7
      core/FileOperationProgress.cpp
  14. 2 2
      core/FileOperationProgress.h
  15. 4 4
      core/FtpFileSystem.cpp
  16. 23 28
      core/PuttyIntf.cpp
  17. 2 2
      core/Queue.cpp
  18. 7 6
      core/ScpFileSystem.cpp
  19. 13 16
      core/Script.cpp
  20. 1 2
      core/Script.h
  21. 185 76
      core/SecureShell.cpp
  22. 7 0
      core/SecureShell.h
  23. 13 4
      core/SessionData.cpp
  24. 2 0
      core/SessionData.h
  25. 1 0
      core/SessionInfo.h
  26. 17 13
      core/SftpFileSystem.cpp
  27. 1 0
      core/SftpFileSystem.h
  28. 187 39
      core/Terminal.cpp
  29. 8 1
      core/Terminal.h
  30. 44 5
      dragext/DragExt.cpp
  31. 4 4
      forms/CustomScpExplorer.cpp
  32. 6 6
      forms/CustomScpExplorer.h
  33. 14 3
      forms/LocationProfiles.cpp
  34. 1 0
      forms/LocationProfiles.dfm
  35. 4 0
      forms/LocationProfiles.h
  36. 11 0
      forms/Login.cpp
  37. 1 0
      forms/Login.dfm
  38. 4 0
      forms/Login.h
  39. 14 1
      forms/OpenDirectory.cpp
  40. 1 0
      forms/OpenDirectory.dfm
  41. 4 0
      forms/OpenDirectory.h
  42. 33 2
      forms/Preferences.cpp
  43. 2 0
      forms/Preferences.dfm
  44. 6 0
      forms/Preferences.h
  45. 1 0
      packages/filemng/BaseUtils.hpp
  46. 13 1
      packages/filemng/BaseUtils.pas
  47. 4 2
      packages/filemng/CustomDirView.hpp
  48. 23 40
      packages/filemng/CustomDirView.pas
  49. 4 4
      packages/filemng/CustomDriveView.hpp
  50. 30 135
      packages/filemng/CustomDriveView.pas
  51. 3 2
      packages/filemng/DirView.pas
  52. 0 4
      packages/filemng/DriveView.hpp
  53. 1 7
      packages/filemng/DriveView.pas
  54. 6 1
      packages/my/NortonLikeListView.pas
  55. 98 0
      packages/my/PasTools.hpp
  56. 373 1
      packages/my/PasTools.pas
  57. 0 1
      packages/tbx/TBXUtils.pas
  58. 7 1
      packages/theme/ThemeMgr.pas
  59. 18 0
      putty/windows/WINHANDL.C
  60. 17 0
      putty/windows/WINPROXY.C
  61. 4 0
      putty/windows/WINSTUFF.H
  62. 1 1
      release/winscp.u3i
  63. 16 1
      release/winscpsetup.iss
  64. 22 0
      windows/ConsoleRunner.cpp
  65. 11 2
      windows/CustomWinConfiguration.cpp
  66. 3 0
      windows/CustomWinConfiguration.h
  67. 1 1
      windows/Setup.cpp
  68. 6 4
      windows/TerminalManager.cpp
  69. 1 1
      windows/TerminalManager.h
  70. 4 2
      windows/WinConfiguration.cpp
  71. 1 0
      windows/WinConfiguration.h
  72. 11 1
      windows/WinMain.cpp

+ 4 - 4
Console.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 2,0,0,89
-PRODUCTVERSION 2,0,0,89
+FILEVERSION 2,1,0,92
+PRODUCTVERSION 2,1,0,92
 FILEOS 0x4
 FILETYPE 0x1
 {
@@ -10,13 +10,13 @@ FILETYPE 0x1
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Console interface for WinSCP\0"
-            VALUE "FileVersion", "2.0.0.89\0"
+            VALUE "FileVersion", "2.1.0.92\0"
             VALUE "InternalName", "console\0"
             VALUE "LegalCopyright", "(c) 2000-2008 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "winscp.com\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.1.3.0\0"
+            VALUE "ProductVersion", "4.1.4.0\0"
             VALUE "WWW", "http://winscp.net/\0"
         }
     }

+ 4 - 4
DragExt.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 1,1,7,86
-PRODUCTVERSION 1,1,7,86
+FILEVERSION 1,1,8,87
+PRODUCTVERSION 1,1,8,87
 FILEOS 0x4
 FILETYPE 0x2
 {
@@ -10,13 +10,13 @@ FILETYPE 0x2
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Drag&Drop shell extension for WinSCP\0"
-            VALUE "FileVersion", "1.1.7.86\0"
+            VALUE "FileVersion", "1.1.8.87\0"
             VALUE "InternalName", "dragext\0"
             VALUE "LegalCopyright", "(c) 2000-2008 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "dragext.dll\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.1.3.0\0"
+            VALUE "ProductVersion", "4.1.4.0\0"
             VALUE "WWW", "http://winscp.net/\0"
         }
     }

+ 4 - 4
DragExt64.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 1,1,7,86
-PRODUCTVERSION 1,1,7,86
+FILEVERSION 1,1,8,87
+PRODUCTVERSION 1,1,8,87
 FILEOS 0x4
 FILETYPE 0x2
 {
@@ -10,13 +10,13 @@ FILETYPE 0x2
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Drag&Drop shell extension for WinSCP (64-bit)\0"
-            VALUE "FileVersion", "1.1.7.86\0"
+            VALUE "FileVersion", "1.1.8.87\0"
             VALUE "InternalName", "dragext64\0"
             VALUE "LegalCopyright", "(c) 2000-2008 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "dragext64.dll\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.1.3.0\0"
+            VALUE "ProductVersion", "4.1.4.0\0"
             VALUE "WWW", "http://winscp.net/\0"
         }
     }

+ 1 - 1
WinSCP.bpr

@@ -58,6 +58,7 @@
   </LINKER>
   <FILELIST>
       <FILE FILENAME="WinSCP.cpp" FORMNAME="" UNITNAME="WinSCP" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="windows\Windows.rc" FORMNAME="" UNITNAME="Windows.rc" CONTAINERID="RCCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="forms\CustomScpExplorer.cpp" FORMNAME="CustomScpExplorerForm" UNITNAME="CustomScpExplorer" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="windows\CustomWinConfiguration.cpp" FORMNAME="" UNITNAME="CustomWinConfiguration" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="windows\EditorManager.cpp" FORMNAME="" UNITNAME="EditorManager" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
@@ -83,7 +84,6 @@
       <FILE FILENAME="lib\RScpComp.lib" FORMNAME="" UNITNAME="RScpComp.lib" CONTAINERID="LibTool" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="lib\ScpCore.lib" FORMNAME="" UNITNAME="ScpCore.lib" CONTAINERID="LibTool" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="lib\ScpForms.lib" FORMNAME="" UNITNAME="ScpForms.lib" CONTAINERID="LibTool" DESIGNCLASS="" LOCALCOMMAND=""/>
-      <FILE FILENAME="windows\Windows.rc" FORMNAME="" UNITNAME="Windows.rc" CONTAINERID="RCCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="WinSCP.res" FORMNAME="" UNITNAME="WinSCP.res" CONTAINERID="ResTool" DESIGNCLASS="" LOCALCOMMAND=""/>
   </FILELIST>
   <BUILDTOOLS>

+ 5 - 5
WinSCP.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 4,1,3,393
-PRODUCTVERSION 4,1,3,393
+FILEVERSION 4,1,4,407
+PRODUCTVERSION 4,1,4,407
 FILEOS 0x4
 FILETYPE 0x1
 {
@@ -10,13 +10,13 @@ FILETYPE 0x1
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "SFTP, FTP and SCP client\0"
-            VALUE "FileVersion", "4.1.3.393\0"
+            VALUE "FileVersion", "4.1.4.407\0"
             VALUE "InternalName", "winscp\0"
             VALUE "LegalCopyright", "(c) 2000-2008 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
-            VALUE "OriginalFilename", "winscp413.exe\0"
+            VALUE "OriginalFilename", "winscp414.exe\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.1.3.0\0"
+            VALUE "ProductVersion", "4.1.4.0\0"
             VALUE "WWW", "http://winscp.net/\0"
         }
     }

+ 1 - 0
console/Console.h

@@ -6,6 +6,7 @@
 #define CONSOLE_EVENT_REQUEST "WinSCPConsoleEventRequest"
 #define CONSOLE_EVENT_RESPONSE "WinSCPConsoleEventResponse"
 #define CONSOLE_EVENT_CANCEL "WinSCPConsoleEventCancel"
+#define CONSOLE_JOB "WinSCPConsoleJob"
 //---------------------------------------------------------------------------
 struct TConsoleCommStruct
 {

+ 49 - 14
console/Main.cpp

@@ -25,7 +25,7 @@ inline TConsoleCommStruct* GetCommStruct(HANDLE FileMapping)
     FILE_MAP_ALL_ACCESS, 0, 0, 0));
   if (Result == NULL)
   {
-    throw logic_error("Cannot open mapping object.");
+    throw runtime_error("Cannot open mapping object.");
   }
   return Result;
 }
@@ -36,7 +36,7 @@ inline void FreeCommStruct(TConsoleCommStruct* CommStruct)
 }
 //---------------------------------------------------------------------------
 void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& ResponseEvent,
-  HANDLE& CancelEvent, HANDLE& FileMapping)
+  HANDLE& CancelEvent, HANDLE& FileMapping, HANDLE& Job)
 {
   int Attempts = 0;
   char Name[MAX_PATH];
@@ -46,7 +46,7 @@ void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& Respon
   {
     if (Attempts > MAX_ATTEMPTS)
     {
-      throw logic_error("Cannot find unique name for event object.");
+      throw runtime_error("Cannot find unique name for event object.");
     }
 
     #ifdef CONSOLE_TEST
@@ -68,21 +68,21 @@ void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& Respon
   RequestEvent = CreateEvent(NULL, false, false, Name);
   if (RequestEvent == NULL)
   {
-    throw logic_error("Cannot create request event object.");
+    throw runtime_error("Cannot create request event object.");
   }
 
   sprintf(Name, "%s%d", CONSOLE_EVENT_RESPONSE, InstanceNumber);
   ResponseEvent = CreateEvent(NULL, false, false, Name);
   if (ResponseEvent == NULL)
   {
-    throw logic_error("Cannot create response event object.");
+    throw runtime_error("Cannot create response event object.");
   }
 
   sprintf(Name, "%s%d", CONSOLE_EVENT_CANCEL, InstanceNumber);
   CancelEvent = CreateEvent(NULL, false, false, Name);
   if (CancelEvent == NULL)
   {
-    throw logic_error("Cannot create cancel event object.");
+    throw runtime_error("Cannot create cancel event object.");
   }
 
   sprintf(Name, "%s%d", CONSOLE_MAPPING, InstanceNumber);
@@ -90,7 +90,41 @@ void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& Respon
     0, sizeof(TConsoleCommStruct), Name);
   if (FileMapping == NULL)
   {
-    throw logic_error("Cannot create mapping object.");
+    throw runtime_error("Cannot create mapping object.");
+  }
+
+  typedef HANDLE (* TCreateJobObject)(LPSECURITY_ATTRIBUTES JobAttributes, LPCTSTR Name);
+  typedef HANDLE (* TSetInformationJobObject)(HANDLE Job, JOBOBJECTINFOCLASS JobObjectInformationClass,
+    LPVOID JobObjectInformation, DWORD JobObjectInformationLength);
+
+  HANDLE Kernel32 = GetModuleHandle("kernel32");
+  TCreateJobObject CreateJobObject =
+    (TCreateJobObject)GetProcAddress(Kernel32, "CreateJobObjectA");
+  TSetInformationJobObject SetInformationJobObject =
+    (TSetInformationJobObject)GetProcAddress(Kernel32, "SetInformationJobObject");
+  if ((CreateJobObject != NULL) && (SetInformationJobObject != NULL))
+  {
+    sprintf(Name, "%s%d", CONSOLE_JOB, InstanceNumber);
+    Job = CreateJobObject(NULL, Name);
+    if (Job == NULL)
+    {
+      throw runtime_error("Cannot create job object.");
+    }
+
+    JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimitInformation;
+    memset(&ExtendedLimitInformation, 0, sizeof(ExtendedLimitInformation));
+    ExtendedLimitInformation.BasicLimitInformation.LimitFlags =
+      JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+    if (SetInformationJobObject(Job, JobObjectExtendedLimitInformation,
+          &ExtendedLimitInformation, sizeof(ExtendedLimitInformation)) == 0)
+    {
+      CloseHandle(Job);
+      Job = NULL;
+    }
+  }
+  else
+  {
+    Job = NULL;
   }
 
   TConsoleCommStruct* CommStruct = GetCommStruct(FileMapping);
@@ -174,7 +208,7 @@ void InitializeChild(int argc, char* argv[], int InstanceNumber, HANDLE& Child)
   }
   else
   {
-    throw logic_error("Cannot start WinSCP application.");
+    throw runtime_error("Cannot start WinSCP application.");
   }
 }
 //---------------------------------------------------------------------------
@@ -188,12 +222,13 @@ void FinalizeChild(HANDLE Child)
 }
 //---------------------------------------------------------------------------
 void FinalizeConsole(int /*InstanceNumber*/, HANDLE RequestEvent,
-  HANDLE ResponseEvent, HANDLE CancelEvent, HANDLE FileMapping)
+  HANDLE ResponseEvent, HANDLE CancelEvent, HANDLE FileMapping, HANDLE Job)
 {
   CloseHandle(RequestEvent);
   CloseHandle(ResponseEvent);
   CloseHandle(CancelEvent);
   CloseHandle(FileMapping);
+  CloseHandle(Job);
 }
 //---------------------------------------------------------------------------
 static char LastFromBeginning[sizeof(TConsoleCommStruct::TPrintEvent)] = "";
@@ -481,7 +516,7 @@ void ProcessEvent(HANDLE ResponseEvent, HANDLE FileMapping)
         break;
 
       default:
-        throw logic_error("Unknown event");
+        throw runtime_error("Unknown event");
     }
 
     FreeCommStruct(CommStruct);
@@ -527,9 +562,9 @@ int main(int argc, char* argv[])
     OutputType = GetFileType(ConsoleOutput);
 
     int InstanceNumber;
-    HANDLE RequestEvent, ResponseEvent, FileMapping;
+    HANDLE RequestEvent, ResponseEvent, FileMapping, Job;
     InitializeConsole(InstanceNumber, RequestEvent, ResponseEvent,
-      CancelEvent, FileMapping);
+      CancelEvent, FileMapping, Job);
 
     char SavedTitle[512];
     GetConsoleTitle(SavedTitle, sizeof(SavedTitle));
@@ -571,7 +606,7 @@ int main(int argc, char* argv[])
               break;
 
             default:
-              throw logic_error("Error waiting for communication from child process.");
+              throw runtime_error("Error waiting for communication from child process.");
           }
         }
         while (Continue);
@@ -598,7 +633,7 @@ int main(int argc, char* argv[])
     }
 
     FinalizeConsole(InstanceNumber, RequestEvent, ResponseEvent,
-      CancelEvent, FileMapping);
+      CancelEvent, FileMapping, Job);
   }
   catch(const exception& e)
   {

+ 21 - 6
core/Common.cpp

@@ -960,19 +960,21 @@ AnsiString __fastcall FixedLenDateTimeFormat(const AnsiString & Format)
 int __fastcall CompareFileTime(TDateTime T1, TDateTime T2)
 {
   // "FAT" time precision
-  // 1 ms more solves the rounding issues (see also CustomDirView.pas)
-  static TDateTime Second(0, 0, 1, 1);
+  // (when one time is seconds-precision and other is millisecond-precision,
+  // we may have times like 12:00:00.000 and 12:00:01.999, which should
+  // be treated the same)
+  static TDateTime TwoSeconds(0, 0, 2, 0);
   int Result;
   if (T1 == T2)
   {
     // just optimalisation
     Result = 0;
   }
-  else if ((T1 < T2) && (T2 - T1 > Second))
+  else if ((T1 < T2) && (T2 - T1 >= TwoSeconds))
   {
     Result = -1;
   }
-  else if ((T1 > T2) && (T1 - T2 > Second))
+  else if ((T1 > T2) && (T1 - T2 >= TwoSeconds))
   {
     Result = 1;
   }
@@ -1002,8 +1004,21 @@ bool __fastcall RecursiveDeleteFile(const AnsiString FileName, bool ToRecycleBin
   {
     Data.fFlags |= FOF_ALLOWUNDO;
   }
-  int Result = SHFileOperation(&Data);
-  return (Result == 0);
+  int ErrorCode = SHFileOperation(&Data);
+  bool Result = (ErrorCode == 0);
+  if (!Result)
+  {
+    // according to MSDN, SHFileOperation may return following non-Win32
+    // error codes
+    if (((ErrorCode >= 0x71) && (ErrorCode <= 0x88)) ||
+        (ErrorCode == 0xB7) || (ErrorCode == 0x402) || (ErrorCode == 0x10000) ||
+        (ErrorCode == 0x10074))
+    {
+      ErrorCode = 0;
+    }
+    SetLastError(ErrorCode);
+  }
+  return Result;
 }
 //---------------------------------------------------------------------------
 int __fastcall CancelAnswer(int Answers)

+ 1 - 1
core/Common.h

@@ -3,7 +3,7 @@
 #define CommonH
 //---------------------------------------------------------------------------
 #define EXCEPTION throw ExtException(NULL, "")
-#define THROWIFFALSE(C) if (!(C)) EXCEPTION
+#define THROWOSIFFALSE(C) if (!(C)) RaiseLastOSError();
 #define SCOPY(dest, source) \
   strncpy(dest, source, sizeof(dest)); \
   dest[sizeof(dest)-1] = '\0'

+ 15 - 15
core/Configuration.cpp

@@ -45,6 +45,21 @@ void __fastcall TConfiguration::Default()
 {
   TGuard Guard(FCriticalSection);
 
+  TRegistryStorage * AdminStorage;
+  AdminStorage = new TRegistryStorage(RegistryStorageKey, HKEY_LOCAL_MACHINE);
+  try
+  {
+    if (AdminStorage->OpenRootKey(false))
+    {
+      LoadAdmin(AdminStorage);
+      AdminStorage->CloseSubKey();
+    }
+  }
+  __finally
+  {
+    delete AdminStorage;
+  }
+
   RandomSeedFile = FDefaultRandomSeedFile;
   PuttyRegistryStorageKey = "Software\\SimonTatham\\PuTTY";
   FConfirmOverwriting = true;
@@ -220,21 +235,6 @@ void __fastcall TConfiguration::Load()
   {
     delete Storage;
   }
-
-  TRegistryStorage * AdminStorage;
-  AdminStorage = new TRegistryStorage(RegistryStorageKey, HKEY_LOCAL_MACHINE);
-  try
-  {
-    if (AdminStorage->OpenRootKey(false))
-    {
-      LoadAdmin(AdminStorage);
-      AdminStorage->CloseSubKey();
-    }
-  }
-  __finally
-  {
-    delete AdminStorage;
-  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TConfiguration::CopyData(THierarchicalStorage * Source,

+ 23 - 0
core/Exceptions.cpp

@@ -2,11 +2,18 @@
 #include <vcl.h>
 #pragma hdrstop
 
+#include "Common.h"
 #include "Exceptions.h"
 #include "TextsCore.h"
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
+__fastcall ExtException::ExtException(Exception * E) :
+  Exception("")
+{
+  AddMoreMessages(E);
+}
+//---------------------------------------------------------------------------
 __fastcall ExtException::ExtException(Exception* E, AnsiString Msg):
         Exception(Msg)
 {
@@ -116,3 +123,19 @@ __fastcall ExtException::~ExtException()
 {
   delete FMoreMessages;
 }
+//---------------------------------------------------------------------------
+AnsiString __fastcall LastSysErrorMessage()
+{
+  int LastError = GetLastError();
+  AnsiString Result;
+  if (LastError != 0)
+  {
+    Result = FORMAT(Sysconst_SOSError, (LastError, SysErrorMessage(LastError)));
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+__fastcall EOSExtException::EOSExtException(AnsiString Msg) :
+  ExtException(Msg, LastSysErrorMessage())
+{
+}

+ 8 - 0
core/Exceptions.h

@@ -10,6 +10,7 @@
 class ExtException : public Sysutils::Exception
 {
 public:
+  __fastcall ExtException(Exception* E);
   __fastcall ExtException(Exception* E, AnsiString Msg);
   // "copy the exception", just append message to the end
   __fastcall ExtException(AnsiString Msg, Exception* E);
@@ -55,6 +56,13 @@ DERIVE_EXT_EXCEPTION(EScp, ExtException); // SCP protocol fatal error (non-fatal
 DERIVE_EXT_EXCEPTION(EScpSkipFile, ExtException);
 DERIVE_EXT_EXCEPTION(EScpFileSkipped, EScpSkipFile);
 //---------------------------------------------------------------------------
+class EOSExtException : public ExtException
+{
+public:
+  __fastcall EOSExtException();
+  __fastcall EOSExtException(AnsiString Msg);
+};
+//---------------------------------------------------------------------------
 class EFatal : public ExtException
 {
 public:

+ 4 - 7
core/FileOperationProgress.cpp

@@ -171,7 +171,7 @@ int __fastcall TFileOperationProgressType::OverallProgress()
 //---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::DoProgress()
 {
-  if (FOnProgress) FOnProgress(*this, Cancel);
+  FOnProgress(*this, Cancel);
 }
 //---------------------------------------------------------------------------
 void __fastcall TFileOperationProgressType::Finish(AnsiString FileName,
@@ -179,12 +179,9 @@ void __fastcall TFileOperationProgressType::Finish(AnsiString FileName,
 {
   assert(InProgress);
 
-  if (FOnFinished)
-  {
-    FOnFinished(Operation, Side, Temp, FileName,
-      /* TODO : There wasn't 'Success' condition, was it by mistake or by purpose? */
-      Success && (Cancel == csContinue), DisconnectWhenComplete);
-  }
+  FOnFinished(Operation, Side, Temp, FileName,
+    /* TODO : There wasn't 'Success' condition, was it by mistake or by purpose? */
+    Success && (Cancel == csContinue), DisconnectWhenComplete);
   FFilesFinished++;
   DoProgress();
 }

+ 2 - 2
core/FileOperationProgress.h

@@ -16,7 +16,7 @@ typedef void __fastcall (__closure *TFileOperationProgressEvent)
   (TFileOperationProgressType & ProgressData, TCancelStatus & Cancel);
 typedef void __fastcall (__closure *TFileOperationFinished)
   (TFileOperation Operation, TOperationSide Side, bool Temp,
-    const AnsiString FileName, bool Success, bool & DisconnectWhenComplete);
+    const AnsiString & FileName, bool Success, bool & DisconnectWhenComplete);
 //---------------------------------------------------------------------------
 class TFileOperationProgressType
 {
@@ -36,7 +36,7 @@ private:
 
 protected:
   void __fastcall ClearTransfer();
-  void __fastcall DoProgress();
+  inline void __fastcall DoProgress();
 
 public:
   // common data

+ 4 - 4
core/FtpFileSystem.cpp

@@ -925,7 +925,7 @@ void __fastcall TFTPFileSystem::Sink(const AnsiString FileName,
       FILE_OPERATION_LOOP (FMTLOAD(CREATE_DIR_ERROR, (DestFullName)),
         if (!ForceDirectories(DestFullName))
         {
-          EXCEPTION;
+          RaiseLastOSError();
         }
       );
 
@@ -1018,7 +1018,7 @@ void __fastcall TFTPFileSystem::Sink(const AnsiString FileName,
     if ((NewAttrs & Attrs) != NewAttrs)
     {
       FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DestFullName)),
-        THROWIFFALSE(FileSetAttr(DestFullName, Attrs | NewAttrs) == 0);
+        THROWOSIFFALSE(FileSetAttr(DestFullName, Attrs | NewAttrs) == 0);
       );
     }
   }
@@ -1238,7 +1238,7 @@ void __fastcall TFTPFileSystem::Source(const AnsiString FileName,
   else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
   {
     FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (FileName)),
-      THROWIFFALSE(FileSetAttr(FileName, Attrs & ~faArchive) == 0);
+      THROWOSIFFALSE(FileSetAttr(FileName, Attrs & ~faArchive) == 0);
     )
   }
 }
@@ -1319,7 +1319,7 @@ void __fastcall TFTPFileSystem::DirectorySource(const AnsiString DirectoryName,
     else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
     {
       FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DirectoryName)),
-        THROWIFFALSE(FileSetAttr(DirectoryName, Attrs & ~faArchive) == 0);
+        THROWOSIFFALSE(FileSetAttr(DirectoryName, Attrs & ~faArchive) == 0);
       )
     }
   }

+ 23 - 28
core/PuttyIntf.cpp

@@ -82,38 +82,33 @@ AnsiString __fastcall ProtocolName(int Protocol)
 //---------------------------------------------------------------------------
 extern "C" char * do_select(Plug plug, SOCKET skt, int startup)
 {
-  // is NULL, when sk_newlistener is used to check for free port from
-  // TTerminal::OpenTunnel()
-  if (plug != NULL)
-  {
-    void * frontend;
+  void * frontend;
 
-    if (!is_ssh(plug) && !is_pfwd(plug))
-    {
-      // If it is not SSH/PFwd plug, them it must be Proxy plug.
-      // Get SSH/PFwd plug which it wraps.
-      Proxy_Socket ProxySocket = ((Proxy_Plug)plug)->proxy_socket;
-      plug = ProxySocket->plug;
-    }
+  if (!is_ssh(plug) && !is_pfwd(plug))
+  {
+    // If it is not SSH/PFwd plug, them it must be Proxy plug.
+    // Get SSH/PFwd plug which it wraps.
+    Proxy_Socket ProxySocket = ((Proxy_Plug)plug)->proxy_socket;
+    plug = ProxySocket->plug;
+  }
 
-    bool pfwd = is_pfwd(plug);
-    if (pfwd)
-    {
-      plug = (Plug)get_pfwd_backend(plug);
-    }
+  bool pfwd = is_pfwd(plug);
+  if (pfwd)
+  {
+    plug = (Plug)get_pfwd_backend(plug);
+  }
 
-    frontend = get_ssh_frontend(plug);
-    assert(frontend);
+  frontend = get_ssh_frontend(plug);
+  assert(frontend);
 
-    TSecureShell * SecureShell = reinterpret_cast<TSecureShell*>(frontend);
-    if (!pfwd)
-    {
-      SecureShell->UpdateSocket(skt, startup);
-    }
-    else
-    {
-      SecureShell->UpdatePortFwdSocket(skt, startup);
-    }
+  TSecureShell * SecureShell = reinterpret_cast<TSecureShell*>(frontend);
+  if (!pfwd)
+  {
+    SecureShell->UpdateSocket(skt, startup);
+  }
+  else
+  {
+    SecureShell->UpdatePortFwdSocket(skt, startup);
   }
 
   return NULL;

+ 2 - 2
core/Queue.cpp

@@ -112,7 +112,7 @@ protected:
   void __fastcall TerminalShowExtendedException(TTerminal * Terminal,
     Exception * E, void * Arg);
   void __fastcall OperationFinished(TFileOperation Operation, TOperationSide Side,
-    bool Temp, const AnsiString FileName, bool Success,
+    bool Temp, const AnsiString & FileName, bool Success,
     bool & DisconnectWhenFinished);
   void __fastcall OperationProgress(TFileOperationProgressType & ProgressData,
     TCancelStatus & Cancel);
@@ -1195,7 +1195,7 @@ void __fastcall TTerminalItem::TerminalShowExtendedException(
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalItem::OperationFinished(TFileOperation /*Operation*/,
-  TOperationSide /*Side*/, bool /*Temp*/, const AnsiString /*FileName*/,
+  TOperationSide /*Side*/, bool /*Temp*/, const AnsiString & /*FileName*/,
   bool /*Success*/, bool & /*DisconnectWhenFinished*/)
 {
   // nothing

+ 7 - 6
core/ScpFileSystem.cpp

@@ -374,7 +374,8 @@ void __fastcall TSCPFileSystem::Idle()
   if ((FTerminal->SessionData->PingType != ptOff) &&
       (Now() - FSecureShell->LastDataSent > FTerminal->SessionData->PingIntervalDT))
   {
-    if (FTerminal->SessionData->PingType == ptDummyCommand)
+    if ((FTerminal->SessionData->PingType == ptDummyCommand) &&
+        FSecureShell->Ready)
     {
       if (!FProcessingCommand)
       {
@@ -1754,7 +1755,7 @@ void __fastcall TSCPFileSystem::SCPSource(const AnsiString FileName,
   else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
   {
     FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (FileName)),
-      THROWIFFALSE(FileSetAttr(FileName, Attrs & ~faArchive) == 0);
+      THROWOSIFFALSE(FileSetAttr(FileName, Attrs & ~faArchive) == 0);
     )
   }
 
@@ -1776,7 +1777,7 @@ void __fastcall TSCPFileSystem::SCPDirectorySource(const AnsiString DirectoryNam
   // Get directory attributes
   FILE_OPERATION_LOOP (FMTLOAD(CANT_GET_ATTRS, (DirectoryName)),
     Attrs = FileGetAttr(DirectoryName);
-    if (Attrs == -1) EXCEPTION;
+    if (Attrs == -1) RaiseLastOSError();
   )
 
   AnsiString Buf;
@@ -1851,7 +1852,7 @@ void __fastcall TSCPFileSystem::SCPDirectorySource(const AnsiString DirectoryNam
       else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
       {
         FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DirectoryName)),
-          THROWIFFALSE(FileSetAttr(DirectoryName, Attrs & ~faArchive) == 0);
+          THROWOSIFFALSE(FileSetAttr(DirectoryName, Attrs & ~faArchive) == 0);
         )
       }
     }
@@ -2202,7 +2203,7 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
           if (!FileData.Exists)
           {
             FILE_OPERATION_LOOP (FMTLOAD(CREATE_DIR_ERROR, (DestFileName)),
-              if (!ForceDirectories(DestFileName)) EXCEPTION;
+              if (!ForceDirectories(DestFileName)) RaiseLastOSError();
             );
             /* SCP: can we set the timestamp for directories ? */
           }
@@ -2380,7 +2381,7 @@ void __fastcall TSCPFileSystem::SCPSink(const AnsiString TargetDir,
           if ((NewAttrs & FileData.Attrs) != NewAttrs)
           {
             FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DestFileName)),
-              THROWIFFALSE(FileSetAttr(DestFileName, FileData.Attrs | NewAttrs) == 0);
+              THROWOSIFFALSE(FileSetAttr(DestFileName, FileData.Attrs | NewAttrs) == 0);
             );
           }
         }

+ 13 - 16
core/Script.cpp

@@ -320,11 +320,10 @@ void __fastcall TScript::Init()
   FKeepingUpToDate = false;
 
   FCommands = new TScriptCommands;
-  FCommands->Register(";", 0, 0, &DummyProc, 0, -1, true);
-  FCommands->Register("#", 0, 0, &DummyProc, 0, -1, true);
   FCommands->Register("help", SCRIPT_HELP_DESC, SCRIPT_HELP_HELP, &HelpProc, 0, -1, false);
   FCommands->Register("man", 0, SCRIPT_HELP_HELP, &HelpProc, 0, -1, false);
-  FCommands->Register("call", SCRIPT_CALL_DESC2, SCRIPT_CALL_HELP2, &CallProc, 1, -1, false);
+  // the call command does not have switches itself, but the commands may have
+  FCommands->Register("call", SCRIPT_CALL_DESC2, SCRIPT_CALL_HELP2, &CallProc, 1, -1, true);
   FCommands->Register("!", 0, SCRIPT_CALL_HELP2, &CallProc, 1, -1, false);
   FCommands->Register("pwd", SCRIPT_PWD_DESC, SCRIPT_PWD_HELP, &PwdProc, 0, 0, false);
   FCommands->Register("cd", SCRIPT_CD_DESC, SCRIPT_CD_HELP, &CdProc, 0, 1, false);
@@ -365,15 +364,18 @@ void __fastcall TScript::Command(AnsiString Cmd)
 {
   try
   {
-    if (FEcho)
+    if (!Cmd.Trim().IsEmpty() && (Cmd[1] != ';') && (Cmd[1] != '#'))
     {
-      PrintLine(Cmd);
-    }
+      if (FEcho)
+      {
+        PrintLine(Cmd);
+      }
 
-    AnsiString Command;
-    if (CutToken(Cmd, Command))
-    {
-      FCommands->Execute(Command, Cmd);
+      AnsiString Command;
+      if (CutToken(Cmd, Command))
+      {
+        FCommands->Execute(Command, Cmd);
+      }
     }
   }
   catch(Exception & E)
@@ -671,11 +673,6 @@ bool __fastcall TScript::EnsureCommandSessionFallback(TFSCapability Capability)
   return Result;
 }
 //---------------------------------------------------------------------------
-void __fastcall TScript::DummyProc(TScriptProcParams * /*Parameters*/)
-{
-  // noop
-}
-//---------------------------------------------------------------------------
 void __fastcall TScript::HelpProc(TScriptProcParams * Parameters)
 {
   AnsiString Output;
@@ -1580,7 +1577,7 @@ void __fastcall TManagementScript::TerminalOperationProgress(
 //---------------------------------------------------------------------------
 void __fastcall TManagementScript::TerminalOperationFinished(
   TFileOperation Operation, TOperationSide /*Side*/,
-  bool /*Temp*/, const AnsiString FileName, Boolean Success,
+  bool /*Temp*/, const AnsiString & FileName, Boolean Success,
   bool & /*DisconnectWhenComplete*/)
 {
   assert(Operation != foCalculateSize);

+ 1 - 2
core/Script.h

@@ -90,7 +90,6 @@ protected:
     int Start, int End, TFileListType ListType);
   void __fastcall FreeFileList(TStrings * FileList);
 
-  void __fastcall DummyProc(TScriptProcParams * Parameters);
   void __fastcall HelpProc(TScriptProcParams * Parameters);
   void __fastcall CallProc(TScriptProcParams * Parameters);
   void __fastcall PwdProc(TScriptProcParams * Parameters);
@@ -162,7 +161,7 @@ protected:
   void __fastcall TerminalOperationProgress(TFileOperationProgressType & ProgressData,
     TCancelStatus & Cancel);
   void __fastcall TerminalOperationFinished(TFileOperation Operation, TOperationSide Side,
-    bool Temp, const AnsiString FileName, Boolean Success,
+    bool Temp, const AnsiString & FileName, Boolean Success,
     bool & DisconnectWhenComplete);
 
   void __fastcall PrintActiveSession();

+ 185 - 76
core/SecureShell.cpp

@@ -15,10 +15,6 @@
 #include <winsock2.h>
 #endif
 //---------------------------------------------------------------------------
-#define SSH_ERROR(x) throw ESsh(NULL, x)
-#define SSH_FATAL_ERROR_EXT(E, x) throw ESshFatal(E, x)
-#define SSH_FATAL_ERROR(x) SSH_FATAL_ERROR_EXT(NULL, x)
-//---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
 #define MAX_BUFSIZE 32768
@@ -37,6 +33,8 @@ __fastcall TSecureShell::TSecureShell(TSessionUI* UI,
   FLog = Log;
   FConfiguration = Configuration;
   FActive = false;
+  FWaiting = 0;
+  FOpened = false;
   OutPtr = NULL;
   Pending = NULL;
   FBackendHandle = NULL;
@@ -52,6 +50,7 @@ __fastcall TSecureShell::TSecureShell(TSessionUI* UI,
 //---------------------------------------------------------------------------
 __fastcall TSecureShell::~TSecureShell()
 {
+  assert(FWaiting == 0);
   Active = false;
   ResetConnection();
   CloseHandle(FSocketEvent);
@@ -333,6 +332,7 @@ void __fastcall TSecureShell::Open()
   ResetSessionInfo();
 
   assert(!FSessionInfo.SshImplementation.IsEmpty());
+  FOpened = true;
 }
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::Init()
@@ -357,7 +357,7 @@ void __fastcall TSecureShell::Init()
     {
       if (FAuthenticating && !FAuthenticationLog.IsEmpty())
       {
-        FatalError(&E, FMTLOAD(AUTHENTICATION_LOG, (FAuthenticationLog)));
+        FUI->FatalError(&E, FMTLOAD(AUTHENTICATION_LOG, (FAuthenticationLog)));
       }
       else
       {
@@ -369,7 +369,7 @@ void __fastcall TSecureShell::Init()
   {
     if (FAuthenticating)
     {
-      FatalError(&E, LoadStr(AUTHENTICATION_FAILED));
+      FUI->FatalError(&E, LoadStr(AUTHENTICATION_FAILED));
     }
     else
     {
@@ -844,21 +844,57 @@ void __fastcall TSecureShell::SendEOF()
   SendSpecial(TS_EOF);
 }
 //---------------------------------------------------------------------------
-void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
+int __fastcall TSecureShell::TimeoutPrompt(TQueryParamsTimerEvent PoolEvent)
 {
-  CheckConnection();
-  int BufSize = FBackend->send(FBackendHandle, (char *)Buf, Len);
-  if (Configuration->LogProtocol >= 1)
+  FWaiting++;
+
+  int Answer;
+  try
   {
-    LogEvent(FORMAT("Sent %u bytes", (static_cast<int>(Len))));
-    LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (BufSize)));
+    TQueryParams Params(qpFatalAbort | qpAllowContinueOnError);
+    Params.Timer = 500;
+    Params.TimerEvent = PoolEvent;
+    Params.TimerMessage = FMTLOAD(TIMEOUT_STILL_WAITING, (FSessionData->Timeout));
+    Params.TimerAnswers = qaAbort;
+    Answer = FUI->QueryUser(FMTLOAD(CONFIRM_PROLONG_TIMEOUT2, (FSessionData->Timeout)),
+      NULL, qaRetry | qaAbort, &Params);
   }
-  FLastDataSent = Now();
-  // among other forces receive of pending data to free the servers's send buffer
-  EventSelectLoop(0, false, NULL);
-
-  while (BufSize > MAX_BUFSIZE)
+  __finally
   {
+    FWaiting--;
+  }
+  return Answer;
+}
+//---------------------------------------------------------------------------
+void __fastcall TSecureShell::SendBuffer(unsigned int & Result)
+{
+  // for comments see PoolForData
+  if (!Active)
+  {
+    Result = qaRetry;
+  }
+  else
+  {
+    try
+    {
+      if (FBackend->sendbuffer(FBackendHandle) <= MAX_BUFSIZE)
+      {
+        Result = qaOK;
+      }
+    }
+    catch(...)
+    {
+      Result = qaRetry;
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TSecureShell::DispatchSendBuffer(int BufSize)
+{
+  TDateTime Start = Now();
+  do
+  {
+    CheckConnection();
     if (Configuration->LogProtocol >= 1)
     {
       LogEvent(FORMAT("There are %u bytes remaining in the send buffer, "
@@ -871,6 +907,50 @@ void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
     {
       LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (BufSize)));
     }
+
+    if (Now() - Start > FSessionData->TimeoutDT)
+    {
+      LogEvent("Waiting for dispatching send buffer timed out, asking user what to do.");
+      int Answer = TimeoutPrompt(SendBuffer);
+      switch (Answer)
+      {
+        case qaRetry:
+          Start = Now();
+          break;
+
+        case qaOK:
+          BufSize = 0;
+          break;
+
+        default:
+          assert(false);
+          // fallthru
+
+        case qaAbort:
+          FatalError(LoadStr(USER_TERMINATED));
+          break;
+      }
+    }
+  }
+  while (BufSize > MAX_BUFSIZE);
+}
+//---------------------------------------------------------------------------
+void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
+{
+  CheckConnection();
+  int BufSize = FBackend->send(FBackendHandle, (char *)Buf, Len);
+  if (Configuration->LogProtocol >= 1)
+  {
+    LogEvent(FORMAT("Sent %u bytes", (static_cast<int>(Len))));
+    LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (BufSize)));
+  }
+  FLastDataSent = Now();
+  // among other forces receive of pending data to free the servers's send buffer
+  EventSelectLoop(0, false, NULL);
+
+  if (BufSize > MAX_BUFSIZE)
+  {
+    DispatchSendBuffer(BufSize);
   }
   CheckConnection();
 }
@@ -1008,21 +1088,6 @@ void __fastcall TSecureShell::CaptureOutput(TLogLineType Type,
   FLog->Add(Type, Line);
 }
 //---------------------------------------------------------------------------
-void __fastcall TSecureShell::FatalError(Exception * E, AnsiString Msg)
-{
-  // the same code is in TTerminal
-  if (FActive)
-  {
-    // We log this instead of exception handler, because Close() would
-    // probably cause exception handler to loose pointer to TShellLog()
-    LogEvent("Attempt to close connection due to fatal exception:");
-    FLog->Add(llException, Msg);
-    FLog->AddException(E);
-    Close();
-  }
-  SSH_FATAL_ERROR_EXT(E, Msg);
-}
-//---------------------------------------------------------------------------
 int __fastcall TSecureShell::TranslateErrorMessage(AnsiString & Message)
 {
   static const TPuttyTranslation Translation[] = {
@@ -1044,7 +1109,7 @@ void __fastcall TSecureShell::PuttyFatalError(AnsiString Error)
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::FatalError(AnsiString Error)
 {
-  FatalError(NULL, Error);
+  FUI->FatalError(NULL, Error);
 }
 //---------------------------------------------------------------------------
 void __fastcall inline TSecureShell::LogEvent(const AnsiString & Str)
@@ -1095,7 +1160,7 @@ void __fastcall TSecureShell::UpdateSocket(SOCKET value, bool Startup)
     // Remove the branch eventualy:
     // When TCP connection fails, PuTTY does not release the memory allocate for
     // socket. As a simple hack we call sk_tcp_close() in ssh.c to release the memory,
-    // until they fix it better. Unfortunately sk_tcp_close calles do_select,
+    // until they fix it better. Unfortunately sk_tcp_close calls do_select,
     // so we must filter that out.
   }
   else
@@ -1103,7 +1168,15 @@ void __fastcall TSecureShell::UpdateSocket(SOCKET value, bool Startup)
     assert(value);
     assert((FActive && (FSocket == value)) || (!FActive && Startup));
 
-    SocketEventSelect(value, FSocketEvent, Startup);
+    // filter our "local proxy" connection, which have no socket
+    if (value != INVALID_SOCKET)
+    {
+      SocketEventSelect(value, FSocketEvent, Startup);
+    }
+    else
+    {
+      assert(FSessionData->ProxyMethod == pmCmd);
+    }
 
     if (Startup)
     {
@@ -1165,6 +1238,7 @@ void __fastcall TSecureShell::Discard()
 {
   bool WasActive = FActive;
   FActive = false;
+  FOpened = false;
 
   if (WasActive)
   {
@@ -1177,6 +1251,10 @@ void __fastcall TSecureShell::Close()
   LogEvent("Closing connection.");
   assert(FActive);
 
+  // this is particularly necessary when using local proxy command
+  // (e.g. plink), otherwise it hangs in sk_localproxy_close
+  SendEOF();
+
   FreeBackend();
 
   Discard();
@@ -1198,32 +1276,40 @@ void inline __fastcall TSecureShell::CheckConnection(int Message)
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::PoolForData(WSANETWORKEVENTS & Events, unsigned int & Result)
 {
-  try
+  if (!Active)
   {
-    if (Configuration->LogProtocol >= 2)
+    // see comment below
+    Result = qaRetry;
+  }
+  else
+  {
+    try
     {
-      LogEvent("Pooling for data in case they finally arrives");
-    }
+      if (Configuration->LogProtocol >= 2)
+      {
+        LogEvent("Pooling for data in case they finally arrives");
+      }
 
-    // in extreme condition it may happen that send buffer is full, but there
-    // will be no data comming and we may not empty the send buffer because we
-    // do not process FD_WRITE until we receive any FD_READ
-    if (EventSelectLoop(0, false, &Events))
+      // in extreme condition it may happen that send buffer is full, but there
+      // will be no data comming and we may not empty the send buffer because we
+      // do not process FD_WRITE until we receive any FD_READ
+      if (EventSelectLoop(0, false, &Events))
+      {
+        LogEvent("Data has arrived, closing query to user.");
+        Result = qaOK;
+      }
+    }
+    catch(...)
     {
-      LogEvent("Data has arrived, closing query to user.");
-      Result = qaOK;
+      // if we let the exception out, it may popup another message dialog
+      // in whole event loop, another call to PoolForData from original dialog
+      // would be invoked, leading to an infinite loop.
+      // by retrying we hope (that probably fatal) error would repeat in WaitForData.
+      // anyway now once no actual work is done in EventSelectLoop,
+      // hardly any exception can occur actually
+      Result = qaRetry;
     }
   }
-  catch(...)
-  {
-    // if we let the exception out, it may popup another message dialog
-    // in whole event loop, another call to PoolForData from original dialog
-    // would be invoked, leading to an infinite loop.
-    // by retrying we hope (that probably fatal) error would repeat in WaitForData.
-    // anyway now once no actual work is done in EventSelectLoop,
-    // hardly any exception can occur actually
-    Result = qaRetry;
-  }
 }
 //---------------------------------------------------------------------------
 class TPoolForDataEvent
@@ -1265,13 +1351,7 @@ void __fastcall TSecureShell::WaitForData()
       TPoolForDataEvent Event(this, Events);
 
       LogEvent("Waiting for data timed out, asking user what to do.");
-      TQueryParams Params(qpFatalAbort | qpAllowContinueOnError);
-      Params.Timer = 500;
-      Params.TimerEvent = Event.PoolForData;
-      Params.TimerMessage = FMTLOAD(TIMEOUT_STILL_WAITING, (FSessionData->Timeout));
-      Params.TimerAnswers = qaAbort;
-      int Answer = FUI->QueryUser(FMTLOAD(CONFIRM_PROLONG_TIMEOUT2, (FSessionData->Timeout)),
-        NULL, qaRetry | qaAbort, &Params);
+      int Answer = TimeoutPrompt(Event.PoolForData);
       switch (Answer)
       {
         case qaRetry:
@@ -1403,12 +1483,24 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
     {
       LogEvent("Looking for network events");
     }
-
     unsigned int TicksBefore = GetTickCount();
-    int WaitResult = WaitForSingleObject(FSocketEvent, MSec);
-    switch (WaitResult)
+    int HandleCount;
+    // note that this returns all handles, not only the session-related handles
+    HANDLE * Handles = handle_get_events(&HandleCount);
+    try
     {
-      case WAIT_OBJECT_0:
+      Handles = sresize(Handles, HandleCount + 1, HANDLE);
+      Handles[HandleCount] = FSocketEvent;
+      unsigned int WaitResult = WaitForMultipleObjects(HandleCount + 1, Handles, FALSE, MSec);
+      if (WaitResult < WAIT_OBJECT_0 + HandleCount)
+      {
+        if (handle_got_event(Handles[WaitResult - WAIT_OBJECT_0]))
+        {
+          Result = true;
+        }
+      }
+      else if (WaitResult == WAIT_OBJECT_0 + HandleCount)
+      {
         if (Configuration->LogProtocol >= 1)
         {
           LogEvent("Detected network event");
@@ -1437,25 +1529,29 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
             i++;
           }
         }
-        break;
-
-      case WAIT_TIMEOUT:
+      }
+      else if (WaitResult == WAIT_TIMEOUT)
+      {
         if (Configuration->LogProtocol >= 2)
         {
           LogEvent("Timeout waiting for network events");
         }
 
         MSec = 0;
-        break;
-
-      default:
+      }
+      else
+      {
         if (Configuration->LogProtocol >= 2)
         {
-          LogEvent(FORMAT("Unknown waiting result %d", (WaitResult)));
+          LogEvent(FORMAT("Unknown waiting result %d", (int(WaitResult))));
         }
 
         MSec = 0;
-        break;
+      }
+    }
+    __finally
+    {
+      sfree(Handles);
     }
 
     unsigned int TicksAfter = GetTickCount();
@@ -1489,8 +1585,16 @@ void __fastcall TSecureShell::Idle(unsigned int MSec)
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::KeepAlive()
 {
-  LogEvent("Sending null packet to keep session alive.");
-  SendSpecial(TS_PING);
+  if (FActive && (FWaiting == 0))
+  {
+    LogEvent("Sending null packet to keep session alive.");
+    SendSpecial(TS_PING);
+  }
+  else
+  {
+    // defer next keepalive attempt
+    FLastDataSent = Now();
+  }
 }
 //---------------------------------------------------------------------------
 unsigned long __fastcall TSecureShell::MaxPacketSize()
@@ -1744,3 +1848,8 @@ bool __fastcall TSecureShell::GetStoredCredentialsTried()
 {
   return FStoredPasswordTried || FStoredPasswordTriedForKI;
 }
+//---------------------------------------------------------------------------
+bool __fastcall TSecureShell::GetReady()
+{
+  return FOpened && (FWaiting == 0);
+}

+ 7 - 0
core/SecureShell.h

@@ -42,6 +42,8 @@ private:
   bool FStoredPasswordTried;
   bool FStoredPasswordTriedForKI;
   int FSshVersion;
+  bool FOpened;
+  int FWaiting;
 
   unsigned PendLen;
   unsigned PendSize;
@@ -80,6 +82,10 @@ private:
   bool __fastcall EventSelectLoop(unsigned int MSec, bool ReadEventRequired,
     WSANETWORKEVENTS * Events);
   void __fastcall UpdateSessionInfo();
+  bool __fastcall GetReady();
+  void __fastcall DispatchSendBuffer(int BufSize);
+  void __fastcall SendBuffer(unsigned int & Result);
+  int __fastcall TimeoutPrompt(TQueryParamsTimerEvent PoolEvent);
 
 protected:
   TCaptureOutputEvent FOnCaptureOutput;
@@ -143,6 +149,7 @@ public:
   void __fastcall PuttyLogEvent(const AnsiString & Str);
 
   __property bool Active = { read = FActive, write = SetActive };
+  __property bool Ready = { read = GetReady };
   __property TCaptureOutputEvent OnCaptureOutput = { read = FOnCaptureOutput, write = FOnCaptureOutput };
   __property TDateTime LastDataSent = { read = FLastDataSent };
   __property AnsiString LastTunnelError = { read = FLastTunnelError };

+ 13 - 4
core/SessionData.cpp

@@ -29,6 +29,12 @@ const TKex DefaultKexList[KEX_COUNT] =
 const char FSProtocolNames[FSPROTOCOL_COUNT][11] = { "SCP", "SFTP (SCP)", "SFTP", "SSH", "SFTP", "FTP" };
 const int SshPortNumber = 22;
 const int FtpPortNumber = 21;
+//---------------------------------------------------------------------
+TDateTime __fastcall SecToDateTime(int Sec)
+{
+  return TDateTime((unsigned short)(Sec/60/60),
+    (unsigned short)(Sec/60%60), (unsigned short)(Sec%60), 0);
+}
 //--- TSessionData ----------------------------------------------------
 AnsiString TSessionData::FInvalidChars("\\[]");
 //---------------------------------------------------------------------
@@ -1343,6 +1349,11 @@ void __fastcall TSessionData::SetEOLType(TEOLType value)
   SET_SESSION_PROPERTY(EOLType);
 }
 //---------------------------------------------------------------------------
+TDateTime __fastcall TSessionData::GetTimeoutDT()
+{
+  return SecToDateTime(Timeout);
+}
+//---------------------------------------------------------------------------
 void __fastcall TSessionData::SetTimeout(int value)
 {
   SET_SESSION_PROPERTY(Timeout);
@@ -1410,8 +1421,7 @@ void __fastcall TSessionData::SetPingIntervalDT(TDateTime value)
 //---------------------------------------------------------------------------
 TDateTime __fastcall TSessionData::GetPingIntervalDT()
 {
-  return TDateTime((unsigned short)(PingInterval/60/60),
-    (unsigned short)(PingInterval/60%60), (unsigned short)(PingInterval%60), 0);
+  return SecToDateTime(PingInterval);
 }
 //---------------------------------------------------------------------------
 void __fastcall TSessionData::SetPingType(TPingType value)
@@ -1810,8 +1820,7 @@ void __fastcall TSessionData::SetFtpPingInterval(int value)
 //---------------------------------------------------------------------------
 TDateTime __fastcall TSessionData::GetFtpPingIntervalDT()
 {
-  return TDateTime((unsigned short)(FtpPingInterval/60/60),
-    (unsigned short)(FtpPingInterval/60%60), (unsigned short)(FtpPingInterval%60), 0);
+  return SecToDateTime(FtpPingInterval);
 }
 //---------------------------------------------------------------------------
 void __fastcall TSessionData::SetFtpPingType(TPingType value)

+ 2 - 0
core/SessionData.h

@@ -268,6 +268,7 @@ private:
   void __fastcall SetFtpPingType(TPingType value);
   void __fastcall SetUtf(TAutoSwitch value);
   void __fastcall SetHostKey(AnsiString value);
+  TDateTime __fastcall GetTimeoutDT();
   static AnsiString __fastcall DecodeUrlChars(const AnsiString & S, bool Decode);
 
 public:
@@ -349,6 +350,7 @@ public:
   __property AnsiString Shell = { read = FShell, write = SetShell };
   __property AnsiString SftpServer = { read = FSftpServer, write = SetSftpServer };
   __property int Timeout = { read = FTimeout, write = SetTimeout };
+  __property TDateTime TimeoutDT = { read = GetTimeoutDT };
   __property bool UnsetNationalVars = { read = FUnsetNationalVars, write = SetUnsetNationalVars };
   __property bool IgnoreLsWarnings  = { read=FIgnoreLsWarnings, write=SetIgnoreLsWarnings };
   __property bool TcpNoDelay  = { read=FTcpNoDelay, write=SetTcpNoDelay };

+ 1 - 0
core/SessionInfo.h

@@ -59,6 +59,7 @@ public:
     AnsiString Name, AnsiString Instructions, TStrings * Prompts,
     TStrings * Results) = 0;
   virtual void __fastcall DisplayBanner(const AnsiString & Banner) = 0;
+  virtual void __fastcall FatalError(Exception * E, AnsiString Msg) = 0;
   virtual void __fastcall ShowExtendedException(Exception * E) = 0;
   virtual void __fastcall Closed() = 0;
 };

+ 17 - 13
core/SftpFileSystem.cpp

@@ -1729,7 +1729,8 @@ void __fastcall TSFTPFileSystem::Idle()
   if ((FTerminal->SessionData->PingType != ptOff) &&
       (Now() - FSecureShell->LastDataSent > FTerminal->SessionData->PingIntervalDT))
   {
-    if (FTerminal->SessionData->PingType == ptDummyCommand)
+    if ((FTerminal->SessionData->PingType == ptDummyCommand) &&
+        FSecureShell->Ready)
     {
       TSFTPPacket Packet(SSH_FXP_REALPATH);
       Packet.AddPathString("/", FUtfStrings);
@@ -2733,11 +2734,15 @@ void __fastcall TSFTPFileSystem::DoStartup()
     FTerminal->LogEvent("We will use UTF-8 strings for status messages only");
   }
 
+  FOpenSSH =
+    // Sun SSH is based on OpenSSH (suffers the same bugs)
+    (GetSessionInfo().SshImplementation.Pos("OpenSSH") == 1) ||
+    (GetSessionInfo().SshImplementation.Pos("Sun_SSH") == 1);
+
   FMaxPacketSize = FTerminal->SessionData->SFTPMaxPacketSize;
   if (FMaxPacketSize == 0)
   {
-    if ((GetSessionInfo().SshImplementation.Pos("OpenSSH") == 1) &&
-        (FVersion == 3) && !FSupport->Loaded)
+    if (FOpenSSH && (FVersion == 3) && !FSupport->Loaded)
     {
       FMaxPacketSize = 4 + (256 * 1024); // len + 256kB payload
       FTerminal->LogEvent(FORMAT("Limiting packet size to OpenSSH sftp-server limit of %d bytes",
@@ -3258,8 +3263,7 @@ void __fastcall TSFTPFileSystem::CreateLink(const AnsiString FileName,
   TSFTPPacket Packet(SSH_FXP_SYMLINK);
 
   bool Buggy = (FTerminal->SessionData->SFTPBug[sbSymlink] == asOn) ||
-    ((FTerminal->SessionData->SFTPBug[sbSymlink] == asAuto) &&
-      (GetSessionInfo().SshImplementation.Pos("OpenSSH") == 1));
+    ((FTerminal->SessionData->SFTPBug[sbSymlink] == asAuto) && FOpenSSH);
 
   if (!Buggy)
   {
@@ -3312,7 +3316,7 @@ bool __fastcall TSFTPFileSystem::LoadFilesProperties(TStrings * FileList)
   // without knowledge of server's capabilities, this all make no sense
   if (FSupport->Loaded)
   {
-    TFileOperationProgressType Progress(FTerminal->FOnProgress, FTerminal->FOnFinished);
+    TFileOperationProgressType Progress(&FTerminal->DoProgress, &FTerminal->DoFinished);
     Progress.Start(foGetProperties, osRemote, FileList->Count);
 
     FTerminal->FOperationProgress = &Progress;
@@ -3479,7 +3483,7 @@ void __fastcall TSFTPFileSystem::CalculateFilesChecksum(const AnsiString & Alg,
   TStrings * FileList, TStrings * Checksums,
   TCalculatedChecksumEvent OnCalculatedChecksum)
 {
-  TFileOperationProgressType Progress(FTerminal->FOnProgress, FTerminal->FOnFinished);
+  TFileOperationProgressType Progress(&FTerminal->DoProgress, &FTerminal->DoFinished);
   Progress.Start(foCalculateChecksum, osRemote, FileList->Count);
 
   FTerminal->FOperationProgress = &Progress;
@@ -4158,7 +4162,7 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
   else if (CopyParam->ClearArchive && FLAGSET(OpenParams.LocalFileAttrs, faArchive))
   {
     FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (FileName)),
-      THROWIFFALSE(FileSetAttr(FileName, OpenParams.LocalFileAttrs & ~faArchive) == 0);
+      THROWOSIFFALSE(FileSetAttr(FileName, OpenParams.LocalFileAttrs & ~faArchive) == 0);
     )
   }
 }
@@ -4486,7 +4490,7 @@ void __fastcall TSFTPFileSystem::SFTPDirectorySource(const AnsiString DirectoryN
     else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
     {
       FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DirectoryName)),
-        THROWIFFALSE(FileSetAttr(DirectoryName, Attrs & ~faArchive) == 0);
+        THROWOSIFFALSE(FileSetAttr(DirectoryName, Attrs & ~faArchive) == 0);
       )
     }
   }
@@ -4617,7 +4621,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
       );
 
       FILE_OPERATION_LOOP (FMTLOAD(CREATE_DIR_ERROR, (DestFullName)),
-        if (!ForceDirectories(DestFullName)) EXCEPTION;
+        if (!ForceDirectories(DestFullName)) RaiseLastOSError();
       );
 
       TSinkFileParams SinkFileParams;
@@ -5001,11 +5005,11 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
 
           if (FileExists(DestFullName))
           {
-            if (!Sysutils::DeleteFile(DestFullName)) EXCEPTION;
+            if (!Sysutils::DeleteFile(DestFullName)) RaiseLastOSError();
           }
           if (!Sysutils::RenameFile(DestPartinalFullName, DestFullName))
           {
-            EXCEPTION;
+            RaiseLastOSError();
           }
         );
       }
@@ -5020,7 +5024,7 @@ void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
       if ((NewAttrs & Attrs) != NewAttrs)
       {
         FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DestFullName)),
-          THROWIFFALSE(FileSetAttr(DestFullName, Attrs | NewAttrs) == 0);
+          THROWOSIFFALSE(FileSetAttr(DestFullName, Attrs | NewAttrs) == 0);
         );
       }
 

+ 1 - 0
core/SftpFileSystem.h

@@ -100,6 +100,7 @@ protected:
   bool FUtfStrings;
   bool FUtfNever;
   bool FSignedTS;
+  bool FOpenSSH;
   TStrings * FFixedPaths;
   unsigned long FMaxPacketSize;
 

+ 187 - 39
core/Terminal.cpp

@@ -31,10 +31,6 @@
 //---------------------------------------------------------------------------
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
-#define SSH_ERROR(x) throw ESsh(NULL, x)
-#define SSH_FATAL_ERROR_EXT(E, x) throw ESshFatal(E, x)
-#define SSH_FATAL_ERROR(x) SSH_FATAL_ERROR_EXT(NULL, x)
-//---------------------------------------------------------------------------
 #define COMMAND_ERROR_ARI(MESSAGE, REPEAT) \
   { \
     int Result = CommandError(&E, MESSAGE, qaRetry | qaSkip | qaAbort); \
@@ -227,6 +223,7 @@ public:
     AnsiString Name, AnsiString Instructions, TStrings * Prompts,
     TStrings * Results);
   virtual void __fastcall DisplayBanner(const AnsiString & Banner);
+  virtual void __fastcall FatalError(Exception * E, AnsiString Msg);
   virtual void __fastcall ShowExtendedException(Exception * E);
   virtual void __fastcall Closed();
 
@@ -311,6 +308,11 @@ void __fastcall TTunnelUI::DisplayBanner(const AnsiString & Banner)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TTunnelUI::FatalError(Exception * E, AnsiString Msg)
+{
+  throw ESshFatal(E, Msg);
+}
+//---------------------------------------------------------------------------
 void __fastcall TTunnelUI::ShowExtendedException(Exception * E)
 {
   if (GetCurrentThreadId() == FTerminalThread)
@@ -325,6 +327,80 @@ void __fastcall TTunnelUI::Closed()
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+class TCallbackGuard
+{
+public:
+  inline __fastcall TCallbackGuard(TTerminal * FTerminal);
+  inline __fastcall ~TCallbackGuard();
+
+  void __fastcall FatalError(Exception * E, const AnsiString & Msg);
+  inline void __fastcall Verify();
+  void __fastcall Dismiss();
+
+private:
+  ExtException * FFatalError;
+  TTerminal * FTerminal;
+  bool FGuarding;
+};
+//---------------------------------------------------------------------------
+__fastcall TCallbackGuard::TCallbackGuard(TTerminal * Terminal) :
+  FTerminal(Terminal),
+  FFatalError(NULL),
+  FGuarding(FTerminal->FCallbackGuard == NULL)
+{
+  if (FGuarding)
+  {
+    FTerminal->FCallbackGuard = this;
+  }
+}
+//---------------------------------------------------------------------------
+__fastcall TCallbackGuard::~TCallbackGuard()
+{
+  if (FGuarding)
+  {
+    assert((FTerminal->FCallbackGuard == this) || (FTerminal->FCallbackGuard == NULL));
+    FTerminal->FCallbackGuard = NULL;
+  }
+
+  delete FFatalError;
+}
+//---------------------------------------------------------------------------
+void __fastcall TCallbackGuard::FatalError(Exception * E, const AnsiString & Msg)
+{
+  assert(FGuarding);
+  assert(FFatalError == NULL);
+
+  FFatalError = new ExtException(E, Msg);
+
+  // silently abort what we are doing.
+  // non-silent exception would be catched probably by default application
+  // exception handler, which may not do an appropriate action
+  // (particularly it will not resume broken transfer).
+  Abort();
+}
+//---------------------------------------------------------------------------
+void __fastcall TCallbackGuard::Dismiss()
+{
+  assert(FFatalError == NULL);
+  FGuarding = false;
+}
+//---------------------------------------------------------------------------
+void __fastcall TCallbackGuard::Verify()
+{
+  if (FGuarding)
+  {
+    FGuarding = false;
+    assert(FTerminal->FCallbackGuard == this);
+    FTerminal->FCallbackGuard = NULL;
+
+    if (FFatalError != NULL)
+    {
+      throw ESshFatal(FFatalError, "");
+    }
+  }
+}
+//---------------------------------------------------------------------------
+//---------------------------------------------------------------------------
 __fastcall TTerminal::TTerminal(TSessionData * SessionData,
   TConfiguration * Configuration)
 {
@@ -369,6 +445,7 @@ __fastcall TTerminal::TTerminal(TSessionData * SessionData,
   FTunnelLog = NULL;
   FTunnelUI = NULL;
   FTunnelOpening = false;
+  FCallbackGuard = NULL;
 }
 //---------------------------------------------------------------------------
 __fastcall TTerminal::~TTerminal()
@@ -378,6 +455,11 @@ __fastcall TTerminal::~TTerminal()
     Close();
   }
 
+  if (FCallbackGuard != NULL)
+  {
+    // see TTerminal::ShowExtendedException
+    FCallbackGuard->Dismiss();
+  }
   assert(FTunnel == NULL);
 
   SAFE_DESTROY(FCommandSession);
@@ -401,29 +483,34 @@ __fastcall TTerminal::~TTerminal()
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::Idle()
 {
-  if (Configuration->LogProtocol >= 1)
-  {
-    LogEvent("Session upkeep");
-  }
-
-  assert(FFileSystem != NULL);
-  FFileSystem->Idle();
-
-  if (CommandSessionOpened)
+  // once we disconnect, do nothing, until reconnect handler
+  // "receives the information"
+  if (Active)
   {
-    try
+    if (Configuration->LogProtocol >= 1)
     {
-      FCommandSession->Idle();
+      LogEvent("Session upkeep");
     }
-    catch(Exception & E)
+
+    assert(FFileSystem != NULL);
+    FFileSystem->Idle();
+
+    if (CommandSessionOpened)
     {
-      // If the secondary session is dropped, ignore the error and let
-      // it be reconnected when needed.
-      // BTW, non-fatal error can hardly happen here, that's why
-      // it is displayed, because it can be useful to know.
-      if (FCommandSession->Active)
+      try
+      {
+        FCommandSession->Idle();
+      }
+      catch(Exception & E)
       {
-        FCommandSession->ShowExtendedException(&E);
+        // If the secondary session is dropped, ignore the error and let
+        // it be reconnected when needed.
+        // BTW, non-fatal error can hardly happen here, that's why
+        // it is displayed, because it can be useful to know.
+        if (FCommandSession->Active)
+        {
+          FCommandSession->ShowExtendedException(&E);
+        }
       }
     }
   }
@@ -754,7 +841,9 @@ void __fastcall TTerminal::Closed()
 
   if (OnClose)
   {
+    TCallbackGuard Guard(this);
     OnClose(this);
+    Guard.Verify();
   }
 
   FStatus = ssClosed;
@@ -846,7 +935,9 @@ bool __fastcall TTerminal::DoPromptUser(TSessionData * /*Data*/, TPromptKind Kin
 
   if (OnPromptUser != NULL)
   {
+    TCallbackGuard Guard(this);
     OnPromptUser(this, Kind, Name, Instructions, Prompts, Results, AResult, NULL);
+    Guard.Verify();
   }
 
   if (AResult && (Configuration->RememberPassword) &&
@@ -868,7 +959,9 @@ int __fastcall TTerminal::QueryUser(const AnsiString Query,
   int Answer = AbortAnswer(Answers);
   if (FOnQueryUser)
   {
+    TCallbackGuard Guard(this);
     FOnQueryUser(this, Query, MoreMessages, Answers, Params, Answer, QueryType, NULL);
+    Guard.Verify();
   }
   return Answer;
 }
@@ -912,8 +1005,10 @@ void __fastcall TTerminal::DisplayBanner(const AnsiString & Banner)
       bool NeverShowAgain = false;
       int Options =
         FLAGMASK(Configuration->ForceBanners, boDisableNeverShowAgain);
+      TCallbackGuard Guard(this);
       OnDisplayBanner(this, SessionData->SessionName, Banner,
         NeverShowAgain, Options);
+      Guard.Verify();
       if (!Configuration->ForceBanners && NeverShowAgain)
       {
         Configuration->NeverShowBanner(SessionData->SessionKey, Banner);
@@ -927,7 +1022,11 @@ void __fastcall TTerminal::ShowExtendedException(Exception * E)
   Log->AddException(E);
   if (OnShowExtendedException != NULL)
   {
+    TCallbackGuard Guard(this);
+    // the event handler may destroy 'this' ...
     OnShowExtendedException(this, E, NULL);
+    // .. hence guard is dismissed from destructor, to make following call no-op
+    Guard.Verify();
   }
 }
 //---------------------------------------------------------------------------
@@ -941,7 +1040,9 @@ void __fastcall TTerminal::DoInformation(const AnsiString & Str, bool Status,
 
   if (OnInformation)
   {
+    TCallbackGuard Guard(this);
     OnInformation(this, Str, Status, Active);
+    Guard.Verify();
   }
 }
 //---------------------------------------------------------------------------
@@ -950,6 +1051,28 @@ void __fastcall TTerminal::Information(const AnsiString & Str, bool Status)
   DoInformation(Str, Status);
 }
 //---------------------------------------------------------------------------
+void __fastcall TTerminal::DoProgress(TFileOperationProgressType & ProgressData,
+  TCancelStatus & Cancel)
+{
+  if (OnProgress != NULL)
+  {
+    TCallbackGuard Guard(this);
+    OnProgress(ProgressData, Cancel);
+    Guard.Verify();
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TTerminal::DoFinished(TFileOperation Operation, TOperationSide Side, bool Temp,
+  const AnsiString & FileName, bool Success, bool & DisconnectWhenComplete)
+{
+  if (OnFinished != NULL)
+  {
+    TCallbackGuard Guard(this);
+    OnFinished(Operation, Side, Temp, FileName, Success, DisconnectWhenComplete);
+    Guard.Verify();
+  }
+}
+//---------------------------------------------------------------------------
 bool __fastcall TTerminal::GetIsCapable(TFSCapability Capability) const
 {
   assert(FFileSystem);
@@ -1352,7 +1475,9 @@ void __fastcall TTerminal::DoChangeDirectory()
 {
   if (FOnChangeDirectory)
   {
+    TCallbackGuard Guard(this);
     FOnChangeDirectory(this);
+    Guard.Verify();
   }
 }
 //---------------------------------------------------------------------------
@@ -1360,7 +1485,9 @@ void __fastcall TTerminal::DoReadDirectory(bool ReloadOnly)
 {
   if (FOnReadDirectory)
   {
+    TCallbackGuard Guard(this);
     FOnReadDirectory(this, ReloadOnly);
+    Guard.Verify();
   }
 }
 //---------------------------------------------------------------------------
@@ -1368,7 +1495,9 @@ void __fastcall TTerminal::DoStartReadDirectory()
 {
   if (FOnStartReadDirectory)
   {
+    TCallbackGuard Guard(this);
     FOnStartReadDirectory(this);
+    Guard.Verify();
   }
 }
 //---------------------------------------------------------------------------
@@ -1376,7 +1505,9 @@ void __fastcall TTerminal::DoReadDirectoryProgress(int Progress, bool & Cancel)
 {
   if (FReadingCurrentDirectory && (FOnReadDirectoryProgress != NULL))
   {
+    TCallbackGuard Guard(this);
     FOnReadDirectoryProgress(this, Progress, Cancel);
+    Guard.Verify();
   }
 }
 //---------------------------------------------------------------------------
@@ -1449,7 +1580,6 @@ bool __fastcall TTerminal::GetExceptionOnFail() const
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::FatalError(Exception * E, AnsiString Msg)
 {
-  // the same code is in TSecureShell
   if (Active)
   {
     // We log this instead of exception handler, because Close() would
@@ -1459,17 +1589,27 @@ void __fastcall TTerminal::FatalError(Exception * E, AnsiString Msg)
     Log->AddException(E);
     Close();
   }
-  SSH_FATAL_ERROR_EXT(E, Msg);
+
+  if (FCallbackGuard != NULL)
+  {
+    FCallbackGuard->FatalError(E, Msg);
+  }
+  else
+  {
+    throw ESshFatal(E, Msg);
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminal::CommandError(Exception * E, const AnsiString Msg)
 {
+  assert(FCallbackGuard == NULL);
   CommandError(E, Msg, 0);
 }
 //---------------------------------------------------------------------------
 int __fastcall TTerminal::CommandError(Exception * E, const AnsiString Msg,
   int Answers)
 {
+  assert(FCallbackGuard == NULL);
   int Result = 0;
   if (E && E->InheritsFrom(__classid(EFatal)))
   {
@@ -2021,7 +2161,7 @@ bool __fastcall TTerminal::ProcessFiles(TStrings * FileList,
 
   try
   {
-    TFileOperationProgressType Progress(FOnProgress, FOnFinished);
+    TFileOperationProgressType Progress(&DoProgress, &DoFinished);
     Progress.Start(Operation, Side, FileList->Count);
 
     FOperationProgress = &Progress;
@@ -2992,7 +3132,7 @@ bool __fastcall TTerminal::CreateLocalFile(const AnsiString FileName,
             FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (FileName)),
               if (FileSetAttr(FileName, FileAttr & ~(faReadOnly | faHidden)) != 0)
               {
-                EXCEPTION;
+                RaiseLastOSError();
               }
             );
           }
@@ -3003,7 +3143,7 @@ bool __fastcall TTerminal::CreateLocalFile(const AnsiString FileName,
         }
         else
         {
-          EXCEPTION;
+          RaiseLastOSError();
         }
       }
     }
@@ -3023,7 +3163,7 @@ void __fastcall TTerminal::OpenLocalFile(const AnsiString FileName,
 
   FILE_OPERATION_LOOP (FMTLOAD(FILE_NOT_EXISTS, (FileName)),
     Attrs = FileGetAttr(FileName);
-    if (Attrs == -1) EXCEPTION;
+    if (Attrs == -1) RaiseLastOSError();
   )
 
   if ((Attrs & faDirectory) == 0)
@@ -3043,7 +3183,7 @@ void __fastcall TTerminal::OpenLocalFile(const AnsiString FileName,
       if (Handle == INVALID_HANDLE_VALUE)
       {
         Handle = 0;
-        EXCEPTION;
+        RaiseLastOSError();
       }
     );
 
@@ -3056,7 +3196,7 @@ void __fastcall TTerminal::OpenLocalFile(const AnsiString FileName,
           FILETIME ATime;
           FILETIME MTime;
           FILETIME CTime;
-          if (!GetFileTime(Handle, &CTime, &ATime, &MTime)) EXCEPTION;
+          if (!GetFileTime(Handle, &CTime, &ATime, &MTime)) RaiseLastOSError();
           if (ACTime)
           {
             *ACTime = ConvertTimestampToUnixSafe(CTime, SessionData->DSTMode);
@@ -3079,7 +3219,7 @@ void __fastcall TTerminal::OpenLocalFile(const AnsiString FileName,
           unsigned long LSize;
           unsigned long HSize;
           LSize = GetFileSize(Handle, &HSize);
-          if ((LSize == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) EXCEPTION;
+          if ((LSize == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) RaiseLastOSError();
           *ASize = (__int64(HSize) << 32) + LSize;
         );
       }
@@ -3172,7 +3312,7 @@ void __fastcall TTerminal::CalculateLocalFileSize(const AnsiString FileName,
 void __fastcall TTerminal::CalculateLocalFilesSize(TStrings * FileList,
   __int64 & Size, const TCopyParamType * CopyParam)
 {
-  TFileOperationProgressType OperationProgress(FOnProgress, FOnFinished);
+  TFileOperationProgressType OperationProgress(&DoProgress, &DoFinished);
   bool DisconnectWhenComplete = false;
   OperationProgress.Start(foCalculateSize, osLocal, FileList->Count);
   try
@@ -3497,11 +3637,19 @@ void __fastcall TTerminal::SynchronizeCollectFile(const AnsiString FileName,
           bool LocalModified = false;
           // for spTimestamp+spBySize require that the file sizes are the same
           // before comparing file time
-          bool TimeCompare =
-            FLAGCLEAR(Data->Params, spNotByTime) &&
-            (FLAGCLEAR(Data->Params, spTimestamp) ||
-             FLAGCLEAR(Data->Params, spBySize) ||
-             (ChecklistItem->Local.Size == ChecklistItem->Remote.Size));
+          int TimeCompare;
+          if (FLAGCLEAR(Data->Params, spNotByTime) &&
+              (FLAGCLEAR(Data->Params, spTimestamp) ||
+               FLAGCLEAR(Data->Params, spBySize) ||
+               (ChecklistItem->Local.Size == ChecklistItem->Remote.Size)))
+          {
+            TimeCompare = CompareFileTime(ChecklistItem->Local.Modification,
+                 ChecklistItem->Remote.Modification);
+          }
+          else
+          {
+            TimeCompare = 0;
+          }
           if (TimeCompare &&
               (CompareFileTime(ChecklistItem->Local.Modification,
                  ChecklistItem->Remote.Modification) < 0))
@@ -3920,7 +4068,7 @@ bool __fastcall TTerminal::CopyToRemote(TStrings * FilesToCopy,
         (FLAGCLEAR(Params, cpDelete) ? CopyParam : NULL));
     }
 
-    TFileOperationProgressType OperationProgress(FOnProgress, FOnFinished);
+    TFileOperationProgressType OperationProgress(&DoProgress, &DoFinished);
     OperationProgress.Start((Params & cpDelete ? foMove : foCopy), osLocal,
       FilesToCopy->Count, Params & cpTemporary, TargetDir, CopyParam->CPSLimit);
 
@@ -4003,7 +4151,7 @@ bool __fastcall TTerminal::CopyToLocal(TStrings * FilesToCopy,
     {
       __int64 TotalSize;
       bool TotalSizeKnown = false;
-      TFileOperationProgressType OperationProgress(FOnProgress, FOnFinished);
+      TFileOperationProgressType OperationProgress(&DoProgress, &DoFinished);
 
       if (CopyParam->CalculateSize)
       {

+ 8 - 1
core/Terminal.h

@@ -26,6 +26,7 @@ struct TFileSystemInfo;
 struct TSpaceAvailable;
 typedef TStringList TUsersGroupsList;
 class TTunnelUI;
+class TCallbackGuard;
 //---------------------------------------------------------------------------
 typedef void __fastcall (__closure *TQueryUserEvent)
   (TObject * Sender, const AnsiString Query, TStrings * MoreMessages, int Answers,
@@ -140,6 +141,7 @@ friend class TSCPFileSystem;
 friend class TSFTPFileSystem;
 friend class TFTPFileSystem;
 friend class TTunnelUI;
+friend class TCallbackGuard;
 
 private:
   TSessionData * FSessionData;
@@ -189,6 +191,8 @@ private:
   TInformationEvent FOnInformation;
   TNotifyEvent FOnClose;
   bool FAnyInformation;
+  TCallbackGuard * FCallbackGuard;
+
   void __fastcall CommandError(Exception * E, const AnsiString Msg);
   int __fastcall CommandError(Exception * E, const AnsiString Msg, int Answers);
   AnsiString __fastcall PeekCurrentDirectory();
@@ -285,7 +289,7 @@ protected:
   TStrings * __fastcall GetFixedPaths();
   void __fastcall DoStartup();
   virtual bool __fastcall DoQueryReopen(Exception * E);
-  void __fastcall FatalError(Exception * E, AnsiString Msg);
+  virtual void __fastcall FatalError(Exception * E, AnsiString Msg);
   void __fastcall ResetConnection();
   virtual bool __fastcall DoPromptUser(TSessionData * Data, TPromptKind Kind,
     AnsiString Name, AnsiString Instructions, TStrings * Prompts,
@@ -310,6 +314,9 @@ protected:
   virtual void __fastcall DisplayBanner(const AnsiString & Banner);
   virtual void __fastcall Closed();
   bool __fastcall IsListenerFree(unsigned int PortNumber);
+  void __fastcall DoProgress(TFileOperationProgressType & ProgressData, TCancelStatus & Cancel);
+  void __fastcall DoFinished(TFileOperation Operation, TOperationSide Side, bool Temp,
+    const AnsiString & FileName, bool Success, bool & DisconnectWhenComplete);
 
   __property TFileOperationProgressType * OperationProgress = { read=FOperationProgress };
 

+ 44 - 5
dragext/DragExt.cpp

@@ -714,7 +714,50 @@ STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND Hwnd, UINT Func, UINT Flags,
                 {
                   DEBUG("CShellExt::CopyCallback dragging");
                   DEBUG(CommStruct->DropDest);
-                  if (strcmp(CommStruct->DropDest, SrcFile) == 0)
+                  bool IsDropDest;
+                  if (stricmp(CommStruct->DropDest, SrcFile) == 0)
+                  {
+                    IsDropDest = true;
+                    DEBUG("CShellExt::CopyCallback dragged file match as is");
+                  }
+                  else
+                  {
+                    char DropDestShort[MAX_PATH];
+                    char SrcFileShort[MAX_PATH];
+                    size_t DropDestSize = GetShortPathName(CommStruct->DropDest,
+                      DropDestShort, sizeof(DropDestShort));
+                    size_t SrcFileSize = GetShortPathName(SrcFile,
+                      SrcFileShort, sizeof(SrcFileShort));
+                    if ((DropDestSize == 0) || (SrcFileSize == 0))
+                    {
+                      DEBUG("CShellExt::CopyCallback cannot convert paths to short form");
+                      IsDropDest = false;
+                    }
+                    else if ((DropDestSize >= sizeof(DropDestShort)) ||
+                        (SrcFileSize >= sizeof(SrcFileShort)))
+                    {
+                      DEBUG("CShellExt::CopyCallback short paths too long");
+                      IsDropDest = false;
+                    }
+                    else
+                    {
+                      DEBUG(DropDestShort);
+                      DEBUG(SrcFileShort);
+
+                      if (stricmp(DropDestShort, SrcFileShort) == 0)
+                      {
+                        DEBUG("CShellExt::CopyCallback dragged file match after converted to short form");
+                        IsDropDest = true;
+                      }
+                      else
+                      {
+                        DEBUG("CShellExt::CopyCallback dragged file does NOT match");
+                        IsDropDest = false;
+                      }
+                    }
+                  }
+
+                  if (IsDropDest)
                   {
                     CommStruct->Dragging = false;
                     strncpy(CommStruct->DropDest, DestFile, sizeof(CommStruct->DropDest));
@@ -722,10 +765,6 @@ STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND Hwnd, UINT Func, UINT Flags,
                     Result = IDNO;
                     DEBUG("CShellExt::CopyCallback dragging refused");
                   }
-                  else
-                  {
-                    DEBUG("CShellExt::CopyCallback dragged file does NOT match");
-                  }
                 }
                 else
                 {

+ 4 - 4
forms/CustomScpExplorer.cpp

@@ -886,7 +886,7 @@ bool __fastcall TCustomScpExplorerForm::PanelOperation(TOperationSide /*Side*/,
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::DoOperationFinished(
   TFileOperation Operation, TOperationSide Side,
-  bool /*Temp*/, const AnsiString FileName, bool Success,
+  bool /*Temp*/, const AnsiString & FileName, bool Success,
   bool & DisconnectWhenComplete)
 {
   if (!FAutoOperation)
@@ -939,7 +939,7 @@ void __fastcall TCustomScpExplorerForm::DoOperationFinished(
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::OperationFinished(
   TFileOperation Operation, TOperationSide Side,
-  bool Temp, const AnsiString FileName, Boolean Success,
+  bool Temp, const AnsiString & FileName, Boolean Success,
   bool & DisconnectWhenComplete)
 {
   DoOperationFinished(Operation, Side, Temp, FileName, Success,
@@ -5632,8 +5632,8 @@ void __fastcall TCustomScpExplorerForm::FormConstrainedResize(
   {
     MinWidth = 0;
     MinHeight = 0;
-    MaxWidth = 0;
-    MaxHeight = 0;
+    MaxWidth = 32000;
+    MaxHeight = 32000;
   }
 }
 //---------------------------------------------------------------------------

+ 6 - 6
forms/CustomScpExplorer.h

@@ -280,7 +280,7 @@ protected:
   void __fastcall UpdateStatusBar();
   virtual void __fastcall UpdateStatusPanelText(TTBXStatusPanel * Panel);
   virtual void __fastcall DoOperationFinished(TFileOperation Operation,
-    TOperationSide Side, bool Temp, const AnsiString FileName, bool Success,
+    TOperationSide Side, bool Temp, const AnsiString & FileName, bool Success,
     bool & DisconnectWhenFinished);
   virtual void __fastcall DoOpenDirectoryDialog(TOpenDirectoryMode Mode, TOperationSide Side);
   virtual void __fastcall FileOperationProgress(
@@ -292,10 +292,10 @@ protected:
     const TEditedFileData & Data);
   void __fastcall ExecutedFileEarlyClosed(const TEditedFileData & Data,
     bool & KeepOpen);
-  void __fastcall CMAppSysCommand(TMessage & Message);
-  void __fastcall WMAppCommand(TMessage & Message);
-  void __fastcall WMSysCommand(TMessage & Message);
-  void __fastcall WMWindowPosChanging(TWMWindowPosMsg & Message);
+  inline void __fastcall CMAppSysCommand(TMessage & Message);
+  inline void __fastcall WMAppCommand(TMessage & Message);
+  inline void __fastcall WMSysCommand(TMessage & Message);
+  inline void __fastcall WMWindowPosChanging(TWMWindowPosMsg & Message);
   virtual void __fastcall SysResizing(unsigned int Cmd);
   DYNAMIC void __fastcall DoShow();
   TStrings * __fastcall CreateVisitedDirectories(TOperationSide Side);
@@ -476,7 +476,7 @@ public:
     AnsiString HelpKeyword, const TMessageParams * Params = NULL,
     TTerminal * Terminal = NULL);
   void __fastcall OperationFinished(TFileOperation Operation, TOperationSide Side,
-    bool Temp, const AnsiString FileName, bool Success, bool & DisconnectWhenFinished);
+    bool Temp, const AnsiString & FileName, bool Success, bool & DisconnectWhenFinished);
   void __fastcall OperationProgress(TFileOperationProgressType & ProgressData, TCancelStatus & Cancel);
   void __fastcall ShowExtendedException(TTerminal * Terminal, Exception * E);
   void __fastcall InactiveTerminalException(TTerminal * Terminal, Exception * E);

+ 14 - 3
forms/LocationProfiles.cpp

@@ -62,6 +62,8 @@ __fastcall TLocationProfilesDialog::TLocationProfilesDialog(TComponent * AOwner)
   FFolders->Sorted = true;
   FFolders->Duplicates = dupIgnore;
 
+  FScrollOnDragOver = new TTreeViewScrollOnDragOver(ProfilesView, true);
+
   UseSystemSettings(this);
 
   InstallPathWordBreakProc(LocalDirectoryEdit);
@@ -70,6 +72,7 @@ __fastcall TLocationProfilesDialog::TLocationProfilesDialog(TComponent * AOwner)
 //---------------------------------------------------------------------
 __fastcall TLocationProfilesDialog::~TLocationProfilesDialog()
 {
+  SAFE_DESTROY(FScrollOnDragOver);
   SAFE_DESTROY(FBookmarkList);
   SAFE_DESTROY(FFolders);
 }
@@ -455,23 +458,25 @@ void __fastcall TLocationProfilesDialog::BookmarkButtonClick(TObject *Sender)
 }
 //---------------------------------------------------------------------------
 void __fastcall TLocationProfilesDialog::ProfilesViewStartDrag(
-      TObject * /*Sender*/ , TDragObject *& /*DragObject*/)
+  TObject * /*Sender*/, TDragObject *& /*DragObject*/)
 {
   if (!ProfilesView->Selected->Data)
   {
     Abort();
   }
   FBookmarkDragSource = ProfilesView->Selected;
+  FScrollOnDragOver->StartDrag();
 }
 //---------------------------------------------------------------------------
 void __fastcall TLocationProfilesDialog::ProfilesViewDragOver(
-    TObject */*Sender*/, TObject *Source, int /*X*/, int /*Y*/,
-    TDragState /*State*/, bool &Accept)
+  TObject * /*Sender*/, TObject * Source, int X, int Y,
+  TDragState /*State*/, bool & Accept)
 {
   if (Source == ProfilesView)
   {
     Accept = (ProfilesView->DropTarget != NULL) &&
       (FBookmarkDragSource != ProfilesView->DropTarget);
+    FScrollOnDragOver->DragOver(TPoint(X, Y));
   }
 }
 //---------------------------------------------------------------------------
@@ -746,3 +751,9 @@ void __fastcall TLocationProfilesDialog::UpdateActions()
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TLocationProfilesDialog::ProfilesViewEndDrag(
+  TObject * /*Sender*/, TObject * /*Target*/, int /*X*/, int /*Y*/)
+{
+  FScrollOnDragOver->EndDrag();
+}
+//---------------------------------------------------------------------------

+ 1 - 0
forms/LocationProfiles.dfm

@@ -84,6 +84,7 @@ object LocationProfilesDialog: TLocationProfilesDialog
       OnDragOver = ProfilesViewDragOver
       OnEdited = ProfilesViewEdited
       OnEditing = ProfilesViewEditing
+      OnEndDrag = ProfilesViewEndDrag
       OnExpanded = ProfilesViewExpanded
       OnGetImageIndex = ProfilesViewGetImageIndex
       OnGetSelectedIndex = ProfilesViewGetSelectedIndex

+ 4 - 0
forms/LocationProfiles.h

@@ -22,6 +22,7 @@
 #include "IEComboBox.hpp"
 #include <ComCtrls.hpp>
 #include <ImgList.hpp>
+#include <PasTools.hpp>
 //----------------------------------------------------------------------------
 class TLocationProfilesDialog : public TForm
 {
@@ -75,6 +76,8 @@ __published:
     AnsiString & S);
   void __fastcall ProfilesViewEditing(TObject * Sender, TTreeNode * Node,
     bool & AllowEdit);
+  void __fastcall ProfilesViewEndDrag(TObject *Sender, TObject *Target,
+          int X, int Y);
 
 public:
   __fastcall TLocationProfilesDialog(TComponent* AOwner);
@@ -106,6 +109,7 @@ private:
   TBookmarkList * FBookmarkList;
   AnsiString FLocalDirectory;
   AnsiString FRemoteDirectory;
+  TTreeViewScrollOnDragOver * FScrollOnDragOver;
 
   void __fastcall SetLocalDirectory(AnsiString value);
   AnsiString __fastcall GetLocalDirectory();

+ 11 - 0
forms/Login.cpp

@@ -72,6 +72,7 @@ __fastcall TLoginDialog::TLoginDialog(TComponent* AOwner)
   FTreeLabels = new TStringList();
   FRecycleBinSheetVisible = false;
   FHintNode = NULL;
+  FScrollOnDragOver = new TTreeViewScrollOnDragOver(SessionTree, true);
 
   // we need to make sure that window procedure is set asap
   // (so that CM_SHOWINGCHANGED handling is applied)
@@ -81,6 +82,7 @@ __fastcall TLoginDialog::TLoginDialog(TComponent* AOwner)
 //---------------------------------------------------------------------
 __fastcall TLoginDialog::~TLoginDialog()
 {
+  delete FScrollOnDragOver;
   assert(FSystemSettings);
   DeleteSystemSettings(this, FSystemSettings);
   FSystemSettings = NULL;
@@ -2390,6 +2392,7 @@ void __fastcall TLoginDialog::SessionTreeProc(TMessage & AMessage)
         {
           SessionTree->DropTarget = DropTarget;
         }
+        FScrollOnDragOver->DragOver(P);
       }
       else
       {
@@ -2419,6 +2422,8 @@ void __fastcall TLoginDialog::SessionTreeStartDrag(TObject * /*Sender*/,
   {
     Abort();
   }
+
+  FScrollOnDragOver->StartDrag();
 }
 //---------------------------------------------------------------------------
 void __fastcall TLoginDialog::SessionTreeDragDrop(TObject * Sender,
@@ -2517,3 +2522,9 @@ void __fastcall TLoginDialog::SessionTreeMouseMove(TObject * /*Sender*/,
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TLoginDialog::SessionTreeEndDrag(TObject * /*Sender*/,
+  TObject * /*Target*/, int /*X*/, int /*Y*/)
+{
+  FScrollOnDragOver->EndDrag();
+}
+//---------------------------------------------------------------------------

+ 1 - 0
forms/Login.dfm

@@ -342,6 +342,7 @@ object LoginDialog: TLoginDialog
           OnDragDrop = SessionTreeDragDrop
           OnEdited = SessionTreeEdited
           OnEditing = SessionTreeEditing
+          OnEndDrag = SessionTreeEndDrag
           OnExpanded = SessionTreeExpandedCollapsed
           OnKeyDown = SessionTreeKeyDown
           OnMouseMove = SessionTreeMouseMove

+ 4 - 0
forms/Login.h

@@ -23,6 +23,7 @@
 //----------------------------------------------------------------------------
 #include <Configuration.h>
 #include <SessionData.h>
+#include <PasTools.hpp>
 
 #include "LogSettings.h"
 #include "GeneralSettings.h"
@@ -357,6 +358,8 @@ __published:
           int X, int Y);
   void __fastcall SessionTreeMouseMove(TObject *Sender, TShiftState Shift,
           int X, int Y);
+  void __fastcall SessionTreeEndDrag(TObject *Sender, TObject *Target,
+          int X, int Y);
 
 private:
   int NoUpdate;
@@ -380,6 +383,7 @@ private:
   bool FRecycleBinSheetVisible;
   TWndMethod FOldSessionTreeProc;
   TTreeNode * FHintNode;
+  TTreeViewScrollOnDragOver * FScrollOnDragOver;
 
   void __fastcall LoadSession(TSessionData * aSessionData);
   void __fastcall UpdateControls();

+ 14 - 1
forms/OpenDirectory.cpp

@@ -54,6 +54,7 @@ __fastcall TOpenDirectoryDialog::TOpenDirectoryDialog(TComponent * AOwner):
   FBookmarkDragDest = -1;
   FTerminal = NULL;
   FBookmarkList = new TBookmarkList();
+  FScrollOnDragOver = new TListBoxScrollOnDragOver(BookmarksList, true);
 
   InstallPathWordBreakProc(LocalDirectoryEdit);
   InstallPathWordBreakProc(RemoteDirectoryEdit);
@@ -61,6 +62,7 @@ __fastcall TOpenDirectoryDialog::TOpenDirectoryDialog(TComponent * AOwner):
 //---------------------------------------------------------------------
 __fastcall TOpenDirectoryDialog::~TOpenDirectoryDialog()
 {
+  SAFE_DESTROY(FScrollOnDragOver);
   SAFE_DESTROY(FBookmarkList);
 }
 //---------------------------------------------------------------------
@@ -338,13 +340,18 @@ void __fastcall TOpenDirectoryDialog::BookmarksListStartDrag(
 {
   FBookmarkDragSource = BookmarksList->ItemIndex;
   FBookmarkDragDest = -1;
+  FScrollOnDragOver->StartDrag();
 }
 //---------------------------------------------------------------------------
 void __fastcall TOpenDirectoryDialog::BookmarksListDragOver(
       TObject */*Sender*/, TObject *Source, int X, int Y, TDragState /*State*/,
       bool &Accept)
 {
-  if (Source == BookmarksList) Accept = AllowBookmarkDrag(X, Y);
+  if (Source == BookmarksList)
+  {
+    Accept = AllowBookmarkDrag(X, Y);
+    FScrollOnDragOver->DragOver(TPoint(X, Y));
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TOpenDirectoryDialog::BookmarksListDragDrop(
@@ -418,3 +425,9 @@ void __fastcall TOpenDirectoryDialog::HelpButtonClick(TObject * /*Sender*/)
   FormHelp(this);
 }
 //---------------------------------------------------------------------------
+void __fastcall TOpenDirectoryDialog::BookmarksListEndDrag(TObject * /*Sender*/,
+  TObject * /*Target*/, int /*X*/, int /*Y*/)
+{
+  FScrollOnDragOver->EndDrag();
+}
+//---------------------------------------------------------------------------

+ 1 - 0
forms/OpenDirectory.dfm

@@ -102,6 +102,7 @@ object OpenDirectoryDialog: TOpenDirectoryDialog
       OnDblClick = BookmarksListDblClick
       OnDragDrop = BookmarksListDragDrop
       OnDragOver = BookmarksListDragOver
+      OnEndDrag = BookmarksListEndDrag
       OnKeyDown = BookmarksListKeyDown
       OnStartDrag = BookmarksListStartDrag
     end

+ 4 - 0
forms/OpenDirectory.h

@@ -20,6 +20,7 @@
 #include <WinInterface.h>
 #include <Bookmarks.h>
 #include "IEComboBox.hpp"
+#include <PasTools.hpp>
 //----------------------------------------------------------------------------
 class TOpenDirectoryDialog : public TForm
 {
@@ -58,6 +59,8 @@ __published:
   void __fastcall LocalDirectoryBrowseButtonClick(TObject *Sender);
   void __fastcall SwitchButtonClick(TObject *Sender);
   void __fastcall HelpButtonClick(TObject *Sender);
+  void __fastcall BookmarksListEndDrag(TObject *Sender, TObject *Target,
+          int X, int Y);
 
 public:
   __fastcall TOpenDirectoryDialog(TComponent* AOwner);
@@ -87,6 +90,7 @@ private:
   TOpenDirectoryMode FMode;
   TBookmarkList * FBookmarkList;
   bool FAllowSwitch;
+  TListBoxScrollOnDragOver * FScrollOnDragOver;
 
   void __fastcall SetDirectory(AnsiString value);
   AnsiString __fastcall GetDirectory();

+ 33 - 2
forms/Preferences.cpp

@@ -68,6 +68,9 @@ __fastcall TPreferencesDialog::TPreferencesDialog(TComponent* AOwner)
   FEditorList = new TEditorList();
   UseSystemSettings(this);
 
+  FCustomCommandsScrollOnDragOver = new TListViewScrollOnDragOver(CustomCommandsView, true);
+  FCopyParamScrollOnDragOver = new TListViewScrollOnDragOver(CopyParamListView, true);
+
   LoggingFrame->Init();
   InstallPathWordBreakProc(RandomSeedFileEdit);
   InstallPathWordBreakProc(DDTemporaryDirectoryEdit);
@@ -77,6 +80,8 @@ __fastcall TPreferencesDialog::TPreferencesDialog(TComponent* AOwner)
 //---------------------------------------------------------------------------
 __fastcall TPreferencesDialog::~TPreferencesDialog()
 {
+  SAFE_DESTROY(FCopyParamScrollOnDragOver);
+  SAFE_DESTROY(FCustomCommandsScrollOnDragOver);
   delete FEditorFont;
   delete FCustomCommands;
   delete FCopyParamList;
@@ -956,11 +961,29 @@ void __fastcall TPreferencesDialog::UpDownCommandButtonClick(TObject * Sender)
     CustomCommandsView->ItemIndex + (Sender == UpCommandButton ? -1 : 1));
 }
 //---------------------------------------------------------------------------
+TListViewScrollOnDragOver * __fastcall TPreferencesDialog::ScrollOnDragOver(TObject * ListView)
+{
+  if (ListView == CopyParamListView)
+  {
+    return FCopyParamScrollOnDragOver;
+  }
+  else if (ListView == CustomCommandsView)
+  {
+    return FCustomCommandsScrollOnDragOver;
+  }
+  else
+  {
+    assert(false);
+    return NULL;
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TPreferencesDialog::ListViewStartDrag(
       TObject * Sender, TDragObject *& /*DragObject*/)
 {
   FListViewDragSource = dynamic_cast<TListView*>(Sender)->ItemIndex;
   FListViewDragDest = -1;
+  ScrollOnDragOver(Sender)->StartDrag();
 }
 //---------------------------------------------------------------------------
 bool __fastcall TPreferencesDialog::AllowListViewDrag(TObject * Sender, int X, int Y)
@@ -983,8 +1006,8 @@ void __fastcall TPreferencesDialog::CustomCommandsViewDragDrop(
 }
 //---------------------------------------------------------------------------
 void __fastcall TPreferencesDialog::ListViewDragOver(
-      TObject * Sender, TObject * Source, int /*X*/, int /*Y*/,
-      TDragState /*State*/, bool & Accept)
+  TObject * Sender, TObject * Source, int X, int Y,
+  TDragState /*State*/, bool & Accept)
 {
   if (Source == Sender)
   {
@@ -992,6 +1015,8 @@ void __fastcall TPreferencesDialog::ListViewDragOver(
     // (when dropped on item itself, when it was dragged over another item before,
     // that another item remains highlighted forever)
     Accept = true;
+
+    ScrollOnDragOver(Source)->DragOver(TPoint(X, Y));
   }
 }
 //---------------------------------------------------------------------------
@@ -1454,3 +1479,9 @@ void __fastcall TPreferencesDialog::NavigationTreeCollapsing(
   AllowCollapse = false;
 }
 //---------------------------------------------------------------------------
+void __fastcall TPreferencesDialog::ListViewEndDrag(
+  TObject * Sender, TObject * /*Target*/, int /*X*/, int /*Y*/)
+{
+  ScrollOnDragOver(Sender)->EndDrag();
+}
+//---------------------------------------------------------------------------

+ 2 - 0
forms/Preferences.dfm

@@ -1102,6 +1102,7 @@ object PreferencesDialog: TPreferencesDialog
             ViewStyle = vsReport
             OnData = CustomCommandsViewData
             OnDblClick = CustomCommandsViewDblClick
+            OnEndDrag = ListViewEndDrag
             OnDragDrop = CustomCommandsViewDragDrop
             OnDragOver = ListViewDragOver
             OnKeyDown = CustomCommandsViewKeyDown
@@ -1828,6 +1829,7 @@ object PreferencesDialog: TPreferencesDialog
             ViewStyle = vsReport
             OnData = CopyParamListViewData
             OnDblClick = CopyParamListViewDblClick
+            OnEndDrag = ListViewEndDrag
             OnDragDrop = CopyParamListViewDragDrop
             OnDragOver = ListViewDragOver
             OnInfoTip = CopyParamListViewInfoTip

+ 6 - 0
forms/Preferences.h

@@ -25,6 +25,7 @@
 #include "HistoryComboBox.hpp"
 #include "PasswordEdit.hpp"
 #include <Dialogs.hpp>
+#include <PasTools.hpp>
 //----------------------------------------------------------------------------
 class TCustomCommands;
 class TEditorList;
@@ -274,6 +275,8 @@ __published:
           AnsiString &Name, bool &Action);
   void __fastcall NavigationTreeCollapsing(TObject *Sender,
           TTreeNode *Node, bool &AllowCollapse);
+  void __fastcall ListViewEndDrag(TObject *Sender,
+          TObject *Target, int X, int Y);
 private:
   TPreferencesMode FPreferencesMode;
   TFont * FEditorFont;
@@ -286,6 +289,8 @@ private:
   int FListViewDragDest;
   TPreferencesDialogData * FDialogData;
   AnsiString FBeforeDialogPath;
+  TListViewScrollOnDragOver * FCustomCommandsScrollOnDragOver;
+  TListViewScrollOnDragOver * FCopyParamScrollOnDragOver;
   bool FNoUpdate;
   void __fastcall SetPreferencesMode(TPreferencesMode value);
   void __fastcall CMDialogKey(TWMKeyDown & Message);
@@ -309,6 +314,7 @@ protected:
   bool __fastcall AllowListViewDrag(TObject * Sender, int X, int Y);
   void __fastcall PrepareNavigationTree(TTreeView * Tree);
   virtual void __fastcall Dispatch(void * Message);
+  TListViewScrollOnDragOver * __fastcall ScrollOnDragOver(TObject * ListView);
 };
 //----------------------------------------------------------------------------
 #endif

+ 1 - 0
packages/filemng/BaseUtils.hpp

@@ -52,6 +52,7 @@ extern PACKAGE void __fastcall ReduceDateTimePrecision(System::TDateTime &DateTi
 extern PACKAGE bool __fastcall SpecialFolderLocation(int Folder, AnsiString &Path, Shlobj::PItemIDList &PIDL)/* overload */;
 extern PACKAGE bool __fastcall SpecialFolderLocation(int Folder, AnsiString &Path)/* overload */;
 extern PACKAGE Controls::TImageList* __fastcall ShellImageList(Classes::TComponent* Owner, unsigned Flags);
+extern PACKAGE AnsiString __fastcall FormatLastOSError(AnsiString Message);
 
 }	/* namespace Baseutils */
 using namespace Baseutils;

+ 13 - 1
packages/filemng/BaseUtils.pas

@@ -54,6 +54,8 @@ function SpecialFolderLocation(Folder: Integer; var Path: string;
 function SpecialFolderLocation(Folder: Integer; var Path: string): Boolean; overload;
 function ShellImageList(Owner: TComponent; Flags: UINT): TImageList;
 
+function FormatLastOSError(Message: string): string;
+
 resourcestring
   SNoValidPath = 'Can''t find any valid path.';
   SUcpPathsNotSupported = 'UNC paths are not supported.';
@@ -61,7 +63,7 @@ resourcestring
 implementation
 
 uses
-  IEDriveInfo, DateUtils, ShellApi;
+  IEDriveInfo, DateUtils, ShellApi, SysConst;
 
 var
   GetDiskFreeSpaceEx: function (Directory: PChar;
@@ -350,6 +352,16 @@ begin
   Result.ShareImages := True;
 end;
 
+function FormatLastOSError(Message: string): string;
+var
+  LastError: Integer;
+begin
+  Result := Message;
+  LastError := GetLastError;
+  if LastError <> 0 then
+    Result := Result + #13#10 + #13#10 + Format(SOSError, [LastError, SysErrorMessage(LastError)]);
+end;
+
 initialization
   InitDriveSpacePtr;
 end.

+ 4 - 2
packages/filemng/CustomDirView.hpp

@@ -11,6 +11,7 @@
 #pragma option push -w-
 #pragma option push -Vx
 #include <NortonLikeListView.hpp>	// Pascal unit
+#include <PasTools.hpp>	// Pascal unit
 #include <SysUtils.hpp>	// Pascal unit
 #include <PathLabel.hpp>	// Pascal unit
 #include <IEListView.hpp>	// Pascal unit
@@ -203,8 +204,6 @@ private:
 	bool FForceRename;
 	Dragdrop::TDragResult FLastDDResult;
 	AnsiString FLastRenameName;
-	_FILETIME FLastVScrollTime;
-	int FVScrollCount;
 	bool FContextMenu;
 	bool FDragEnabled;
 	#pragma pack(push, 1)
@@ -243,6 +242,7 @@ private:
 	TMatchMaskEvent FOnMatchMask;
 	TDirViewGetOverlayEvent FOnGetOverlay;
 	AnsiString FMask;
+	Pastools::TListViewScrollOnDragOver* FScrollOnDragOver;
 	HIDESBASE MESSAGE void __fastcall CNNotify(Messages::TWMNotify &Message);
 	HIDESBASE MESSAGE void __fastcall WMLButtonDblClk(Messages::TWMMouse &Message);
 	HIDESBASE MESSAGE void __fastcall WMLButtonUp(Messages::TWMMouse &Message);
@@ -364,6 +364,8 @@ protected:
 	virtual bool __fastcall EnableDragOnClick(void);
 	virtual void __fastcall SetMask(AnsiString Value);
 	DYNAMIC AnsiString __fastcall NormalizeMask(AnsiString Mask);
+	void __fastcall ScrollOnDragOverBeforeUpdate(System::TObject* ObjectToValidate);
+	void __fastcall ScrollOnDragOverAfterUpdate(void);
 	__property Controls::TImageList* ImageList16 = {read=FImageList16};
 	__property Controls::TImageList* ImageList32 = {read=FImageList32};
 	

+ 23 - 40
packages/filemng/CustomDirView.pas

@@ -11,7 +11,7 @@ uses
   Forms, ComCtrls, ShellAPI, ComObj, ShlObj, Dialogs,
   ActiveX, CommCtrl, Extctrls, ImgList, Menus,
   PIDL, BaseUtils, DragDrop, DragDropFilesEx, IEDriveInfo,
-  IEListView, PathLabel, SysUtils;
+  IEListView, PathLabel, SysUtils, PasTools;
 
 const
   clDefaultItemColor = -(COLOR_ENDCOLORS + 1);
@@ -168,8 +168,6 @@ type
     FForceRename: Boolean;
     FLastDDResult: TDragResult;
     FLastRenameName: string;
-    FLastVScrollTime: TFileTime;
-    FVScrollCount: Integer;
     FContextMenu: Boolean;
     FDragEnabled: Boolean;
     FDragPos: TPoint;
@@ -202,6 +200,7 @@ type
     FOnMatchMask: TMatchMaskEvent;
     FOnGetOverlay: TDirViewGetOverlayEvent;
     FMask: string;
+    FScrollOnDragOver: TListViewScrollOnDragOver;
 
     procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
     procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
@@ -339,6 +338,8 @@ type
     function EnableDragOnClick: Boolean; override;
     procedure SetMask(Value: string); virtual;
     function NormalizeMask(Mask: string): string; dynamic;
+    procedure ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);
+    procedure ScrollOnDragOverAfterUpdate;
     property ImageList16: TImageList read FImageList16;
     property ImageList32: TImageList read FImageList32;
   public
@@ -944,6 +945,10 @@ begin
     OnProcessDropped := DDProcessDropped;
     OnDragDetect := DDDragDetect;
   end;
+
+  FScrollOnDragOver := TListViewScrollOnDragOver.Create(Self, False);
+  FScrollOnDragOver.OnBeforeUpdate := ScrollOnDragOverBeforeUpdate;
+  FScrollOnDragOver.OnAfterUpdate := ScrollOnDragOverAfterUpdate;
 end;
 
 procedure TCustomDirView.ClearItems;
@@ -1240,6 +1245,7 @@ destructor TCustomDirView.Destroy;
 begin
   Assert(not FSavedSelection);
 
+  FreeAndNil(FScrollOnDragOver);
   FreeAndNil(FSavedNames);
   FreeAndNil(FHistoryPaths);
 
@@ -1922,6 +1928,16 @@ begin
   Reload(CacheIcons);
 end;
 
+procedure TCustomDirView.ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);
+begin
+  GlobalDragImageList.HideDragImage;
+end;
+
+procedure TCustomDirView.ScrollOnDragOverAfterUpdate;
+begin
+  GlobalDragImageList.ShowDragImage;
+end;
+
 procedure TCustomDirView.DDDragEnter(DataObj: IDataObject; grfKeyState: Longint;
   Point: TPoint; var dwEffect: longint; var Accept: Boolean);
 var
@@ -1953,8 +1969,7 @@ begin
     Accept := False;
   end;
 
-  GetSystemTimeAsFileTime(FLastVScrollTime);
-  FVScrollCount := 0;
+  FScrollOnDragOver.StartDrag;
 
   if Assigned(FOnDDDragEnter) then
     FOnDDDragEnter(Self, DataObj, grfKeyState, Point, dwEffect, Accept);
@@ -1979,11 +1994,8 @@ procedure TCustomDirView.DDDragOver(grfKeyState: Integer; Point: TPoint;
   var dwEffect: Integer);
 var
   DropItem: TListItem;
-  KnowTime: TFileTime;
-  NbPixels: Integer;
   CanDrop: Boolean;
   HasDropHandler: Boolean;
-  WParam: LongInt;
 begin
   FDDOwnerIsSource := DragDropFilesEx.OwnerIsSource;
 
@@ -2018,37 +2030,8 @@ begin
     end;
   end;
 
-  GetSystemTimeAsFileTime(KnowTime);
-  NbPixels := Abs((Font.Height));
-  {Vertical scrolling, if viewstyle = vsReport:}
-  if (ViewStyle = vsReport) and (not Loading) and Assigned(TopItem) and
-     (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
-      ((FVScrollCount > DDMaxSlowCount) and
-        ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
-  begin
-    if ((DropItem = TopItem) or (Point.Y - 3 * nbPixels <= 0)) and
-       (TopItem.Index > 0) then WParam := SB_LINEUP
-      else
-    if (Point.Y + 3 * nbPixels > Height) then WParam := SB_LINEDOWN
-      else WParam := -1;
-    if WParam >= 0 then
-    begin
-      if GlobalDragImageList.Dragging then
-        GlobalDragImageList.HideDragImage;
-      Perform(WM_VSCROLL, WParam, 0);
-      if FVScrollCount > DDMaxSlowCount then
-          Perform(WM_VSCROLL, WParam, 0);
-      if FVScrollCount > DDMaxSlowCount * 3 then
-        Perform(WM_VSCROLL, WParam, 0);
-      Update;
-      if GlobalDragImageList.Dragging then
-        GlobalDragImageList.ShowDragImage;
-
-      GetSystemTimeAsFileTime(FLastVScrollTime);
-      Inc(FVScrollCount);
-    end
-      else FVScrollCount := 0;
-  end; {VScrollDelay}
+  if not Loading then
+    FScrollOnDragOver.DragOver(Point);
 
   {Set dropeffect:}
   if (not HasDropHandler) and (not Loading) then
@@ -2284,7 +2267,7 @@ begin
   if OnlyFocused or (SelCount = 0) then
   begin
     Result := Assigned(ItemFocused) and ItemIsFile(ItemFocused) and
-      ((not FilesOnly) or (not ItemIsDirectory(ItemFocused)))
+      ((not FilesOnly) or (not ItemIsDirectory(ItemFocused)));
   end
     else
   begin

+ 4 - 4
packages/filemng/CustomDriveView.hpp

@@ -10,6 +10,7 @@
 #pragma delphiheader begin
 #pragma option push -w-
 #pragma option push -Vx
+#include <PasTools.hpp>	// Pascal unit
 #include <DragDropFilesEx.hpp>	// Pascal unit
 #include <IEDriveInfo.hpp>	// Pascal unit
 #include <CustomDirView.hpp>	// Pascal unit
@@ -55,10 +56,6 @@ protected:
 	char FDragDrive;
 	bool FExeDrag;
 	bool FDDLinkOnExeDrag;
-	_FILETIME FDragOverTime;
-	_FILETIME FLastVScrollTime;
-	_FILETIME FLastHScrollTime;
-	int FVScrollCount;
 	Comctrls::TTreeNode* FDragNode;
 	_FILETIME FDragStartTime;
 	#pragma pack(push, 1)
@@ -76,6 +73,7 @@ protected:
 	bool FShowHiddenDirs;
 	bool FContinue;
 	Controls::TImageList* FImageList;
+	Pastools::TTreeViewScrollOnDragOver* FScrollOnDragOver;
 	Customdirview::TDDOnDragEnter FOnDDDragEnter;
 	Customdirview::TDDOnDragLeave FOnDDDragLeave;
 	Customdirview::TDDOnDragOver FOnDDDragOver;
@@ -151,6 +149,8 @@ protected:
 	virtual void __fastcall RebuildTree(void) = 0 ;
 	virtual void __fastcall DisplayContextMenu(Comctrls::TTreeNode* Node, const Types::TPoint &ScreenPos) = 0 ;
 	virtual void __fastcall DisplayPropertiesMenu(Comctrls::TTreeNode* Node) = 0 ;
+	void __fastcall ScrollOnDragOverBeforeUpdate(System::TObject* ObjectToValidate);
+	void __fastcall ScrollOnDragOverAfterUpdate(void);
 	__property Controls::TImageList* ImageList = {read=FImageList};
 	
 public:

+ 30 - 135
packages/filemng/CustomDriveView.pas

@@ -5,7 +5,7 @@ interface
 uses
   Classes, ComCtrls, CommCtrl, Windows, Controls, Forms, ShlObj, Messages,
     Graphics,
-  DragDrop, CustomDirView, IEDriveInfo, DragDropFilesEx;
+  DragDrop, CustomDirView, IEDriveInfo, DragDropFilesEx, PasTools;
 
 type
   {Types uses by the function IterateSubTree:}
@@ -32,10 +32,6 @@ type
     FDragDrive: TDrive;
     FExeDrag: Boolean;
     FDDLinkOnExeDrag: Boolean;
-    FDragOverTime: FILETIME;
-    FLastVScrollTime: FILETIME;
-    FLastHScrollTime: FILETIME;
-    FVScrollCount: Integer;
     FDragNode: TTreeNode;
     FDragStartTime: FILETIME;
     FDragPos: TPoint;
@@ -47,6 +43,7 @@ type
     FShowHiddenDirs: Boolean;
     FContinue: Boolean;
     FImageList: TImageList;
+    FScrollOnDragOver: TTreeViewScrollOnDragOver;
 
     FOnDDDragEnter: TDDOnDragEnter;
     FOnDDDragLeave: TDDOnDragLeave;
@@ -147,6 +144,9 @@ type
     procedure DisplayContextMenu(Node: TTreeNode; ScreenPos: TPoint); virtual; abstract;
     procedure DisplayPropertiesMenu(Node: TTreeNode); virtual; abstract;
 
+    procedure ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);
+    procedure ScrollOnDragOverAfterUpdate;
+
     property ImageList: TImageList read FImageList;
 
   public
@@ -227,9 +227,6 @@ uses
   SysUtils, ShellApi, ImgList,
   IEListView, BaseUtils;
 
-const
-  DDExpandDelay = 25000000;
-
 constructor TCustomDriveView.Create(AOwner: TComponent);
 var
   WinVer: TOSVersionInfo;
@@ -282,10 +279,15 @@ begin
   end;
 
   OnCustomDrawItem := InternalOnDrawItem;
+
+  FScrollOnDragOver := TTreeViewScrollOnDragOver.Create(Self, False);
+  FScrollOnDragOver.OnBeforeUpdate := ScrollOnDragOverBeforeUpdate;
+  FScrollOnDragOver.OnAfterUpdate := ScrollOnDragOverAfterUpdate;
 end;
 
 destructor TCustomDriveView.Destroy;
 begin
+  FreeAndNil(FScrollOnDragOver);
   FreeAndNil(FImageList);
 
   if Assigned(Images) then
@@ -362,10 +364,27 @@ begin
   end;
 end; {InternalOnDrawItem}
 
+procedure TCustomDriveView.ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);
+var
+  NodeToValidate: TTreeNode;
+begin
+  GlobalDragImageList.HideDragImage;
+  if Assigned(ObjectToValidate) then
+  begin
+    NodeToValidate := (ObjectToValidate as TTreeNode);
+    if not NodeToValidate.HasChildren then
+      ValidateDirectory(NodeToValidate);
+  end;
+end;
+
+procedure TCustomDriveView.ScrollOnDragOverAfterUpdate;
+begin
+  GlobalDragImageList.ShowDragImage;
+end;
+
 procedure TCustomDriveView.DDDragEnter(DataObj: IDataObject; KeyState: Longint;
   Point: TPoint; var Effect: Longint; var Accept: Boolean);
 var
-  KeyBoardState : TKeyBoardState;
   i: Integer;
 begin
   if (FDragDropFilesEx.FileList.Count > 0) and
@@ -388,16 +407,7 @@ begin
     FDragDrive := #0;
   end;
 
-  GetSystemTimeAsFileTime(FDragOverTime);
-  GetSystemTimeAsFileTime(FLastHScrollTime);
-  GetSystemTimeAsFileTime(FLastVScrollTime);
-  FVScrollCount := 0;
-
-  if (GetKeyState(VK_SPACE) <> 0) And GetKeyboardState(KeyBoardState) then
-  begin
-    KeyBoardState[VK_SPACE] := 0;
-    SetKeyBoardState(KeyBoardState);
-  end;
+  FScrollOnDragOver.StartDrag;
 
   if Assigned(FOnDDDragEnter) then
     FOnDDDragEnter(Self, DataObj, KeyState, Point, Effect, Accept);
@@ -420,11 +430,6 @@ end; {DragLeave}
 procedure TCustomDriveView.DDDragOver(KeyState: Longint; Point: TPoint; var Effect: Longint);
 var
   Node: TTreeNode;
-  KnowTime: FILETIME;
-  TempTopItem: TTreeNode;
-  NbPixels: Integer;
-  ScrollInfo: TScrollInfo;
-  KeyBoardState: TKeyBoardState;
   Rect1: TRect;
   UpdateImage: Boolean;
   LastDragNode: TTreeNode;
@@ -475,117 +480,7 @@ begin
          ((Node = FDragNode) or Node.HasAsParent(FDragNode) or (FDragNode.Parent = Node)) then
          Effect := DropEffect_None;
 
-      GetSystemTimeAsFileTime(KnowTime);
-
-      if GetKeyState(VK_SPACE) = 0 then
-      begin
-        {Expand node after 2.5 seconds: }
-        if not Assigned(LastDragNode) or (LastDragNode <> Node) then
-            GetSystemTimeAsFileTime(FDragOverTime)     {not previous droptarget: start timer}
-          else
-        begin
-          if ((Int64(KnowTime) - Int64(FDragOverTime)) > DDExpandDelay) then
-          begin
-            TempTopItem := TopItem;
-            GlobalDragImageList.HideDragImage;
-            Node.Expand(False);
-            TopItem := TempTopItem;
-            Update;
-            GlobalDragImageList.ShowDragImage;
-            FDragOverTime := KnowTime;
-          end;
-        end;
-      end
-        else
-      begin
-        {restart timer}
-        GetSystemTimeAsFileTime(FDragOverTime);
-        if GetKeyboardState(KeyBoardState) then
-        begin
-          KeyBoardState[VK_Space] := 0;
-          SetKeyBoardState(KeyBoardState);
-        end;
-
-        TempTopItem := TopItem;
-        GlobalDragImageList.HideDragImage;
-        if not Node.HasChildren then
-          ValidateDirectory(Node);
-        if Node.Expanded then
-        begin
-          if not Selected.HasAsParent(Node) then
-            Node.Collapse(False);
-        end
-          else Node.Expand(False);
-        TopItem := TempTopItem;
-        Update;
-        GlobalDragImageList.ShowDragImage;
-      end;
-
-      NbPixels := Abs((Font.Height));
-
-      {Vertical treescrolling:}
-      if ((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
-         ((FVScrollCount > 3) and
-          ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay Div 4))) then
-      begin
-        {Scroll tree up, if droptarget is topitem:}
-        if Node = TopItem then
-        begin
-          GlobalDragImageList.HideDragImage;
-          Perform(WM_VSCROLL, SB_LINEUP, 0);
-          GlobalDragImageList.ShowDragImage;
-          GetSystemTimeAsFileTime(FLastVScrollTime);
-          Inc(FVScrollCount);
-        end
-          else
-        {Scroll tree down, if next visible item of droptarget is not visible:}
-        begin
-          if Point.Y + 3 * nbPixels > Height then
-          begin
-            GlobalDragImageList.HideDragImage;
-            Perform(WM_VSCROLL, SB_LINEDOWN, 0);
-            GlobalDragImageList.ShowDragImage;
-            GetSystemTimeAsFileTime(FLastVScrollTime);
-            Inc(FVScrollCount);
-          end
-            else
-          begin
-            FVScrollCount := 0;
-          end;
-        end;
-      end; {VScrollDelay}
-
-      {Horizontal treescrolling:}
-      {Scroll tree Left}
-      if ((Int64(KnowTime) - Int64(FLastHScrollTime)) > DDHScrollDelay) then
-      begin
-        GetSystemTimeAsFileTime(FLastHScrollTime);
-        ScrollInfo.cbSize := SizeOf(ScrollInfo);
-        ScrollInfo.FMask := SIF_ALL;
-        GetScrollInfo(Handle, SB_HORZ, ScrollInfo);
-        if ScrollInfo.nMin <> ScrollInfo.nMax then
-        begin
-          if Point.X < 50 then
-          begin
-            if Node.DisplayRect(True).Right + 50 < Width then
-            begin
-              GlobalDragImageList.HideDragImage;
-              Perform(WM_HSCROLL, SB_LINELEFT, 0);
-              GlobalDragImageList.ShowDragImage;
-            end;
-          end
-            else
-          if Point.X > (Width - 50) then
-          begin
-            if Node.DisplayRect(True).Left > 50 then
-            begin
-              GlobalDragImageList.HideDragImage;
-              Perform(WM_HSCROLL, SB_LINERIGHT, 0);
-              GlobalDragImageList.ShowDragImage;
-            end;
-          end;
-        end;
-      end;
+      FScrollOnDragOver.DragOver(Point);
     end {Assigned(Node)}
       else
     begin

+ 3 - 2
packages/filemng/DirView.pas

@@ -1794,7 +1794,8 @@ begin
                   Rect(ViewOrigin, Point(ViewOrigin.X + ClientWidth, ViewOrigin.Y + ClientHeight)),
                   ItemFocused.DisplayRect(drBounds));
           end;
-        end;
+        end
+          else FocusedIsVisible := False; // shut up
 
         SaveCursor := Screen.Cursor;
         Screen.Cursor := crHourGlass;
@@ -3701,7 +3702,7 @@ begin
         Info := SErrorRenameFile + HItem.pszText;
 
       MessageBeep(MB_ICONHAND);
-      if MessageDlg(Info, mtError, [mbOK, mbAbort], 0) = mrOK then
+      if MessageDlg(FormatLastOSError(Info), mtError, [mbOK, mbAbort], 0) = mrOK then
         RetryRename(HItem.pszText);
     end;
   finally

+ 0 - 4
packages/filemng/DriveView.hpp

@@ -555,10 +555,6 @@ extern PACKAGE System::ResourceString _Space;
 #define Driveview_Space System::LoadResourceString(&Driveview::_Space)
 static const Shortint msThreadChangeDelay = 0x32;
 static const unsigned CInvalidSize = 0xffffffff;
-static const Shortint DDMaxSlowCount = 0x3;
-static const int DDVScrollDelay = 0x1e8480;
-static const int DDHScrollDelay = 0x1e8480;
-static const int DDDragStartDelay = 0x7a120;
 #define ErrorNodeNA "%s: Node not assigned"
 static const Shortint dvdsFloppy = 0x8;
 static const Shortint dvdsRereadAllways = 0x10;

+ 1 - 7
packages/filemng/DriveView.pas

@@ -56,12 +56,6 @@ const
 {$ENDIF}
   CInvalidSize = $FFFFFFFF;
 
-  DDMaxSlowCount = 3;
-
-  DDVScrollDelay   = 2000000;
-  DDHScrollDelay   = 2000000;
-  DDDragStartDelay = 500000;
-
   ErrorNodeNA = '%s: Node not assigned';
 
   {Flags used by TDriveView.RefreshRootNodes:}
@@ -826,7 +820,7 @@ begin
           Info := SErrorRenameFile + Item.pszText;
 
         MessageBeep(MB_ICONHAND);
-        if MessageDlg(Info, mtError, [mbOK, mbAbort], 0) = mrOK then
+        if MessageDlg(FormatLastOSError(Info), mtError, [mbOK, mbAbort], 0) = mrOK then
         begin
           FLastRenameName := Item.pszText;
           FRenameNode := Node;

+ 6 - 1
packages/my/NortonLikeListView.pas

@@ -163,6 +163,9 @@ procedure Register;
 
 implementation
 
+uses
+  PasTools;
+
 procedure Register;
 begin
   RegisterComponents('Martin', [TNortonLikeListView]);
@@ -189,7 +192,9 @@ begin
   // On Windows Vista, native GetNextItem for selection stops working once we
   // disallow deselecting any item (see ExCanChange).
   // So we need to manage selection state ourselves
-  FManageSelection := (Win32MajorVersion >= 6);
+  // cannot use Win32MajorVersion as it is affected by compatibility mode and
+  // the bug is present even in compatibility mode
+  FManageSelection := IsVista;
 end;
 
 destructor TCustomNortonLikeListView.Destroy;

+ 98 - 0
packages/my/PasTools.hpp

@@ -10,7 +10,12 @@
 #pragma delphiheader begin
 #pragma option push -w-
 #pragma option push -Vx
+#include <Controls.hpp>	// Pascal unit
+#include <ExtCtrls.hpp>	// Pascal unit
+#include <ComCtrls.hpp>	// Pascal unit
 #include <Classes.hpp>	// Pascal unit
+#include <Types.hpp>	// Pascal unit
+#include <Windows.hpp>	// Pascal unit
 #include <SysInit.hpp>	// Pascal unit
 #include <System.hpp>	// Pascal unit
 
@@ -19,8 +24,101 @@
 namespace Pastools
 {
 //-- type declarations -------------------------------------------------------
+typedef void __fastcall (__closure *TControlScrollBeforeUpdate)(System::TObject* ObjectToValidate);
+
+typedef void __fastcall (__closure *TControlScrollAfterUpdate)(void);
+
+class DELPHICLASS TCustomControlScrollOnDragOver;
+class PASCALIMPLEMENTATION TCustomControlScrollOnDragOver : public System::TObject 
+{
+	typedef System::TObject inherited;
+	
+private:
+	TControlScrollBeforeUpdate FOnBeforeUpdate;
+	TControlScrollAfterUpdate FOnAfterUpdate;
+	Extctrls::TTimer* FDragOverTimer;
+	Controls::TControl* FControl;
+	_FILETIME FDragOverTime;
+	_FILETIME FLastVScrollTime;
+	int FVScrollCount;
+	void __fastcall DragOverTimer(System::TObject* Sender);
+	void __fastcall BeforeUpdate(System::TObject* ObjectToValidate);
+	void __fastcall AfterUpdate(void);
+	
+public:
+	__fastcall TCustomControlScrollOnDragOver(Controls::TControl* Control, bool ScheduleDragOver);
+	__fastcall virtual ~TCustomControlScrollOnDragOver(void);
+	virtual void __fastcall StartDrag(void);
+	virtual void __fastcall EndDrag(void);
+	virtual void __fastcall DragOver(const Types::TPoint &Point) = 0 ;
+	__property TControlScrollBeforeUpdate OnBeforeUpdate = {read=FOnBeforeUpdate, write=FOnBeforeUpdate};
+	__property TControlScrollAfterUpdate OnAfterUpdate = {read=FOnAfterUpdate, write=FOnAfterUpdate};
+};
+
+
+class DELPHICLASS TTreeViewScrollOnDragOver;
+class PASCALIMPLEMENTATION TTreeViewScrollOnDragOver : public TCustomControlScrollOnDragOver 
+{
+	typedef TCustomControlScrollOnDragOver inherited;
+	
+private:
+	Comctrls::TTreeNode* FLastDragNode;
+	_FILETIME FLastHScrollTime;
+	
+public:
+	virtual void __fastcall StartDrag(void);
+	virtual void __fastcall DragOver(const Types::TPoint &Point);
+public:
+	#pragma option push -w-inl
+	/* TCustomControlScrollOnDragOver.Create */ inline __fastcall TTreeViewScrollOnDragOver(Controls::TControl* Control, bool ScheduleDragOver) : TCustomControlScrollOnDragOver(Control, ScheduleDragOver) { }
+	#pragma option pop
+	#pragma option push -w-inl
+	/* TCustomControlScrollOnDragOver.Destroy */ inline __fastcall virtual ~TTreeViewScrollOnDragOver(void) { }
+	#pragma option pop
+	
+};
+
+
+class DELPHICLASS TListViewScrollOnDragOver;
+class PASCALIMPLEMENTATION TListViewScrollOnDragOver : public TCustomControlScrollOnDragOver 
+{
+	typedef TCustomControlScrollOnDragOver inherited;
+	
+public:
+	virtual void __fastcall DragOver(const Types::TPoint &Point);
+public:
+	#pragma option push -w-inl
+	/* TCustomControlScrollOnDragOver.Create */ inline __fastcall TListViewScrollOnDragOver(Controls::TControl* Control, bool ScheduleDragOver) : TCustomControlScrollOnDragOver(Control, ScheduleDragOver) { }
+	#pragma option pop
+	#pragma option push -w-inl
+	/* TCustomControlScrollOnDragOver.Destroy */ inline __fastcall virtual ~TListViewScrollOnDragOver(void) { }
+	#pragma option pop
+	
+};
+
+
+class DELPHICLASS TListBoxScrollOnDragOver;
+class PASCALIMPLEMENTATION TListBoxScrollOnDragOver : public TCustomControlScrollOnDragOver 
+{
+	typedef TCustomControlScrollOnDragOver inherited;
+	
+public:
+	virtual void __fastcall DragOver(const Types::TPoint &Point);
+public:
+	#pragma option push -w-inl
+	/* TCustomControlScrollOnDragOver.Create */ inline __fastcall TListBoxScrollOnDragOver(Controls::TControl* Control, bool ScheduleDragOver) : TCustomControlScrollOnDragOver(Control, ScheduleDragOver) { }
+	#pragma option pop
+	#pragma option push -w-inl
+	/* TCustomControlScrollOnDragOver.Destroy */ inline __fastcall virtual ~TListBoxScrollOnDragOver(void) { }
+	#pragma option pop
+	
+};
+
+
 //-- var, const, procedure ---------------------------------------------------
+extern PACKAGE unsigned __fastcall GetTraceFile(void);
 extern PACKAGE Classes::TComponent* __fastcall Construct(TMetaClass* ComponentClass, Classes::TComponent* Owner);
+extern PACKAGE bool __fastcall IsVista(void);
 
 }	/* namespace Pastools */
 using namespace Pastools;

+ 373 - 1
packages/my/PasTools.pas

@@ -3,15 +3,387 @@ unit PasTools;
 interface
 
 uses
-  Classes;
+  Windows, Types, Classes, ComCtrls, ExtCtrls, Controls;
 
 function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;
 
+function IsVista: Boolean;
+
+type
+  TControlScrollBeforeUpdate = procedure(ObjectToValidate: TObject) of object;
+  TControlScrollAfterUpdate = procedure of object;
+
+  TCustomControlScrollOnDragOver = class
+  private
+    FOnBeforeUpdate: TControlScrollBeforeUpdate;
+    FOnAfterUpdate: TControlScrollAfterUpdate;
+    FDragOverTimer: TTimer;
+    FControl: TControl;
+    FDragOverTime: FILETIME;
+    FLastVScrollTime: FILETIME;
+    FVScrollCount: Integer;
+
+    procedure DragOverTimer(Sender: TObject);
+    procedure BeforeUpdate(ObjectToValidate: TObject);
+    procedure AfterUpdate;
+
+  public
+    constructor Create(Control: TControl; ScheduleDragOver: Boolean);
+    destructor Destroy; override;
+    procedure StartDrag; virtual;
+    procedure EndDrag; virtual;
+    procedure DragOver(Point: TPoint); virtual; abstract;
+
+    property OnBeforeUpdate: TControlScrollBeforeUpdate read FOnBeforeUpdate write FOnBeforeUpdate;
+    property OnAfterUpdate: TControlScrollAfterUpdate read FOnAfterUpdate write FOnAfterUpdate;
+  end;
+
+  TTreeViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)
+  private
+    FLastDragNode: TTreeNode;
+    FLastHScrollTime: FILETIME;
+  public
+    procedure StartDrag; override;
+    procedure DragOver(Point: TPoint); override;
+  end;
+
+  TListViewScrollOnDragOver = class(TCustomControlScrollOnDragOver)
+  public
+    procedure DragOver(Point: TPoint); override;
+  end;
+
+  TListBoxScrollOnDragOver = class(TCustomControlScrollOnDragOver)
+  public
+    procedure DragOver(Point: TPoint); override;
+  end;
+
 implementation
 
+uses
+  SysUtils, Messages, StdCtrls;
+
+const
+  DDExpandDelay = 15000000;
+  DDMaxSlowCount = 3;
+  DDVScrollDelay   = 2000000;
+  DDHScrollDelay   = 2000000;
+  DDDragStartDelay = 500000;
+
 function Construct(ComponentClass: TComponentClass; Owner: TComponent): TComponent;
 begin
   Result := ComponentClass.Create(Owner);
 end;
 
+// detects vista, even in compatibility mode
+// (GetLocaleInfoEx is available since Vista only)
+function IsVista: Boolean;
+begin
+  Result := (GetProcAddress(GetModuleHandle(Kernel32), 'GetLocaleInfoEx') <> nil);
+end;
+
+  { TCustomControlScrollOnDragOver }
+
+constructor TCustomControlScrollOnDragOver.Create(Control: TControl;
+  ScheduleDragOver: Boolean);
+begin
+  FControl := Control;
+  FOnBeforeUpdate := nil;
+  FOnAfterUpdate := nil;
+
+  if ScheduleDragOver then
+  begin
+    FDragOverTimer := TTimer.Create(Control);
+    FDragOverTimer.Enabled := False;
+    FDragOverTimer.Interval := 50;
+    FDragOverTimer.OnTimer := DragOverTimer;
+  end
+    else FDragOverTimer := nil;
+end;
+
+destructor TCustomControlScrollOnDragOver.Destroy;
+begin
+  FreeAndNil(FDragOverTimer);
+end;
+
+procedure TCustomControlScrollOnDragOver.DragOverTimer(Sender: TObject);
+var
+  P: TPoint;
+begin
+  P := FControl.ScreenToClient(Mouse.CursorPos);
+  if (P.X >= 0) and (P.X < FControl.Width) and
+     (P.Y >= 0) and (P.Y < FControl.Height) then
+  begin
+    DragOver(P);
+  end;
+end;
+
+procedure TCustomControlScrollOnDragOver.StartDrag;
+begin
+  GetSystemTimeAsFileTime(FDragOverTime);
+  GetSystemTimeAsFileTime(FLastVScrollTime);
+  FVScrollCount := 0;
+
+  if Assigned(FDragOverTimer) then
+    FDragOverTimer.Enabled := True;
+end;
+
+procedure TCustomControlScrollOnDragOver.EndDrag;
+begin
+  if Assigned(FDragOverTimer) then
+    FDragOverTimer.Enabled := False;
+end;
+
+type
+  TPublicControl = class(TControl);
+
+procedure TCustomControlScrollOnDragOver.BeforeUpdate(ObjectToValidate: TObject);
+var
+  DragImages: TDragImageList;
+begin
+  if Assigned(FOnBeforeUpdate) then
+    FOnBeforeUpdate(ObjectToValidate);
+  DragImages := TPublicControl(FControl).GetDragImages;
+  if Assigned(DragImages) then
+    DragImages.HideDragImage;
+end;
+
+procedure TCustomControlScrollOnDragOver.AfterUpdate;
+var
+  DragImages: TDragImageList;
+begin
+  if Assigned(FOnAfterUpdate) then
+    FOnAfterUpdate;
+  DragImages := TPublicControl(FControl).GetDragImages;
+  if Assigned(DragImages) then
+    DragImages.ShowDragImage;
+end;
+
+procedure TTreeViewScrollOnDragOver.StartDrag;
+var
+  KeyBoardState : TKeyBoardState;
+begin
+  inherited;
+
+  FLastDragNode := nil;
+
+  if (GetKeyState(VK_SPACE) <> 0) and GetKeyboardState(KeyBoardState) then
+  begin
+    KeyBoardState[VK_SPACE] := 0;
+    SetKeyBoardState(KeyBoardState);
+  end;
+
+  GetSystemTimeAsFileTime(FLastHScrollTime);
+end;
+
+  { TTreeViewScrollOnDragOver }
+
+procedure TTreeViewScrollOnDragOver.DragOver(Point: TPoint);
+var
+  TreeView: TCustomTreeView;
+  NbPixels: Integer;
+  KnowTime: TFileTime;
+  Node: TTreeNode;
+  TempTopItem: TTreeNode;
+  ScrollInfo: TScrollInfo;
+  KeyBoardState : TKeyBoardState;
+begin
+  TreeView := (FControl as TCustomTreeView);
+  Node := TreeView.GetNodeAt(Point.X, Point.Y);
+  if Assigned(Node) then
+  begin
+    GetSystemTimeAsFileTime(KnowTime);
+    if GetKeyState(VK_SPACE) = 0 then
+    begin
+      {Expand node after 2.5 seconds: }
+      if not Assigned(FLastDragNode) or (FLastDragNode <> Node) then
+      begin
+        {not previous droptarget: start timer}
+        GetSystemTimeAsFileTime(FDragOverTime);
+        FLastDragNode := Node
+      end
+        else
+      begin
+        if ((Int64(KnowTime) - Int64(FDragOverTime)) > DDExpandDelay) then
+        begin
+          TempTopItem := TreeView.TopItem;
+          BeforeUpdate(nil);
+          Node.Expand(False);
+          TreeView.TopItem := TempTopItem;
+          TreeView.Update;
+          AfterUpdate;
+          FDragOverTime := KnowTime;
+        end;
+      end;
+    end
+      else
+    begin
+      {restart timer}
+      GetSystemTimeAsFileTime(FDragOverTime);
+      if GetKeyboardState(KeyBoardState) then
+      begin
+        KeyBoardState[VK_Space] := 0;
+        SetKeyBoardState(KeyBoardState);
+      end;
+
+      TempTopItem := TreeView.TopItem;
+      BeforeUpdate(Node);
+      if Node.Expanded then
+      begin
+        if not TreeView.Selected.HasAsParent(Node) then
+          Node.Collapse(False);
+      end
+        else Node.Expand(False);
+      TreeView.TopItem := TempTopItem;
+      TreeView.Update;
+      AfterUpdate;
+    end;
+
+    NbPixels := Abs(TTreeView(FControl).Font.Height);
+
+    {Vertical treescrolling:}
+    if ((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
+       ((FVScrollCount > 3) and
+        ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay Div 4))) then
+    begin
+      {Scroll tree up, if droptarget is topitem:}
+      if Node = TreeView.TopItem then
+      begin
+        BeforeUpdate(nil);
+        TreeView.Perform(WM_VSCROLL, SB_LINEUP, 0);
+        AfterUpdate;
+        GetSystemTimeAsFileTime(FLastVScrollTime);
+        Inc(FVScrollCount);
+      end
+        else
+      {Scroll tree down, if next visible item of droptarget is not visible:}
+      begin
+        if Point.Y + 3 * nbPixels > TreeView.Height then
+        begin
+          BeforeUpdate(nil);
+          TreeView.Perform(WM_VSCROLL, SB_LINEDOWN, 0);
+          AfterUpdate;
+          GetSystemTimeAsFileTime(FLastVScrollTime);
+          Inc(FVScrollCount);
+        end
+          else
+        begin
+          FVScrollCount := 0;
+        end;
+      end;
+    end; {VScrollDelay}
+
+    {Horizontal treescrolling:}
+    {Scroll tree Left}
+    if ((Int64(KnowTime) - Int64(FLastHScrollTime)) > DDHScrollDelay) then
+    begin
+      GetSystemTimeAsFileTime(FLastHScrollTime);
+      ScrollInfo.cbSize := SizeOf(ScrollInfo);
+      ScrollInfo.FMask := SIF_ALL;
+      GetScrollInfo(TreeView.Handle, SB_HORZ, ScrollInfo);
+      if ScrollInfo.nMin <> ScrollInfo.nMax then
+      begin
+        if Point.X < 50 then
+        begin
+          if Node.DisplayRect(True).Right + 50 < TreeView.Width then
+          begin
+            BeforeUpdate(nil);
+            TreeView.Perform(WM_HSCROLL, SB_LINELEFT, 0);
+            AfterUpdate;
+          end;
+        end
+          else
+        if Point.X > (TreeView.Width - 50) then
+        begin
+          if Node.DisplayRect(True).Left > 50 then
+          begin
+            BeforeUpdate(nil);
+            TreeView.Perform(WM_HSCROLL, SB_LINERIGHT, 0);
+            AfterUpdate;
+          end;
+        end;
+      end;
+    end;
+  end;
+end;
+
+  { TListViewScrollOnDragOver }
+
+procedure TListViewScrollOnDragOver.DragOver(Point: TPoint);
+var
+  ListView: TCustomListView;
+  KnowTime: TFileTime;
+  NbPixels: Integer;
+  WParam: LongInt;
+begin
+  ListView := (FControl as TCustomListView);
+  GetSystemTimeAsFileTime(KnowTime);
+  NbPixels := Abs(TListView(ListView).Font.Height);
+  {Vertical scrolling, if viewstyle = vsReport:}
+  if (TListView(ListView).ViewStyle = vsReport) and Assigned(ListView.TopItem) and
+     (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
+      ((FVScrollCount > DDMaxSlowCount) and
+        ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
+  begin
+    if (Point.Y - 3 * nbPixels <= 0) and (ListView.TopItem.Index > 0) then WParam := SB_LINEUP
+      else
+    if (Point.Y + 3 * nbPixels > ListView.Height) then WParam := SB_LINEDOWN
+      else WParam := -1;
+    if WParam >= 0 then
+    begin
+      BeforeUpdate(nil);
+      ListView.Perform(WM_VSCROLL, WParam, 0);
+      if FVScrollCount > DDMaxSlowCount then
+        ListView.Perform(WM_VSCROLL, WParam, 0);
+      if FVScrollCount > DDMaxSlowCount * 3 then
+        ListView.Perform(WM_VSCROLL, WParam, 0);
+      ListView.Update;
+      AfterUpdate;
+
+      GetSystemTimeAsFileTime(FLastVScrollTime);
+      Inc(FVScrollCount);
+    end
+      else FVScrollCount := 0;
+  end;
+end;
+
+  { TListBoxScrollOnDragOver }
+
+procedure TListBoxScrollOnDragOver.DragOver(Point: TPoint);
+var
+  ListBox: TListBox;
+  KnowTime: TFileTime;
+  NbPixels: Integer;
+  WParam: LongInt;
+begin
+  ListBox := (FControl as TListBox);
+  GetSystemTimeAsFileTime(KnowTime);
+  NbPixels := Abs(ListBox.Font.Height);
+  {Vertical scrolling, if viewstyle = vsReport:}
+  if (ListBox.Items.Count > 0) and
+     (((Int64(KnowTime) - Int64(FLastVScrollTime)) > DDVScrollDelay) or
+      ((FVScrollCount > DDMaxSlowCount) and
+        ((Int64(KnowTime) - Int64(FLastVScrollTime)) > (DDVScrollDelay div 4)))) then
+  begin
+    if (Point.Y - 3 * nbPixels <= 0) and (ListBox.TopIndex > 0) then WParam := SB_LINEUP
+      else
+    if (Point.Y + 3 * nbPixels > ListBox.Height) then WParam := SB_LINEDOWN
+      else WParam := -1;
+    if WParam >= 0 then
+    begin
+      BeforeUpdate(nil);
+      ListBox.Perform(WM_VSCROLL, WParam, 0);
+      if FVScrollCount > DDMaxSlowCount then
+        ListBox.Perform(WM_VSCROLL, WParam, 0);
+      if FVScrollCount > DDMaxSlowCount * 3 then
+        ListBox.Perform(WM_VSCROLL, WParam, 0);
+      ListBox.Update;
+      AfterUpdate;
+
+      GetSystemTimeAsFileTime(FLastVScrollTime);
+      Inc(FVScrollCount);
+    end
+      else FVScrollCount := 0;
+  end;
+end;
+
 end.

+ 0 - 1
packages/tbx/TBXUtils.pas

@@ -214,7 +214,6 @@ implementation
 
 uses TB2Common, Math;
 
-
 {$IFDEF TBX_UNICODE}
 
 function GetTextHeightW(DC: HDC): Integer;

+ 7 - 1
packages/theme/ThemeMgr.pas

@@ -1402,6 +1402,12 @@ var
 begin
   Result := False;
 
+  // workaround for so far unknown bug on vista (bug 140)
+  if Message.Msg = WM_GETICON then
+  begin
+    Exit;
+  end;
+
   // If the main manager was destroyed then it posted this message to the application so all still existing
   // theme managers know a new election is due. Well, it is not purely democratic. The earlier a manager was created
   // the higher is the probability to get this message first and become the new main manager.
@@ -2297,7 +2303,7 @@ function TThemeManager.DoAllowSubclassing(Control: TControl): Boolean;
 begin
   Result := True;
   if Assigned(FOnAllowSubclassing) then
-    FOnAllowSubclassing(Self,Control,Result)
+    FOnAllowSubclassing(Self,Control,Result);
 end;
 
 //----------------------------------------------------------------------------------------------------------------------

+ 18 - 0
putty/windows/WINHANDL.C

@@ -492,7 +492,11 @@ void handle_free(struct handle *h)
     }
 }
 
+#ifdef MPEXT
+int handle_got_event(HANDLE event)
+#else
 void handle_got_event(HANDLE event)
+#endif
 {
     struct handle *h;
 
@@ -507,7 +511,11 @@ void handle_got_event(HANDLE event)
 	 * an event notification here for a handle which is already
 	 * deceased. In that situation we simply do nothing.
 	 */
+    #ifdef MPEXT
+    return 0;
+    #else
 	return;
+    #endif
     }
 
     if (h->u.g.moribund) {
@@ -524,7 +532,11 @@ void handle_got_event(HANDLE event)
 	    h->u.g.busy = TRUE;
 	    SetEvent(h->u.g.ev_from_main);
 	}
+    #ifdef MPEXT
+    return 0;
+    #else
 	return;
+    #endif
     }
 
     if (!h->output) {
@@ -545,6 +557,9 @@ void handle_got_event(HANDLE event)
 	    backlog = h->u.i.gotdata(h, h->u.i.buffer, h->u.i.len);
 	    handle_throttle(&h->u.i, backlog);
 	}
+    #ifdef MPEXT
+    return 1;
+    #endif
     } else {
 	h->u.o.busy = FALSE;
 
@@ -566,6 +581,9 @@ void handle_got_event(HANDLE event)
 	    h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data));
 	    handle_try_output(&h->u.o);
 	}
+    #ifdef MPEXT
+    return 0;
+    #endif
     }
 }
 

+ 17 - 0
putty/windows/WINPROXY.C

@@ -15,6 +15,10 @@
 
 typedef struct Socket_localproxy_tag *Local_Proxy_Socket;
 
+#ifdef MPEXT
+extern char *do_select(Plug plug, SOCKET skt, int startup);
+#endif
+
 struct Socket_localproxy_tag {
     const struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
@@ -63,6 +67,11 @@ static void sk_localproxy_close (Socket s)
 {
     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
 
+    #ifdef MPEXT
+    // WinSCP core uses do_select as signalization of connection up/down
+    do_select(ps->plug, INVALID_SOCKET, 0);
+    #endif
+
     handle_free(ps->to_cmd_h);
     handle_free(ps->from_cmd_h);
     CloseHandle(ps->to_cmd_H);
@@ -198,6 +207,9 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
     CreateProcess(NULL, cmd, NULL, NULL, TRUE,
 		  CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
 		  NULL, NULL, &si, &pi);
+    #ifdef MPEXT
+    sfree(cmd);
+    #endif  
 
     CloseHandle(cmd_from_us);
     CloseHandle(cmd_to_us);
@@ -213,5 +225,10 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
     /* We are responsible for this and don't need it any more */
     sk_addr_free(addr);
 
+    #ifdef MPEXT
+    // WinSCP core uses do_select as signalization of connection up/down
+    do_select(plug, INVALID_SOCKET, 1);
+    #endif
+
     return (Socket) ret;
 }

+ 4 - 0
putty/windows/WINSTUFF.H

@@ -428,7 +428,11 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata,
 int handle_write(struct handle *h, const void *data, int len);
 HANDLE *handle_get_events(int *nevents);
 void handle_free(struct handle *h);
+#ifdef MPEXT
+int handle_got_event(HANDLE event);
+#else
 void handle_got_event(HANDLE event);
+#endif
 void handle_unthrottle(struct handle *h, int backlog);
 int handle_backlog(struct handle *h);
 void *handle_get_privdata(struct handle *h);

+ 1 - 1
release/winscp.u3i

@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <u3manifest version="1.0">
-  <application uuid="48b341d1-d411-4b5a-a82c-f3b5d65602fc" version="4.1.3">
+  <application uuid="48b341d1-d411-4b5a-a82c-f3b5d65602fc" version="4.1.4">
     <icon>winscp.ico</icon>
     <name>WinSCP</name>
     <description>Freeware SFTP (SSH File Transfer Protocol), FTP (File Transfer Protocol) and SCP (Secure CoPy) client for Windows using SSH (Secure SHell). Its main function is safe copying of files between a local and a remote computer.</description>

+ 16 - 1
release/winscpsetup.iss

@@ -162,7 +162,7 @@ Name: quicklaunchicon; Description: {cm:QuickLaunchIconTask}; \
 Name: sendtohook; Description: {cm:SendToHookTask}
 Name: urlhandler; Description: {cm:RegisterAsUrlHandler}
 Name: searchpath; Description: {cm:AddSearchPath}; \
-  Flags: unchecked
+  Flags: unchecked; Check: IsAdminLoggedOn
 
 [Icons]
 ; This is created always (unless user checks Don't create a Start menu folder,
@@ -268,14 +268,26 @@ Root: HKCU; Subkey: "{#RegistryKey}"; Flags: uninsdeletekeyifempty
 ; Norton Commander interface
 Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
   ValueName: "Interface"; ValueData: 0; Check: UserSettings(1)
+Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
+  ValueName: "DefaultInterfaceInterface"; ValueData: 0; \
+  Check: UserSettings(1); Flags: noerror
 ; Explorer-like interface
 Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
   ValueName: "Interface"; ValueData: 1; Check: not UserSettings(1)
+Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
+  ValueName: "DefaultInterfaceInterface"; ValueData: 1; \
+  Check: not UserSettings(1); Flags: noerror
 ; Advanced tab on login dialog
 Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
   ValueName: "ShowAdvancedLoginOptions"; ValueData: 0; Check: not UserSettings(2)
+Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
+  ValueName: "DefaultInterfaceShowAdvancedLoginOptions"; ValueData: 0; \
+  Check: not UserSettings(2); Flags: noerror
 Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
   ValueName: "ShowAdvancedLoginOptions"; ValueData: 1; Check: UserSettings(2)
+Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
+  ValueName: "DefaultInterfaceShowAdvancedLoginOptions"; ValueData: 1; \
+  Check: UserSettings(2); Flags: noerror
 ; If installer enabled ddext, let it reset the settings on uninstall,
 ; so the default is used on the next run
 Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
@@ -295,6 +307,9 @@ Root: HKCU; Subkey: "Software\Classes\SCP"; Flags: dontcreatekey uninsdeletekey;
 Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface\Updates"; \
   ValueType: dword; ValueName: "Period"; ValueData: 7; \
   Tasks: enableupdates; Check: not UpdatesEnabled
+Root: HKLM; SubKey: "{#RegistryKey}"; \
+  ValueType: dword; ValueName: "DefaultUpdatesPeriod"; ValueData: 7; \
+  Tasks: enableupdates; Flags: noerror
 
 #if AnyLanguageComplete == 1
 

+ 22 - 0
windows/ConsoleRunner.cpp

@@ -398,11 +398,33 @@ __fastcall TExternalConsole::TExternalConsole(const AnsiString Instance)
     FORMAT("%s%s", (CONSOLE_EVENT_CANCEL, (Instance))).c_str());
   FFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, false,
     FORMAT("%s%s", (CONSOLE_MAPPING, (Instance))).c_str());
+
   if ((FRequestEvent == NULL) || (FResponseEvent == NULL) || (FFileMapping == NULL))
   {
     throw Exception(LoadStr(EXTERNAL_CONSOLE_INIT_ERROR));
   }
 
+  HANDLE Kernel32 = GetModuleHandle(kernel32);
+  typedef HANDLE (* TOpenJobObject)(DWORD DesiredAccess, BOOL InheritHandles, LPCTSTR Name);
+  typedef HANDLE (* TAssignProcessToJobObject)(HANDLE Job, HANDLE Process);
+  TOpenJobObject OpenJobObject =
+    (TOpenJobObject)GetProcAddress(Kernel32, "OpenJobObjectA");
+  TAssignProcessToJobObject AssignProcessToJobObject =
+    (TAssignProcessToJobObject)GetProcAddress(Kernel32, "AssignProcessToJobObject");
+  if ((OpenJobObject != NULL) && (AssignProcessToJobObject != NULL))
+  {
+    HANDLE Job = OpenJobObject(JOB_OBJECT_ASSIGN_PROCESS, FALSE,
+      FORMAT("%s%s", (CONSOLE_JOB, Instance)).c_str());
+    if (Job != NULL)
+    {
+      AssignProcessToJobObject(Job, GetCurrentProcess());
+      // winscp.com keeps the only reference to the job.
+      // once it gets closed (because winscp.com if forcefully terminated),
+      // we get terminated as well
+      CloseHandle(Job);
+    }
+  }
+
   TConsoleCommStruct * CommStruct = GetCommStruct();
   try
   {

+ 11 - 2
windows/CustomWinConfiguration.cpp

@@ -27,6 +27,8 @@ __fastcall TCustomWinConfiguration::TCustomWinConfiguration():
 {
   FHistory = new TStringList();
   FEmptyHistory = new TStringList();
+  FDefaultInterface = ifCommander;
+  FDefaultShowAdvancedLoginOptions = false;
 }
 //---------------------------------------------------------------------------
 __fastcall TCustomWinConfiguration::~TCustomWinConfiguration()
@@ -70,8 +72,8 @@ void __fastcall TCustomWinConfiguration::Default()
 {
   TGUIConfiguration::Default();
 
-  FShowAdvancedLoginOptions = false;
-  FInterface = ifCommander;
+  FShowAdvancedLoginOptions = FDefaultShowAdvancedLoginOptions;
+  FInterface = FDefaultInterface;
   FLogView = lvNone;
   FSynchronizeChecklist.WindowParams = "0;-1;-1;600;450;0";
   FSynchronizeChecklist.ListParams = "1;1|150,1;100,1;80,1;130,1;25,1;100,1;80,1;130,1|0;1;2;3;4;5;6;7";
@@ -297,6 +299,13 @@ void __fastcall TCustomWinConfiguration::LoadData(
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomWinConfiguration::LoadAdmin(THierarchicalStorage * Storage)
+{
+  TGUIConfiguration::LoadAdmin(Storage);
+  FDefaultInterface = TInterface(Storage->ReadInteger("DefaultInterfaceInterface", FDefaultInterface));
+  FDefaultShowAdvancedLoginOptions = Storage->ReadBool("DefaultInterfaceShowAdvancedLoginOptions", FDefaultShowAdvancedLoginOptions);
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomWinConfiguration::SetShowAdvancedLoginOptions(bool value)
 {
   SET_CONFIG_PROPERTY(ShowAdvancedLoginOptions);

+ 3 - 0
windows/CustomWinConfiguration.h

@@ -38,6 +38,8 @@ private:
   TStrings * FEmptyHistory;
   TSynchronizeChecklistConfiguration FSynchronizeChecklist;
   TConsoleWinConfiguration FConsoleWin;
+  TInterface FDefaultInterface;
+  bool FDefaultShowAdvancedLoginOptions;
 
   void __fastcall SetInterface(TInterface value);
   void __fastcall SetLogView(TLogView value);
@@ -50,6 +52,7 @@ private:
 protected:
   virtual void __fastcall SaveData(THierarchicalStorage * Storage, bool All);
   virtual void __fastcall LoadData(THierarchicalStorage * Storage);
+  virtual void __fastcall LoadAdmin(THierarchicalStorage * Storage);
   virtual void __fastcall Saved();
   void __fastcall ClearHistory();
   void __fastcall DefaultHistory();

+ 1 - 1
windows/Setup.cpp

@@ -51,7 +51,7 @@ void err_out(LPCTSTR err_msg)
 // identified by "sys_err".
 void err_out_sys(LPCTSTR base_err_msg, LONG sys_err)
 {
-  LastPathError = FORMAT("%s%s", (base_err_msg, SysErrorMessage(sys_err)));
+  LastPathError = FORMAT("%s %s", (base_err_msg, SysErrorMessage(sys_err)));
 }
 //---------------------------------------------------------------------------
 // Works as "strcmp" but the comparison is not case sensitive.

+ 6 - 4
windows/TerminalManager.cpp

@@ -657,7 +657,7 @@ void __fastcall TTerminalManager::DeleteLocalFile(const AnsiString FileName, boo
 {
   if (!RecursiveDeleteFile(FileName, (WinConfiguration->DeleteToRecycleBin != Alternative)))
   {
-    throw Exception(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (FileName)));
+    throw EOSExtException(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (FileName)));
   }
 }
 //---------------------------------------------------------------------------
@@ -870,7 +870,7 @@ void __fastcall TTerminalManager::TerminalInformation(
 }
 //---------------------------------------------------------------------------
 void __fastcall TTerminalManager::OperationFinished(::TFileOperation Operation,
-  TOperationSide Side, bool Temp, const AnsiString FileName, bool Success,
+  TOperationSide Side, bool Temp, const AnsiString & FileName, bool Success,
   bool & DisconnectWhenFinished)
 {
   assert(ScpExplorer);
@@ -1109,12 +1109,14 @@ void __fastcall TTerminalManager::Idle()
     {
       if (Terminal == ActiveTerminal)
       {
-        Terminal->ShowExtendedException(&E);
+        // throw further, so that the exception is handled in proper place
+        // (particularly in broken-transfer reconnect handler, bug 72)
+        throw;
       }
       else
       {
         // we may not have inactive terminal, unless there is a explorer,
-        // also Idle is calls frome explorer anyway
+        // also Idle is called from explorer anyway
         assert(ScpExplorer != NULL);
         if (ScpExplorer != NULL)
         {

+ 1 - 1
windows/TerminalManager.h

@@ -121,7 +121,7 @@ private:
   void __fastcall SaveTerminal(TTerminal * Terminal);
   void __fastcall SetActiveTerminalIndex(int value);
   void __fastcall OperationFinished(::TFileOperation Operation, TOperationSide Side,
-    bool Temp, const AnsiString FileName, bool Success,
+    bool Temp, const AnsiString & FileName, bool Success,
     bool & DisconnectWhenFinished);
   void __fastcall OperationProgress(TFileOperationProgressType & ProgressData,
     TCancelStatus & Cancel);

+ 4 - 2
windows/WinConfiguration.cpp

@@ -331,6 +331,7 @@ __fastcall TWinConfiguration::TWinConfiguration(): TCustomWinConfiguration()
   FBookmarks = new TBookmarks();
   FCustomCommands = new TCustomCommands();
   FEditorList = new TEditorList();
+  FDefaultUpdatesPeriod = 0;
   Default();
 
   try
@@ -419,7 +420,7 @@ void __fastcall TWinConfiguration::Default()
   FQueueView.LastHideShow = qvHideWhenEmpty;
   FQueueView.ToolBar = false;
 
-  FUpdates.Period = 0;
+  FUpdates.Period = FDefaultUpdatesPeriod;
   FUpdates.LastCheck = 0;
   FUpdates.HaveResults = false;
   FUpdates.ShownResults = false;
@@ -941,8 +942,9 @@ void __fastcall TWinConfiguration::LoadData(THierarchicalStorage * Storage)
 //---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::LoadAdmin(THierarchicalStorage * Storage)
 {
-  TConfiguration::LoadAdmin(Storage);
+  TCustomWinConfiguration::LoadAdmin(Storage);
   FDisableOpenEdit = Storage->ReadBool("DisableOpenEdit", FDisableOpenEdit);
+  FDefaultUpdatesPeriod = Storage->ReadInteger("DefaultUpdatesPeriod", FDefaultUpdatesPeriod);
 }
 //---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::ClearTemporaryLoginData()

+ 1 - 0
windows/WinConfiguration.h

@@ -291,6 +291,7 @@ private:
   AnsiString FDefaultKeyFile;
   bool FAutoOpenInPutty;
   bool FTelnetForFtpInPutty;
+  TDateTime FDefaultUpdatesPeriod;
 
   void __fastcall SetDoubleClickAction(TDoubleClickAction value);
   void __fastcall SetCopyOnDoubleClickConfirmation(bool value);

+ 11 - 1
windows/WinMain.cpp

@@ -248,7 +248,17 @@ int __fastcall Execute()
     }
     else if (Params->FindSwitch("RemoveSearchPath"))
     {
-      RemoveSearchPath(ExtractFilePath(Application->ExeName));
+      try
+      {
+        RemoveSearchPath(ExtractFilePath(Application->ExeName));
+      }
+      catch(...)
+      {
+        // ignore errors
+        // (RemoveSearchPath is called always on uninstallation,
+        // even if AddSearchPath was not used, so we would get the error
+        // always for non-priviledged user)
+      }
     }
     else if (Params->FindSwitch("Update"))
     {