瀏覽代碼

Bug 2054: Prevent hang when dragging files without drag&drop shell extensions when some mapped network drive is not available

https://winscp.net/tracker/2054
(cherry picked from commit 14586d5e183dfe224244f7677d9d8847eee1725a)

Source commit: 96c7721862728c466aabd55f3a867f2221fe1b55
Martin Prikryl 3 年之前
父節點
當前提交
6858331bfd
共有 2 個文件被更改,包括 81 次插入3 次删除
  1. 79 2
      source/packages/filemng/IEDriveInfo.pas
  2. 2 1
      source/windows/GUITools.cpp

+ 79 - 2
source/packages/filemng/IEDriveInfo.pas

@@ -127,7 +127,57 @@ resourceString
 implementation
 
 uses
-  Math, PIDL, OperationWithTimeout, PasTools;
+  Math, PIDL, OperationWithTimeout, PasTools, CompThread;
+
+var
+  ThreadLock: TRTLCriticalSection;
+  ReadyDrives: string;
+
+type
+  TDriveInfoThread = class(TCompThread)
+  public
+    constructor Create(Drives: string);
+  protected
+    procedure Execute; override;
+  private
+    FDrives: string;
+  end;
+
+constructor TDriveInfoThread.Create(Drives: string);
+begin
+  inherited Create(True);
+  FDrives := Drives;
+  FreeOnTerminate := True;
+  Resume;
+end;
+
+procedure TDriveInfoThread.Execute;
+var
+  I: Integer;
+  FreeSpace, Size: Int64;
+  DriveRoot: string;
+  Drive: Char;
+begin
+  if Length(FDrives) = 1 then
+  begin
+    Drive := FDrives[1];
+    DriveRoot := DriveInfo.GetDriveRoot(Drive);
+    if GetDiskFreeSpaceEx(PChar(DriveRoot), FreeSpace, Size, nil) then
+    begin
+      EnterCriticalSection(ThreadLock);
+      ReadyDrives := ReadyDrives + Drive;
+      LeaveCriticalSection(ThreadLock);
+    end;
+  end
+    else
+  begin
+    for I := 1 to Length(FDrives) do
+    begin
+      TDriveInfoThread.Create(FDrives[I]);
+      Sleep(100);
+    end;
+  end;
+end;
 
 constructor TDriveInfo.Create;
 begin
@@ -146,12 +196,28 @@ begin
 end; {TDriveInfo.Destroy}
 
 procedure TDriveInfo.NeedData;
+var
+  I: Integer;
+  Drive: Char;
 begin
   if not FLoaded then
   begin
     Load;
     FLoaded := True;
   end;
+
+  EnterCriticalSection(ThreadLock);
+  try
+    for I := 1 to Length(ReadyDrives) do
+    begin
+      Drive := ReadyDrives[I];
+      Assert(FData.ContainsKey(Drive));
+      FData[Drive].DriveReady := True;
+    end;
+    ReadyDrives := '';
+  finally
+    LeaveCriticalSection(ThreadLock);
+  end;
 end;
 
 function TDriveInfo.AnyValidPath: string;
@@ -310,6 +376,7 @@ var
   Drive: TRealDrive;
   Reg: TRegistry;
   Folder: TSpecialFolder;
+  Drives: string;
 begin
   FNoDrives := 0;
   Reg := TRegistry.Create;
@@ -326,11 +393,16 @@ begin
 
   FDesktop := nil;
 
+  Drives := EmptyStr;
   for Drive := FirstDrive to LastDrive do
   begin
-    AddDrive(Drive);
+    if AddDrive(Drive).Valid then
+      Drives := Drives + Drive;
   end;
 
+  if Length(Drives) > 0 then
+    TDriveInfoThread.Create(Drives);
+
   for Folder := Low(FFolders) to High(FFolders) do
     FFolders[Folder].Valid := False;
 end;
@@ -417,6 +489,9 @@ var
   DriveInfoRec: TDriveInfoRec;
   S: string;
 begin
+  // Among other, this makes sure the pending drive-ready status from the background thread are collected,
+  // before we overwrite it with fresh status here.
+  NeedData;
   if not Assigned(FDesktop) then
     SHGetDesktopFolder(FDesktop);
 
@@ -671,6 +746,7 @@ begin
 end;
 
 initialization
+  InitializeCriticalSection(ThreadLock);
   if not Assigned(DriveInfo) then
     DriveInfo := TDriveInfo.Create;
 
@@ -680,4 +756,5 @@ finalization
     DriveInfo.Free;
     DriveInfo := nil;
   end;
+  DeleteCriticalSection(ThreadLock);
 end.

+ 2 - 1
source/windows/GUITools.cpp

@@ -349,8 +349,9 @@ TObjectList * StartCreationDirectoryMonitorsOnEachDrive(unsigned int Filter, TFi
   {
     if (ExcludedDrives.Pos(Drive) == 0)
     {
+      // Not calling ReadDriveStatus(... dsSize), relying on drive ready status cached by the background thread
       TDriveInfoRec * DriveInfoRec = DriveInfo->Get(Drive);
-      if (DriveInfoRec->Valid &&
+      if (DriveInfoRec->Valid && DriveInfoRec->DriveReady &&
           (DriveInfoRec->DriveType != DRIVE_CDROM) &&
           ((DriveInfoRec->DriveType != DRIVE_REMOVABLE) || (Drive >= DriveInfo->FirstFixedDrive)))
       {