1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00

Getting rid of TDictionary as it crashes 64-bit linker

https://winscp.net/tracker/1016

Source commit: 02d79c8672c60c572c9b722a93dea8b5030ea1fe
This commit is contained in:
Martin Prikryl
2025-12-23 13:46:56 +00:00
parent 0eb2d61e79
commit c562d3d4c4
10 changed files with 331 additions and 224 deletions

View File

@@ -68,7 +68,7 @@
<RunBCCOutOfProcess>true</RunBCCOutOfProcess>
<SanitizedProjectName>DScpComp</SanitizedProjectName>
<SubProcessesNumber>8</SubProcessesNumber>
<TLIB_PageSize>64</TLIB_PageSize>
<TLIB_PageSize>128</TLIB_PageSize>
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
<VerInfo_Locale>1033</VerInfo_Locale>
</PropertyGroup>

View File

@@ -2,6 +2,7 @@
#include <vcl.h>
#pragma hdrstop
#include <Common.h>
#include "DirView.h"
#include "IEDriveInfo.h"
//---------------------------------------------------------------------------
@@ -12,3 +13,107 @@ __fastcall TDirView::TDirView(TComponent * AOwner) :
{
DriveInfoRequire();
}
//---------------------------------------------------------------------------
void __fastcall TDirView::IconUpdateClear()
{
FIconUpdateQueue = TIconUpdateQueue();
FIconUpdateQueueDeferred = TIconUpdateQueue();
FIconUpdateSet.clear();
}
//---------------------------------------------------------------------------
void __fastcall TDirView::IconUpdateEnqueue(TListItem * ListItem)
{
if (FIconUpdateSet.find(ListItem->Index) == FIconUpdateSet.end())
{
TIconUpdateSchedule Schedule { .Index = ListItem->Index };
FIconUpdateQueue.push(Schedule);
DebugAssert(FIconUpdateSet.size() == FIconUpdateQueue.size() + FIconUpdateQueueDeferred.size());
}
StartIconUpdateThread();
}
//---------------------------------------------------------------------------
int __fastcall TDirView::IconUpdatePeek()
{
int Result;
if (!FIconUpdateQueue.empty())
{
Result = FIconUpdateQueue.front().Index;
}
else if (!FIconUpdateQueueDeferred.empty())
{
Result = FIconUpdateQueueDeferred.front().Index;
}
else
{
Result = -1;
}
return Result;
}
//---------------------------------------------------------------------------
bool __fastcall TDirView::IconUpdateDeprioritize(Dirviewint::TFileRec * ItemData, int Index)
{
bool Result = !FIconUpdateQueue.empty();
if (Result)
{
DebugAssert(!ThumbnailNeeded(ItemData));
if (ItemData->IconEmpty)
{
TIconUpdateSchedule Schedule = FIconUpdateQueue.front();
FIconUpdateQueue.pop();
FIconUpdateQueueDeferred.push(Schedule);
}
else
{
IconUpdateDequeue(Index);
}
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TDirView::IconUpdateDequeue(int Index)
{
if (!FIconUpdateQueue.empty())
{
FIconUpdateQueue.pop();
}
else
{
FIconUpdateQueueDeferred.pop();
}
FIconUpdateSet.erase(Index);
DebugAssert(FIconUpdateSet.size() == FIconUpdateQueue.size() + FIconUpdateQueueDeferred.size());
}
//---------------------------------------------------------------------------
void __fastcall TDirView::PathChanged()
{
TDirViewInt::PathChanged();
// make sure to use PathName as Path maybe just X: what
// ExpandFileName resolves to current working directory
// on the drive, not to root path
UnicodeString Expanded = ExpandFileName(PathName);
FLastPaths[DriveInfo->GetDriveKey(Expanded)] = Expanded;
}
//---------------------------------------------------------------------------
bool __fastcall TDirView::TryGetLastPath(UnicodeString Drive, UnicodeString & Path)
{
auto I = FLastPaths.find(Drive);
bool Result = (I != FLastPaths.end());
if (Result)
{
Path = I->second;
#ifndef DESIGN_ONLY
if (!DirectoryExists(ApiPath(Path)))
{
if (DriveInfo->IsRealDrive(Drive))
{
Path = FORMAT(L"%s:", (Drive));
}
else
{
Path = Drive;
}
}
#endif
}
return Result;
}

View File

@@ -3,11 +3,30 @@
#define DirViewH
//---------------------------------------------------------------------------
#include "DirViewInt.hpp"
#include <set>
#include <queue>
#include <map>
//---------------------------------------------------------------------------
class TDirView : public TDirViewInt
{
public:
__fastcall TDirView(TComponent * AOwner);
protected:
virtual void __fastcall IconUpdateClear();
virtual void __fastcall IconUpdateEnqueue(TListItem * ListItem);
virtual int __fastcall IconUpdatePeek();
virtual bool __fastcall IconUpdateDeprioritize(Dirviewint::TFileRec * ItemData, int Index);
virtual void __fastcall IconUpdateDequeue(int Index);
virtual void __fastcall PathChanged();
virtual bool __fastcall TryGetLastPath(UnicodeString Drive, UnicodeString & Path);
private:
std::set<int> FIconUpdateSet;
typedef std::queue<TIconUpdateSchedule> TIconUpdateQueue;
TIconUpdateQueue FIconUpdateQueue;
TIconUpdateQueue FIconUpdateQueueDeferred;
std::map<UnicodeString, UnicodeString> FLastPaths;
};
//---------------------------------------------------------------------------
#endif

View File

@@ -2,6 +2,7 @@
#include <vcl.h>
#pragma hdrstop
#include <Common.h>
#include "DriveView.h"
#include "IEDriveInfo.h"
//---------------------------------------------------------------------------
@@ -11,4 +12,47 @@ __fastcall TDriveView::TDriveView(TComponent * AOwner) :
TDriveViewInt(AOwner)
{
DriveInfoRequire();
FDriveStatus.reset(new TStringList());
FDriveStatus->Sorted = true;
for (TRealDrive Drive = FirstDrive; Drive <= LastDrive; Drive++)
{
FDriveStatus->AddObject(Drive, CreateDriveStatus());
}
}
//---------------------------------------------------------------------------
TDriveStatus * __fastcall TDriveView::GetDriveStatus(UnicodeString Drive)
{
int Index = FDriveStatus->IndexOf(Drive);
TDriveStatus * Result;
if (Index < 0)
{
Result = CreateDriveStatus();
FDriveStatus->AddObject(Drive, Result);
}
else
{
Result = GetDriveStatus(Index);
}
return Result;
}
//---------------------------------------------------------------------------
TDriveStatus * TDriveView::GetDriveStatus(int Index)
{
return DebugNotNull(dynamic_cast<TDriveStatus *>(FDriveStatus->Objects[Index]));
}
//---------------------------------------------------------------------------
bool __fastcall TDriveView::GetNextDriveStatus(int & Iterator, UnicodeString * Drive, TDriveStatus *& Status)
{
bool Result = (Iterator < FDriveStatus->Count);
if (Result)
{
if (Drive != nullptr)
{
*Drive = FDriveStatus->Strings[Iterator];
}
Status = GetDriveStatus(Iterator);
++Iterator;
}
return Result;
}

View File

@@ -8,6 +8,18 @@ class TDriveView : public TDriveViewInt
{
public:
__fastcall TDriveView(TComponent * AOwner);
virtual TDriveStatus * __fastcall GetDriveStatus(UnicodeString Drive);
protected:
// Using TStringList as it's easy to iterate with int iterator for GetNextDriveStatus.
// Once all code is moved here, we can replace it easily with std::map.
std::unique_ptr<TStringList> FDriveStatus;
virtual bool __fastcall GetNextDriveStatus(int & Iterator, UnicodeString * Drive, TDriveStatus *& Status);
private:
TDriveStatus * GetDriveStatus(int Index);
};
//---------------------------------------------------------------------------
#endif

View File

@@ -2,7 +2,9 @@
#include <vcl.h>
#pragma hdrstop
#include <Common.h>
#include "IEDriveInfo.h"
#include "PasTools.hpp"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
@@ -18,3 +20,66 @@ __fastcall TDriveInfo::TDriveInfo() :
TDriveInfoInt()
{
}
//---------------------------------------------------------------------------
TDriveInfoRec * __fastcall TDriveInfo::AddDrive(UnicodeString Drive)
{
TDriveInfoRec * Result = new TDriveInfoRec();
FData.insert(std::make_pair(Drive, Result));
ResetDrive(Drive);
if (IsFixedDrive(Drive) || !IsRealDrive(Drive)) // not floppy
{
DoReadDriveStatus(Drive, dsSynchronous);
}
else
{
ReadDriveBasicStatus(Drive);
}
return Result;
}
//---------------------------------------------------------------------------
TDriveInfoRec * __fastcall TDriveInfo::GetInternal(UnicodeString Drive)
{
auto I = FData.find(Drive);
DebugAssert(I != FData.end());
return I->second.get();
}
//---------------------------------------------------------------------------
TDriveInfoRec * __fastcall TDriveInfo::Get(UnicodeString Drive)
{
NeedData();
// We might want to wait for FReadyDrives to be empty before returning
// (or even better do that only in DriveReady getter)
TDriveInfoRec * Result;
auto I = FData.find(Drive);
if (I == FData.end())
{
DebugAssert(IsUncPath(Drive));
Result = AddDrive(Drive);
DriveRefresh();
}
else
{
Result = I->second.get();
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TDriveInfo::DriveRemoving(THandle DeviceHandle)
{
for (auto I = FData.begin(); I != FData.end(); ++I)
{
if (I->second->DriveHandle == DeviceHandle)
{
TDriveInfoInt::DriveRemoving(I->first);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TDriveInfo::UpdateDrivesNotifications()
{
for (auto I = FData.begin(); I != FData.end(); ++I)
{
UpdateDriveNotifications(I->first);
}
}

View File

@@ -3,11 +3,22 @@
#define IEDriveInfoH
//---------------------------------------------------------------------------
#include "IEDriveInfoInt.hpp"
#include <map>
//---------------------------------------------------------------------------
class TDriveInfo : public TDriveInfoInt
{
public:
__fastcall TDriveInfo();
protected:
virtual TDriveInfoRec * __fastcall AddDrive(UnicodeString Drive);
virtual TDriveInfoRec * __fastcall GetInternal(UnicodeString Drive);
virtual TDriveInfoRec * __fastcall Get(UnicodeString Drive);
virtual void __fastcall DriveRemoving(THandle DeviceHandle);
virtual void __fastcall UpdateDrivesNotifications();
private:
std::map<UnicodeString, std::unique_ptr<TDriveInfoRec>> FData;
};
//---------------------------------------------------------------------------
void DriveInfoRequire();

View File

@@ -121,7 +121,6 @@ type
FParentFolder: IShellFolder;
FDesktopFolder: IShellFolder;
FDirOK: Boolean;
FPath: string;
FHiddenCount: Integer;
FFilteredCount: Integer;
FNotRelative: Boolean;
@@ -131,9 +130,6 @@ type
// Additional thread components:
FIconUpdateThread: TIconUpdateThread;
// only ever accessed by GUI thread
FIconUpdateSet: TDictionary<Integer, Boolean>;
FIconUpdateQueue: TQueue<TIconUpdateSchedule>;
FIconUpdateQueueDeferred: TQueue<TIconUpdateSchedule>;
FDiscMonitor: TDiscMonitor;
FHomeDirectory: string;
@@ -143,7 +139,6 @@ type
iRecycleFolder: iShellFolder;
PIDLRecycle: PItemIDList;
FLastPath: TDictionary<string, string>;
FTimeoutShellIconRetrieval: Boolean;
FAnyCut: Boolean;
@@ -157,6 +152,8 @@ type
procedure SetDirColProperties(Value: TDirViewColProperties);
protected
FPath: string;
function NewColProperties: TCustomListViewColProperties; override;
function SortAscendingByDefault(Index: Integer): Boolean; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
@@ -183,7 +180,6 @@ type
procedure SetLoadEnabled(Value: Boolean); override;
function GetPath: string; override;
procedure SetPath(Value: string); override;
procedure PathChanged; override;
procedure SetItemImageIndex(Item: TListItem; Index: Integer); override;
procedure ChangeDetected(Sender: TObject; const Directory: string;
var SubdirsChanged: Boolean);
@@ -214,9 +210,9 @@ type
procedure DoFetchIconUpdate;
procedure DoUpdateIcon;
procedure StartFileDeleteThread;
procedure IconUpdateEnqueue(ListItem: TListItem);
function IconUpdatePeek: Integer;
procedure IconUpdateDequeue;
procedure IconUpdateEnqueue(ListItem: TListItem); virtual; abstract;
function IconUpdatePeek: Integer; virtual; abstract;
procedure IconUpdateDequeue(Index: Integer); virtual; abstract;
procedure WMDestroy(var Msg: TWMDestroy); message WM_DESTROY;
procedure Load(DoFocusSomething: Boolean); override;
procedure DoFetchIcon(
@@ -226,6 +222,9 @@ type
dwFileAttributes: DWORD; var psfi: TSHFileInfoW; uFlags: UINT): DWORD_PTR;
function DoCopyToClipboard(Focused: Boolean; Cut: Boolean; Operation: TClipBoardOperation): Boolean;
procedure ClearCutState;
procedure IconUpdateClear; virtual; abstract;
function IconUpdateDeprioritize(ItemData: PFileRec; Index: Integer): Boolean; virtual; abstract;
function TryGetLastPath(Drive: string; var Path: string): Boolean; virtual; abstract;
function HiddenCount: Integer; override;
function FilteredCount: Integer; override;
@@ -687,22 +686,12 @@ begin
DragDropFilesEx.ShellExtensions.DragDropHandler := True;
DragDropFilesEx.ShellExtensions.DropHandler := True;
FIconUpdateSet := TDictionary<Integer, Boolean>.Create;
FIconUpdateQueue := TQueue<TIconUpdateSchedule>.Create;
FIconUpdateQueueDeferred := TQueue<TIconUpdateSchedule>.Create;
FLastPath := nil;
end;
destructor TDirViewInt.Destroy;
begin
FreeAndNil(FIconUpdateQueue);
FreeAndNil(FIconUpdateQueueDeferred);
FreeAndNil(FIconUpdateSet);
if Assigned(PIDLRecycle) then FreePIDL(PIDLRecycle);
FLastPath.Free;
FFileOperator.Free;
FChangeTimer.Free;
@@ -753,23 +742,6 @@ begin
Result := FPath;
end;
procedure TDirViewInt.PathChanged;
var
Expanded: string;
begin
inherited;
// make sure to use PathName as Path maybe just X: what
// ExpandFileName resolves to current working directory
// on the drive, not to root path
Expanded := ExpandFileName(PathName);
if not Assigned(FLastPath) then
begin
FLastPath := TDictionary<string, string>.Create;
end;
FLastPath.AddOrSetValue(DriveInfo.GetDriveKey(Expanded), Expanded);
end;
procedure TDirViewInt.SetPath(Value: string);
const
LongPathPrefix = '\\?\';
@@ -1191,9 +1163,7 @@ procedure TDirViewInt.Load(DoFocusSomething: Boolean);
begin
try
StopIconUpdateThread;
FIconUpdateQueue.Clear;
FIconUpdateQueueDeferred.Clear;
FIconUpdateSet.Clear;
IconUpdateClear();
StopWatchThread;
@@ -2372,40 +2342,6 @@ begin
end;
end;
procedure TDirViewInt.IconUpdateEnqueue(ListItem: TListItem);
var
Schedule: TIconUpdateSchedule;
begin
if not FIconUpdateSet.ContainsKey(ListItem.Index) then
begin
FIconUpdateSet.Add(ListItem.Index, False);
Schedule.Index := ListItem.Index;
FIconUpdateQueue.Enqueue(Schedule);
Assert(FIconUpdateSet.Count = FIconUpdateQueue.Count + FIconUpdateQueueDeferred.Count);
end;
StartIconUpdateThread;
end;
function TDirViewInt.IconUpdatePeek: Integer;
begin
if FIconUpdateQueue.Count > 0 then
Result := FIconUpdateQueue.Peek.Index
else if FIconUpdateQueueDeferred.Count > 0 then
Result := FIconUpdateQueueDeferred.Peek.Index
else
Result := -1;
end;
procedure TDirViewInt.IconUpdateDequeue;
begin
if FIconUpdateQueue.Count > 0 then
FIconUpdateQueue.Dequeue
else
FIconUpdateQueueDeferred.Dequeue;
FIconUpdateSet.Remove(FIconUpdateThread.FCurrentIndex);
Assert(FIconUpdateSet.Count = FIconUpdateQueue.Count + FIconUpdateQueueDeferred.Count);
end;
function TDirViewInt.ThumbnailNeeded(ItemData: PFileRec): Boolean;
begin
Result := (not Assigned(ItemData.Thumbnail)) and (ItemData.ThumbnailSize <> ThumbnailNotNeeded);
@@ -2428,7 +2364,7 @@ begin
if FIconUpdateThread.FCurrentIndex >= Items.Count then
begin
FIconUpdateThread.FCurrentIndex := -1;
IconUpdateDequeue;
IconUpdateDequeue(-1);
end
else
begin
@@ -2448,18 +2384,8 @@ begin
// Deprioritizing unvisible item taken from priority queue.
// As we ask Window to cache the image index (LVIF_DI_SETITEM), we won't get asked again,
// so we have to retrieve the image eventually (but not thumbnail)
if (FIconUpdateQueue.Count > 0) and
Invisible then
if Invisible and IconUpdateDeprioritize(ItemData, FIconUpdateThread.FCurrentIndex) then
begin
Assert(not ThumbnailNeeded(ItemData));
if ItemData.IconEmpty then
begin
FIconUpdateQueueDeferred.Enqueue(FIconUpdateQueue.Dequeue);
end
else
begin
IconUpdateDequeue;
end;
FIconUpdateThread.FCurrentIndex := -1;
end
else
@@ -2472,7 +2398,7 @@ begin
end
else
begin
IconUpdateDequeue;
IconUpdateDequeue(FIconUpdateThread.FCurrentIndex);
FIconUpdateThread.FCurrentIndex := -1;
end;
end;
@@ -2492,7 +2418,7 @@ begin
(not FIconUpdateThread.Terminated) and
(FIconUpdateThread.FCurrentIndex = IconUpdatePeek) then
begin
IconUpdateDequeue;
IconUpdateDequeue(FIconUpdateThread.FCurrentIndex);
if (FIconUpdateThread.FCurrentIndex >= 0) and
(FIconUpdateThread.FCurrentIndex < Items.Count) then
@@ -2749,18 +2675,7 @@ var
APath: string;
DriveRoot: string;
begin
if Assigned(FLastPath) and FLastPath.ContainsKey(Drive) then
begin
APath := FLastPath[Drive];
if not DirectoryExists(ApiPath(APath)) then
begin
if DriveInfo.IsRealDrive(Drive) then
APath := Format('%s:', [Drive])
else
APath := Drive;
end;
end
else
if not TryGetLastPath(Drive, APath) then
begin
if DriveInfo.IsRealDrive(Drive) then
begin

View File

@@ -56,8 +56,6 @@ type
DefaultDir: string; // Current directory
end;
TDriveStatusPair = TPair<string, TDriveStatus>;
TScanDirInfo = record
SearchNewDirs: Boolean;
StartNode: TTreeNode;
@@ -142,8 +140,6 @@ type
TDriveViewInt = class(TCustomDriveView)
private
FDriveStatus: TObjectDictionary<string, TDriveStatus>;
FConfirmDelete: Boolean;
FConfirmOverwrite: Boolean;
FWatchDirectory: Boolean;
@@ -227,6 +223,7 @@ type
function DirAttrMask: Integer;
function CreateDriveStatus: TDriveStatus;
function GetNextDriveStatus(var Iterator: Integer; Drive: PString; var Status: TDriveStatus): Boolean; virtual; abstract;
procedure ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
NewDirs: Boolean); override;
@@ -270,7 +267,7 @@ type
procedure EmptyClipboard; dynamic;
procedure EmptyClipboardIfCut; override;
// Drive handling:
function GetDriveStatus(Drive: string): TDriveStatus;
function GetDriveStatus(Drive: string): TDriveStatus; virtual; abstract;
function GetDriveTypetoNode(Node: TTreeNode): Integer; // Returns DRIVE_CDROM etc..
function GetDriveToNode(Node: TTreeNode): string;
procedure ScanDrive(Drive: string);
@@ -798,21 +795,13 @@ end;
// TDriveViewInt
constructor TDriveViewInt.Create(AOwner: TComponent);
var
Drive: TRealDrive;
begin
inherited;
FCreating := True;
FDriveStatus := TObjectDictionary<string, TDriveStatus>.Create([doOwnsValues]);
FChangeInterval := MSecsPerSec;
for Drive := FirstDrive to LastDrive do
begin
FDriveStatus.Add(Drive, CreateDriveStatus);
end;
FFileOperator := TFileOperator.Create(Self);
FSubDirReaderThread := TSubDirReaderThread.Create(Self);
FDelayedNodes := TStringList.Create;
@@ -841,27 +830,25 @@ begin
FStartPos.Y := -1;
FDragPos := FStartPos;
DriveInfo.AddHandler(DriveNotification);
FDragDropFilesEx.ShellExtensions.DragDropHandler := True;
DriveInfo.AddHandler(DriveNotification);
end; // Create
destructor TDriveViewInt.Destroy;
var
DriveStatusPair: TDriveStatusPair;
begin
DriveInfo.RemoveHandler(DriveNotification);
for DriveStatusPair in FDriveStatus do
var Iterator := 0;
var DriveStatus: TDriveStatus;
while GetNextDriveStatus(Iterator, nil, DriveStatus) do
begin
var DriveStatus := DriveStatusPair.Value;
if Assigned(DriveStatus.DiscMonitor) then
FreeAndNil(DriveStatus.DiscMonitor);
if Assigned(DriveStatus.ChangeTimer) then
FreeAndNil(DriveStatus.ChangeTimer);
end;
FDriveStatus.Free;
if Assigned(FFileOperator) then
FFileOperator.Free;
@@ -896,8 +883,6 @@ begin
end;
procedure TDriveViewInt.CreateWnd;
var
DriveStatus: TDriveStatus;
begin
inherited;
@@ -910,7 +895,9 @@ begin
FPrevSelectedIndex := -1;
end;
for DriveStatus in FDriveStatus.Values do
var Iterator := 0;
var DriveStatus: TDriveStatus;
while GetNextDriveStatus(Iterator, nil, DriveStatus) do
begin
if DriveStatus.RootNodeIndex >= 0 then
begin
@@ -930,7 +917,6 @@ end; // CreateWnd
procedure TDriveViewInt.DestroyWnd;
var
DriveStatus: TDriveStatus;
I: Integer;
begin
FDelayedNodeTimer.Enabled := False;
@@ -960,7 +946,9 @@ begin
FPrevSelected := nil;
end;
for DriveStatus in FDriveStatus.Values do
var Iterator := 0;
var DriveStatus: TDriveStatus;
while GetNextDriveStatus(Iterator, nil, DriveStatus) do
begin
DriveStatus.RootNodeIndex := -1;
if Assigned(DriveStatus.RootNode) then
@@ -1459,14 +1447,15 @@ end;
function TDriveViewInt.GetDrives: TStrings;
var
DriveStatusPair: TDriveStatusPair;
Drives: TStringList;
begin
Drives := TStringList.Create;
// We could iterate only .Keys here, but that crashes IDE for some reason
for DriveStatusPair in FDriveStatus do
var Iterator := 0;
var Drive: string;
var DriveStatus: TDriveStatus;
while GetNextDriveStatus(Iterator, @Drive, DriveStatus) do
begin
Drives.Add(DriveStatusPair.Key);
Drives.Add(Drive);
end;
Drives.CustomSort(CompareDrive);
Result := Drives;
@@ -1627,15 +1616,6 @@ begin
end;
function TDriveViewInt.GetDriveStatus(Drive: string): TDriveStatus;
begin
if not FDriveStatus.TryGetValue(Drive, Result) then
begin
Result := CreateDriveStatus;
FDriveStatus.Add(Drive, Result);
end;
end;
function TDriveViewInt.DoScanDir(FromNode: TTreeNode): Boolean;
begin
Result := not TNodeData(FromNode.Data).IsRecycleBin;
@@ -2162,12 +2142,11 @@ begin
end;
procedure TDriveViewInt.RebuildTree;
var
Drive: string;
begin
for Drive in FDriveStatus.Keys do
var Iterator := 0;
var DriveStatus: TDriveStatus;
while GetNextDriveStatus(Iterator, nil, DriveStatus) do
begin
var DriveStatus := GetDriveStatus(Drive);
if Assigned(DriveStatus.RootNode) and DriveStatus.Scanned then
ValidateDirectory(DriveStatus.RootNode);
end;
@@ -2341,28 +2320,29 @@ begin
end;
procedure TDriveViewInt.ChangeTimerOnTimer(Sender: TObject);
var
DriveStatusPair: TDriveStatusPair;
begin
if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
begin
for DriveStatusPair in FDriveStatus do
var Iterator := 0;
var Drive: string;
var DriveStatus: TDriveStatus;
while GetNextDriveStatus(Iterator, @Drive, DriveStatus) do
begin
if DriveStatusPair.Value.ChangeTimer = Sender then
if DriveStatus.ChangeTimer = Sender then
begin
// Messages are processed during ValidateDirectory, so we may detect another change while
// updating the directory. Prevent the recursion.
// But retry the update afterwards (by reenabling the timer in ChangeDetected)
SuspendChangeTimer;
try
var ChangeTimer := DriveStatusPair.Value.ChangeTimer;
var ChangeTimer := DriveStatus.ChangeTimer;
ChangeTimer.Interval := 0;
ChangeTimer.Enabled := False;
if Assigned(DriveStatusPair.Value.RootNode) then
if Assigned(DriveStatus.RootNode) then
begin
// Check also collapsed (invisible) subdirectories:
ValidateDirectory(DriveStatusPair.Value.RootNode);
ValidateDirectory(DriveStatus.RootNode);
end;
finally
ResumeChangeTimer;
@@ -2432,24 +2412,23 @@ begin
end;
procedure TDriveViewInt.StartAllWatchThreads;
var
DriveStatusPair: TDriveStatusPair;
Drive: string;
begin
if (csDesigning in ComponentState) or (not FWatchDirectory) then
Exit;
for DriveStatusPair in FDriveStatus do
var Iterator := 0;
var Drive: string;
var DriveStatus: TDriveStatus;
while GetNextDriveStatus(Iterator, @Drive, DriveStatus) do
begin
var DriveStatus := DriveStatusPair.Value;
if DriveStatus.Scanned then
begin
if not Assigned(DriveStatus.DiscMonitor) then
CreateWatchThread(DriveStatusPair.Key);
CreateWatchThread(Drive);
if Assigned(DriveStatus.DiscMonitor) and (not DriveStatus.DiscMonitor.Active) then
begin
DriveStatus.DiscMonitor.Open;
SubscribeDriveNotifications(DriveStatusPair.Key);
SubscribeDriveNotifications(Drive);
end;
end;
end;
@@ -2465,15 +2444,14 @@ begin
end;
procedure TDriveViewInt.StopAllWatchThreads;
var
DriveStatusPair: TDriveStatusPair;
begin
if (csDesigning in ComponentState) or (not FWatchDirectory) then
Exit;
for DriveStatusPair in FDriveStatus do
var Iterator := 0;
var DriveStatus: TDriveStatus;
while GetNextDriveStatus(Iterator, nil, DriveStatus) do
begin
var DriveStatus := DriveStatusPair.Value;
if Assigned(DriveStatus.DiscMonitor) then
begin
DriveStatus.DiscMonitor.Close;

View File

@@ -83,7 +83,6 @@ type
TDriveInfoInt = class(TObject)
private
FData: TObjectDictionary<string, TDriveInfoRec>;
FNoDrives: DWORD;
FNoViewOnDrive: DWORD;
FDesktop: IShellFolder;
@@ -94,12 +93,9 @@ type
FHandlers: TList<TDriveNotificationEvent>;
FChangeNotify: ULONG;
function GetFolder(Folder: TSpecialFolder): PSpecialFolderRec;
procedure ReadDriveBasicStatus(Drive: string);
procedure ResetDrive(Drive: string);
procedure SetHonorDrivePolicy(Value: Integer);
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;
function ReadDriveMask(Reg: TRegistry; ValueName: string): DWORD;
@@ -107,18 +103,25 @@ type
procedure CancelDriveRefresh;
procedure InternalWndProc(var Msg: TMessage);
procedure InvokeHandlers(DriveNotification: TDriveNotification; Drive: string);
procedure UpdateDriveNotifications(Drive: string);
procedure UpdateDrivesNotifications;
procedure ProcessThreadResults;
procedure ReadAsynchronous;
procedure DoReadDriveStatus(Drive: string; Flags: Integer);
procedure DriveRemoving(Drive: string);
protected
constructor Create;
procedure DriveRemoving(Drive: string); overload;
procedure ResetDrive(Drive: string);
procedure DoReadDriveStatus(Drive: string; Flags: Integer);
procedure ReadDriveBasicStatus(Drive: string);
procedure UpdateDriveNotifications(Drive: string);
function AddDrive(Drive: string): TDriveInfoRec; virtual; abstract;
function GetInternal(Drive: string): TDriveInfoRec; virtual; abstract;
procedure DriveRemoving(DeviceHandle: THandle); overload; virtual; abstract;
procedure UpdateDrivesNotifications; virtual; abstract;
public
function Get(Drive: string): TDriveInfoRec;
function Get(Drive: string): TDriveInfoRec; virtual; abstract;
property SpecialFolder[Folder: TSpecialFolder]: PSpecialFolderRec read GetFolder;
procedure NeedData;
@@ -228,7 +231,6 @@ begin
FHonorDrivePolicy := 1;
FUseABDrives := True;
FLoaded := False;
FData := TObjectDictionary<string, TDriveInfoRec>.Create([doOwnsValues]);
FHandlers := TList<TDriveNotificationEvent>.Create;
FChangeNotify := 0;
end; {TDriveInfoInt.Create}
@@ -237,7 +239,6 @@ destructor TDriveInfoInt.Destroy;
begin
Assert(FHandlers.Count = 0);
FHandlers.Free;
FData.Free;
inherited;
end; {TDriveInfoInt.Destroy}
@@ -262,8 +263,7 @@ begin
for I := 1 to Length(ReadyDrives) do
begin
Drive := ReadyDrives[I];
Assert(FData.ContainsKey(Drive));
FData[Drive].DriveReady := True;
GetInternal(Drive).DriveReady := True;
UpdateDriveNotifications(Drive);
AppLog(Format('Drive "%s" is ready', [Drive]))
end;
@@ -419,8 +419,7 @@ var
InaccessibleByDrivePolicy, HiddenByDrivePolicy: Boolean;
DriveBitMask: Integer;
begin
Assert(FData.ContainsKey(Drive));
with FData[Drive] do
with GetInternal(Drive) do
begin
DriveType := Windows.GetDriveType(PChar(GetDriveRoot(Drive)));
if IsRealDrive(Drive) then DriveBitMask := GetDriveBitMask(Drive)
@@ -439,7 +438,7 @@ end;
procedure TDriveInfoInt.ResetDrive(Drive: string);
begin
with FData[Drive] do
with GetInternal(Drive) do
begin
DriveReady := False;
DisplayName := '';
@@ -515,7 +514,7 @@ begin
for Drive := FirstDrive to LastDrive do
begin
// Not using Get as that would recurse into Load
if FData[Drive].Valid then
if GetInternal(Drive).Valid then
Drives := Drives + Drive;
end;
TDriveInfoThread.Create(Drives);
@@ -527,17 +526,6 @@ begin
end;
end;
function TDriveInfoInt.AddDrive(Drive: string): TDriveInfoRec;
begin
Result := TDriveInfoRec.Create;
FData.Add(Drive, Result);
ResetDrive(Drive);
if IsFixedDrive(Drive) or (not IsRealDrive(Drive)) then // not floppy
DoReadDriveStatus(Drive, dsSynchronous)
else
ReadDriveBasicStatus(Drive);
end;
function TDriveInfoInt.GetImageIndex(Drive: string): Integer;
begin
NeedData;
@@ -585,21 +573,6 @@ begin
if IsRealDrive(Result) then Result := Result + ':';
end;
function TDriveInfoInt.Get(Drive: string): TDriveInfoRec;
begin
NeedData;
// We might want to wait for FReadyDrives to be empty before returning
// (or even better do that only in DriveReady getter)
if not FData.TryGetValue(Drive, Result) then
begin
Assert(IsUncPath(Drive));
Result := AddDrive(Drive);
DriveRefresh;
end;
end; {TDriveInfoInt.GetData}
procedure TDriveInfoInt.ReadDriveStatus(Drive: string; Flags: Integer);
begin
// Among other, this makes sure the pending drive-ready status from the background thread are collected,
@@ -629,8 +602,7 @@ begin
DriveRoot := GetDriveRoot(Drive);
// When this method is called, the entry always exists already
Assert(FData.ContainsKey(Drive));
DriveInfoRec := FData[Drive];
DriveInfoRec := GetInternal(Drive);
with DriveInfoRec do
begin
Init := True;
@@ -743,12 +715,11 @@ procedure TDriveInfoInt.OverrideDrivePolicy(Drive: string);
var
Mask: DWORD;
begin
Assert(FData.ContainsKey(Drive));
Assert(FData[Drive].ValidButHiddenByDrivePolicy);
Assert(GetInternal(Drive).ValidButHiddenByDrivePolicy);
Mask := (not GetDriveBitMask(Drive));
FNoDrives := FNoDrives and Mask;
ReadDriveStatus(Drive, dsAll);
Assert(FData[Drive].Valid);
Assert(GetInternal(Drive).Valid);
DriveRefresh;
end;
@@ -854,7 +825,6 @@ var
PPIDL: PPItemIDList;
Event: LONG;
Lock: THandle;
DrivePair: TPair<string, TDriveInfoRec>;
begin
with Msg do
begin
@@ -908,11 +878,7 @@ begin
if DeviceType = DBT_DEVTYP_HANDLE then
begin
DeviceHandle := PDEV_BROADCAST_HANDLE(lParam)^.dbch_handle;
for DrivePair in FData do
if DrivePair.Value.DriveHandle = DeviceHandle then
begin
DriveRemoving(DrivePair.Key);
end;
DriveRemoving(DeviceHandle);
end;
end;
end
@@ -941,7 +907,7 @@ end;
procedure TDriveInfoInt.DriveRemoving(Drive: string);
begin
FData[Drive].DriveReady := False;
GetInternal(Drive).DriveReady := False;
UpdateDriveNotifications(Drive);
AppLog(Format('Removing drive "%s"', [Drive]));
InvokeHandlers(dnRemoving, Drive);
@@ -975,7 +941,7 @@ begin
if IsFixedDrive(Drive) then
begin
// Not using Get to avoid recursion
DriveInfoRec := FData[Drive];
DriveInfoRec := GetInternal(Drive);
NeedNotifications :=
(FHandlers.Count > 0) and
(DriveInfoRec.DriveType <> DRIVE_REMOTE) and
@@ -1024,14 +990,6 @@ begin
end;
end;
procedure TDriveInfoInt.UpdateDrivesNotifications;
var
Drive: string;
begin
for Drive in FData.Keys do
UpdateDriveNotifications(Drive);
end;
procedure TDriveInfoInt.DriveRefresh;
begin
InvokeHandlers(dnRefresh, '');