浏览代码

Bug 2216: Local file panel malfunctions when it starts on a drive hidden by Explorer's policy + 2 related bugs

Bug fix: When opening UNC path, the network drive is not added to the other local panel directory tree.
Bug fix: When opening the UNC path on the second local panel, the network drive is not added to drive drop-down menus.
Mechanism for application logging from Pascal code

Source commit: f225fce0fa72a07b3415fd153b39f344cf97096d
Martin Prikryl 2 年之前
父节点
当前提交
f3bd7f4a2f

+ 7 - 0
source/WinSCP.cpp

@@ -16,6 +16,11 @@ USEFORM("forms\ScpExplorer.cpp", ScpExplorerForm);
 #include <GUITools.h>
 #include <Tools.h>
 //---------------------------------------------------------------------------
+void __fastcall AppLogImpl(UnicodeString S)
+{
+  AppLog(S);
+}
+//---------------------------------------------------------------------------
 WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
 {
   int Result = 0;
@@ -27,6 +32,7 @@ WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
     if (Params->FindSwitch(L"applog", AppLogPath))
     {
       ApplicationLog->Enable(AppLogPath);
+      OnAppLog = AppLogImpl;
     }
     AppLog(L"Starting...");
 
@@ -92,6 +98,7 @@ WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
       CoreFinalize();
       WinFinalize();
       AppLog(L"Finalizing done");
+      OnAppLog = NULL;
       SAFE_DESTROY_EX(TApplicationLog, ApplicationLog);
     }
   }

+ 25 - 5
source/forms/ScpCommander.cpp

@@ -2448,13 +2448,33 @@ void __fastcall TScpCommanderForm::CommandLineComboEditWndProc(TMessage & Messag
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TScpCommanderForm::LocalDriveViewRefreshDrives(TObject * /*Sender*/)
+void __fastcall TScpCommanderForm::LocalDriveViewRefreshDrives(TObject * Sender, bool Global)
 {
-  LocalPathComboUpdateDrives();
-  LocalPathComboUpdate(LocalDirView, LocalPathComboBox);
-  if (IsLocalBrowserMode())
+  // Process global drive notifications from only one drive view
+  if (!Global || (Sender == LocalDriveView))
   {
-    LocalPathComboUpdate(OtherLocalDirView, RemotePathComboBox);
+    LocalPathComboUpdateDrives();
+    LocalPathComboUpdate(LocalDirView, LocalPathComboBox);
+    if (IsLocalBrowserMode())
+    {
+      LocalPathComboUpdate(OtherLocalDirView, RemotePathComboBox);
+    }
+  }
+
+  if (!Global)
+  {
+    TDriveView * TheOtherLocalDriveView;
+    if (Sender == LocalDriveView)
+    {
+      TheOtherLocalDriveView = OtherLocalDriveView;
+    }
+    else
+    {
+      DebugAssert(Sender == OtherLocalDriveView);
+      TheOtherLocalDriveView = LocalDriveView;
+    }
+    // Or rather an immediate refresh?
+    TheOtherLocalDriveView->ScheduleDriveRefresh();
   }
 }
 //---------------------------------------------------------------------------

+ 1 - 0
source/forms/ScpCommander.dfm

@@ -1238,6 +1238,7 @@ inherited ScpCommanderForm: TScpCommanderForm
         Height = 45
         WatchDirectory = True
         DirView = OtherLocalDirView
+        OnRefreshDrives = LocalDriveViewRefreshDrives
         OnBusy = DirViewBusy
         OnDDDragEnter = LocalFileControlDDDragEnter
         OnDDDragLeave = FileControlDDDragLeave

+ 1 - 1
source/forms/ScpCommander.h

@@ -513,7 +513,7 @@ __published:
           bool FromLink);
   void __fastcall CommandLineComboBeginEdit(TTBEditItem *Sender,
           TTBEditItemViewer *Viewer, TEdit *EditControl);
-  void __fastcall LocalDriveViewRefreshDrives(TObject *Sender);
+  void __fastcall LocalDriveViewRefreshDrives(TObject *Sender, bool Global);
   void __fastcall QueueSubmenuItemPopup(TTBCustomItem *Sender,
           bool FromLink);
   void __fastcall DirViewHistoryGo(TCustomDirView *Sender, int Index,

+ 30 - 9
source/packages/filemng/DriveView.pas

@@ -117,6 +117,8 @@ type
     procedure Assign(Source: TPersistent); override;
   end;
 
+  TDriveViewRefreshDrives = procedure(Sender: TObject; Global: Boolean) of object;
+
   TDriveView = class(TCustomDriveView)
   private
     FDriveStatus: TObjectDictionary<string, TDriveStatus>;
@@ -145,7 +147,7 @@ type
 
     {Additional events:}
     FOnDisplayContextMenu: TNotifyEvent;
-    FOnRefreshDrives: TNotifyEvent;
+    FOnRefreshDrives: TDriveViewRefreshDrives;
     FOnNeedHiddenDirectories: TNotifyEvent;
 
     {used components:}
@@ -195,7 +197,8 @@ type
     procedure DriveRemoved(Drive: string);
     procedure DriveRemoving(Drive: string);
     procedure CancelDriveRefresh;
-    procedure ScheduleDriveRefresh;
+    procedure DoRefreshDrives(Global: Boolean);
+    procedure RefreshRootNodes(dsFlags: Integer);
 
     function DirAttrMask: Integer;
     function CreateDriveStatus: TDriveStatus;
@@ -254,8 +257,8 @@ type
     function GetDriveToNode(Node: TTreeNode): string;
     function GetDriveText(Drive: string): string;
     procedure ScanDrive(Drive: string);
-    procedure RefreshRootNodes(dsFlags: Integer);
     function GetDrives: TStrings;
+    procedure ScheduleDriveRefresh;
 
     {Node handling:}
     procedure SetImageIndex(Node: TTreeNode); virtual;
@@ -316,8 +319,7 @@ type
     {Additional events:}
     property OnDisplayContextMenu: TNotifyEvent read FOnDisplayContextMenu
       write FOnDisplayContextMenu;
-    property OnRefreshDrives: TNotifyEvent read FOnRefreshDrives
-      write FOnRefreshDrives;
+    property OnRefreshDrives: TDriveViewRefreshDrives read FOnRefreshDrives write FOnRefreshDrives;
     property OnBusy;
 
     property DDLinkOnExeDrag;
@@ -706,8 +708,7 @@ begin
       try
         //DriveInfo.Load;
         RefreshRootNodes(dsAll or dvdsRereadAllways);
-        if Assigned(OnRefreshDrives) then
-          OnRefreshDrives(Self);
+        DoRefreshDrives(True);
       except
         Application.HandleException(Self);
       end;
@@ -717,6 +718,12 @@ begin
   end;
 end;
 
+procedure TDriveView.DoRefreshDrives(Global: Boolean);
+begin
+  if Assigned(OnRefreshDrives) then
+    OnRefreshDrives(Self, Global);
+end;
+
 procedure TDriveView.CancelDriveRefresh;
 begin
   KillTimer(FInternalWindowHandle, 1);
@@ -1561,8 +1568,7 @@ begin
     Result := CreateDriveStatus;
     FDriveStatus.Add(Drive, Result);
     RefreshRootNodes(dsAll or dvdsRereadAllways);
-    if Assigned(OnRefreshDrives) then
-      OnRefreshDrives(Self);
+    DoRefreshDrives(False);
   end;
 end; {GetDriveStatus}
 
@@ -1732,6 +1738,21 @@ begin {FindNodeToPath}
   HandleNeeded;
 
   Drive := DriveInfo.GetDriveKey(Path);
+  if (not Assigned(GetDriveStatus(Drive).RootNode)) and
+     // hidden or possibly recently un-hidden by other drive view (refresh is pending)
+     (DriveInfo.Get(Drive).Valid or DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy) then
+  begin
+    if DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy then
+      DriveInfo.OverrideDrivePolicy(Drive);
+
+    if DriveInfo.Get(Drive).Valid then
+    begin
+      CancelDriveRefresh; // cancel a possible pending refresh (see the previous comment)
+      RefreshRootNodes(dsAll or dvdsRereadAllways); // overkill and is likely already called by GetDriveStatus
+      DoRefreshDrives(False);
+    end;
+  end;
+
   if Assigned(GetDriveStatus(Drive).RootNode) then
   begin
     if DriveInfo.IsRealDrive(Drive) then

+ 57 - 17
source/packages/filemng/IEDriveInfo.pas

@@ -51,6 +51,7 @@ type
     PIDL        : PItemIDList; {Fully qualyfied PIDL}
     Init        : Boolean;     {Drivestatus was updated once}
     Valid       : Boolean;     {Drivestatus is valid}
+    ValidButHiddenByDrivePolicy: Boolean;
     DriveReady  : Boolean;     {Drive is ready}
     DriveType   : Integer;     {DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE}
     DisplayName : string;      {Windows displayname}
@@ -87,6 +88,8 @@ type
     function GetFirstFixedDrive: Char;
     procedure Load;
     function AddDrive(Drive: string): TDriveInfoRec;
+    function GetDriveBitMask(Drive: string): Integer;
+    function DoAnyValidPath(DriveType: Integer; CanBeHidden: Boolean; var Path: string): Boolean;
 
   public
     function Get(Drive: string): TDriveInfoRec;
@@ -103,6 +106,7 @@ type
     function GetDisplayName(Drive: string): string;
     function GetPrettyName(Drive: string): string;
     function ReadDriveStatus(Drive: string; Flags: Integer): Boolean;
+    procedure OverrideDrivePolicy(Drive: string);
     property HonorDrivePolicy: Boolean read FHonorDrivePolicy write SetHonorDrivePolicy;
     property FirstFixedDrive: Char read GetFirstFixedDrive;
     property UseABDrives: Boolean read FUseABDrives write FUseABDrives;
@@ -220,28 +224,36 @@ begin
   end;
 end;
 
-function TDriveInfo.AnyValidPath: string;
+function TDriveInfo.DoAnyValidPath(DriveType: Integer; CanBeHidden: Boolean; var Path: string): Boolean;
 var
   Drive: TRealDrive;
+  DriveInfoRec: TDriveInfoRec;
 begin
-  // Fallback to A:/B: if no other drive is found?
-  for Drive := SystemDrive to LastDrive do
-    if Get(Drive).Valid and
-       (Get(Drive).DriveType = DRIVE_FIXED) and
-       DirectoryExists(ApiPath(GetDriveRoot(Drive))) then
-    begin
-      Result := GetDriveRoot(Drive);
-      Exit;
-    end;
   for Drive := SystemDrive to LastDrive do
-    if Get(Drive).Valid and
-       (Get(Drive).DriveType = DRIVE_REMOTE) and
+  begin
+    DriveInfoRec := Get(Drive);
+    if (DriveInfoRec.Valid or
+        (CanBeHidden and DriveInfoRec.ValidButHiddenByDrivePolicy)) and
+       (DriveInfoRec.DriveType = DriveType) and
        DirectoryExists(ApiPath(GetDriveRoot(Drive))) then
     begin
-      Result := GetDriveRoot(Drive);
+      Result := True;
+      Path := GetDriveRoot(Drive);
       Exit;
     end;
-  raise Exception.Create(SNoValidPath);
+  end;
+
+  Result := False;
+end;
+
+function TDriveInfo.AnyValidPath: string;
+begin
+  if (not DoAnyValidPath(DRIVE_FIXED, False, Result)) and
+     (not DoAnyValidPath(DRIVE_FIXED, True, Result)) and
+     (not DoAnyValidPath(DRIVE_REMOTE, False, Result)) then
+  begin
+    raise Exception.Create(SNoValidPath);
+  end;
 end;
 
 function TDriveInfo.IsRealDrive(Drive: string): Boolean;
@@ -346,15 +358,26 @@ begin
     else Result := SystemDrive;
 end;
 
+function TDriveInfo.GetDriveBitMask(Drive: string): Integer;
+begin
+  Assert(IsRealDrive(Drive));
+  Result := (1 shl (Ord(Drive[1]) - Ord('A')));
+end;
+
 procedure TDriveInfo.ReadDriveBasicStatus(Drive: string);
+var
+  ValidDriveType: Boolean;
+  HiddenByDrivePolicy: Boolean;
 begin
   Assert(FData.ContainsKey(Drive));
   with FData[Drive] do
   begin
     DriveType := Windows.GetDriveType(PChar(GetDriveRoot(Drive)));
-    Valid :=
-      ((not IsRealDrive(Drive)) or (not FHonorDrivePolicy) or (not Bool((1 shl (Ord(Drive[1]) - 65)) and FNoDrives))) and
-      (DriveType in [DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE]);
+    ValidDriveType := (DriveType in [DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE]);
+    HiddenByDrivePolicy :=
+      FHonorDrivePolicy and IsRealDrive(Drive) and ((GetDriveBitMask(Drive) and FNoDrives) <> 0);
+    ValidButHiddenByDrivePolicy := ValidDriveType and HiddenByDrivePolicy;
+    Valid := ValidDriveType and (not HiddenByDrivePolicy);
   end;
 end;
 
@@ -378,6 +401,7 @@ var
   Folder: TSpecialFolder;
   Drives: string;
 begin
+  AppLog('Loading drives');
   FNoDrives := 0;
   Reg := TRegistry.Create;
   try
@@ -390,6 +414,7 @@ begin
     end;
   end;
   Reg.Free;
+  AppLog(Format('NoDrives mask: %d', [Integer(FNoDrives)]));
 
   FDesktop := nil;
 
@@ -401,7 +426,10 @@ begin
   end;
 
   if Length(Drives) > 0 then
+  begin
+    AppLog(Format('Drives found: %s', [Drives]));
     TDriveInfoThread.Create(Drives);
+  end;
 
   for Folder := Low(FFolders) to High(FFolders) do
     FFolders[Folder].Valid := False;
@@ -616,6 +644,18 @@ begin
   end;
 end; {TDriveInfo.ReadDriveStatus}
 
+procedure TDriveInfo.OverrideDrivePolicy(Drive: string);
+var
+  Mask: DWORD;
+begin
+  Assert(FData.ContainsKey(Drive));
+  Assert(FData[Drive].ValidButHiddenByDrivePolicy);
+  Mask := (not GetDriveBitMask(Drive));
+  FNoDrives := FNoDrives and Mask;
+  ReadDriveBasicStatus(Drive);
+  Assert(FData[Drive].Valid);
+end;
+
 function GetShellFileName(const Name: string): string;
 var
   SFI: TSHFileInfo;

+ 17 - 0
source/packages/my/PasTools.pas

@@ -93,6 +93,15 @@ var
 {$EXTERNALSYM ApiPath}
 function ApiPath(Path: string): string;
 
+type
+  TAppLogEvent = procedure(S: string);
+
+var
+  OnAppLog: TAppLogEvent = nil;
+
+{$EXTERNALSYM AppLog}
+procedure AppLog(S: string);
+
 type
   TControlScrollBeforeUpdate = procedure(ObjectToValidate: TObject) of object;
   TControlScrollAfterUpdate = procedure of object;
@@ -686,6 +695,14 @@ begin
   end;
 end;
 
+procedure AppLog(S: string);
+begin
+  if Assigned(OnAppLog) then
+  begin
+    OnAppLog(S);
+  end;
+end;
+
   { TCustomControlScrollOnDragOver }
 
 constructor TCustomControlScrollOnDragOver.Create(Control: TControl;