Skip to content

Commit c802626

Browse files
committed
improve registry check performance
GetGoogleDriveRegistryProvidersAsync: remove one of its loops, rename it, split it up, and add more logging. So we're only accessing the registry once, set `App.AppModel.GoogleDrivePath` in RemovableDrivesService, where we need that path first. Then GoogleDriveCloudDetector can read from it, since it needs the path second. Split up AddMyDriveToPathAndValidate.
1 parent 4ce65c8 commit c802626

File tree

2 files changed

+92
-120
lines changed

2 files changed

+92
-120
lines changed

src/Files.App/Services/Storage/StorageDevicesService.cs

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,20 @@ public IStorageDeviceWatcher CreateWatcher()
1919
public async IAsyncEnumerable<ILocatableFolder> GetDrivesAsync()
2020
{
2121
var list = DriveInfo.GetDrives();
22-
var googleDrivePath = App.AppModel.GoogleDrivePath;
2322
var pCloudDrivePath = App.AppModel.PCloudDrivePath;
2423

24+
var sw = Stopwatch.StartNew();
25+
var googleDrivePath = GoogleDriveCloudDetector.GetRegistryBasePath();
26+
sw.Stop();
2527
#if DEBUG
26-
Debug.WriteLine($"In RDS.GDA: googleDrivePath: {googleDrivePath}");
28+
Debug.WriteLine($"In RemovableDrivesService: Time elapsed for registry check: {sw.Elapsed}");
2729
#endif
30+
App.AppModel.GoogleDrivePath = googleDrivePath ?? string.Empty;
2831

2932
foreach (var drive in list)
3033
{
31-
var shouldSkip = false;
32-
var sw = Stopwatch.StartNew();
33-
await foreach (var cloudProvider in GoogleDriveCloudDetector.GetGoogleDriveProvidersFromRegistryAsync(false))
34-
{
35-
if (cloudProvider.SyncFolder.Equals(drive.Name))
36-
shouldSkip = true;
37-
}
38-
sw.Stop();
39-
40-
#if DEBUG
41-
Debug.WriteLine($"In RDS.GDA after registry check: Time elapsed for filter: {sw.Elapsed}");
42-
Debug.WriteLine($"In RDS.GDA: drive.Name: {drive.Name}");
43-
#endif
44-
45-
if (shouldSkip)
34+
// We don't want cloud drives to appear in a plain "Drives" section.
35+
if (drive.Name.Equals(googleDrivePath) || drive.Name.Equals(pCloudDrivePath))
4636
continue;
4737

4838
var res = await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(drive.Name).AsTask());
@@ -64,10 +54,6 @@ public async IAsyncEnumerable<ILocatableFolder> GetDrivesAsync()
6454
var label = DriveHelpers.GetExtendedDriveLabel(drive);
6555
var driveItem = await DriveItem.CreateFromPropertiesAsync(res.Result, drive.Name.TrimEnd('\\'), label, type, thumbnail);
6656

67-
// Don't add here because Google Drive is already displayed under cloud drives
68-
if (drive.Name == googleDrivePath || drive.Name == pCloudDrivePath)
69-
continue;
70-
7157
App.Logger.LogInformation($"Drive added: {driveItem.Path}, {driveItem.Type}");
7258

7359
yield return driveItem;

src/Files.App/Utils/Cloud/Detector/GoogleDriveCloudDetector.cs

Lines changed: 85 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace Files.App.Utils.Cloud
1212
{
1313
/// <summary>
14-
/// Provides an utility for Google Drive Cloud detection.
14+
/// Provides a utility for Google Drive Cloud detection.
1515
/// </summary>
1616
public sealed class GoogleDriveCloudDetector : AbstractCloudDetector
1717
{
@@ -55,9 +55,6 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
5555
continue;
5656
}
5757

58-
if ((long)reader["is_my_drive"] == 1)
59-
continue;
60-
6158
// By default, the path will be prefixed with "\\?\" (unless another app has explicitly changed it).
6259
// \\?\ indicates to Win32 that the filename may be longer than MAX_PATH (see MSDN).
6360
// Parts of .NET (e.g. the File class) don't handle this very well, so remove this prefix.
@@ -69,12 +66,9 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
6966
var folder = await StorageFolder.GetFolderFromPathAsync(path);
7067
string title = reader["title"]?.ToString() ?? folder.Name;
7168

72-
App.AppModel.GoogleDrivePath = path;
73-
7469
#if DEBUG
75-
Debug.WriteLine($"In GDCD in roots table: App.AppModel.GoogleDrivePath being set to: {path}");
76-
Debug.WriteLine("YIELD RETURNING from GoogleDriveCloudDetector#GetProviders (roots): ");
77-
Debug.WriteLine($"name=Google Drive ({title}); path={path}");
70+
Debug.WriteLine("YIELD RETURNING from `GoogleDriveCloudDetector.GetProviders()` (roots): ");
71+
Debug.WriteLine($"Name: Google Drive ({title}); SyncFolder: {path}");
7872
#endif
7973

8074
yield return new CloudProvider(CloudProviders.GoogleDrive)
@@ -84,6 +78,7 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
8478
};
8579
}
8680

81+
var iconFile = await GetGoogleDriveIconFileAsync();
8782
// Google virtual drive
8883
reader = cmdMedia.ExecuteReader();
8984

@@ -94,21 +89,13 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
9489
continue;
9590

9691
if (!AddMyDriveToPathAndValidate(ref path))
97-
{
98-
_logger.LogWarning($"Validation failed for {path} (media)");
9992
continue;
100-
}
10193

10294
var folder = await StorageFolder.GetFolderFromPathAsync(path);
10395
string title = reader["name"]?.ToString() ?? folder.Name;
10496

105-
App.AppModel.GoogleDrivePath = path;
106-
107-
var iconFile = await GetGoogleDriveIconFileAsync();
108-
10997
#if DEBUG
110-
Debug.WriteLine($"In GDCD in media table: App.AppModel.GoogleDrivePath being set to: {path}");
111-
Debug.WriteLine("YIELD RETURNING from GoogleDriveCloudDetector#GetProviders (media): ");
98+
Debug.WriteLine("YIELD RETURNING from `GoogleDriveCloudDetector.GetProviders` (media): ");
11299
Debug.WriteLine($"name={title}; path={path}");
113100
#endif
114101

@@ -126,19 +113,18 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
126113
await Inspect(database, "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY 1", "root_preferences db, all tables");
127114
#endif
128115

129-
await foreach (var provider in GetGoogleDriveProvidersFromRegistryAsync())
116+
var registryPath = App.AppModel.GoogleDrivePath;
117+
if (!AddMyDriveToPathAndValidate(ref registryPath))
118+
yield break;
119+
yield return new CloudProvider(CloudProviders.GoogleDrive)
130120
{
131-
132-
#if DEBUG
133-
Debug.WriteLine("YIELD RETURNING from GoogleDriveCloudDetector#GetProviders (registry): ");
134-
Debug.WriteLine($"name={provider.Name}; path={provider.SyncFolder}");
135-
#endif
136-
137-
yield return provider;
138-
}
121+
Name = "Google Drive",
122+
SyncFolder = registryPath,
123+
IconData = iconFile is not null ? await iconFile.ToByteArrayAsync() : null
124+
};
139125
}
140126

141-
private async Task Inspect(SqliteConnection database, string sqlCommand, string contentsOf)
127+
private static async Task Inspect(SqliteConnection database, string sqlCommand, string contentsOf)
142128
{
143129
await using var cmdTablesAll = new SqliteCommand(sqlCommand, database);
144130
var reader = await cmdTablesAll.ExecuteReaderAsync();
@@ -194,12 +180,12 @@ private async Task Inspect(SqliteConnection database, string sqlCommand, string
194180
return googleDriveRegValueJson;
195181
}
196182

197-
public static async IAsyncEnumerable<ICloudProvider> GetGoogleDriveProvidersFromRegistryAsync(bool addMyDriveToPath = true)
183+
public static string? GetRegistryBasePath()
198184
{
199185
var googleDriveRegValJson = GetGoogleDriveRegValJson();
200186

201187
if (googleDriveRegValJson is null)
202-
yield break;
188+
return null;
203189

204190
var googleDriveRegValJsonProperty = googleDriveRegValJson
205191
.RootElement.EnumerateObject()
@@ -210,46 +196,72 @@ public static async IAsyncEnumerable<ICloudProvider> GetGoogleDriveProvidersFrom
210196
if (googleDriveRegValJsonProperty.Value.ValueKind == JsonValueKind.Undefined)
211197
{
212198
_logger.LogWarning($"Root element of Google Drive registry value for value name '{_googleDriveRegValName}' was empty.");
213-
yield break;
199+
return null;
214200
}
215201

216202
#if DEBUG
217203
Debug.WriteLine("REGISTRY LOGGING");
218204
Debug.WriteLine(googleDriveRegValJsonProperty.ToString());
219205
#endif
220206

221-
foreach (var item in googleDriveRegValJsonProperty.Value.EnumerateArray())
207+
var item = googleDriveRegValJsonProperty.Value.EnumerateArray().FirstOrDefault();
208+
if (item.ValueKind == JsonValueKind.Undefined)
222209
{
223-
if (!item.TryGetProperty(_googleDriveRegValPropName, out var googleDriveRegValProp))
224-
continue;
210+
_logger.LogWarning($"Array in the root element of Google Drive registry value for value name '{_googleDriveRegValName}' was empty.");
211+
return null;
212+
}
225213

226-
if (!googleDriveRegValProp.TryGetProperty(_googleDriveRegValPropPropName, out var googleDriveRegValPropProp))
227-
continue;
214+
if (!item.TryGetProperty(_googleDriveRegValPropName, out var googleDriveRegValProp))
215+
{
216+
_logger.LogWarning($"First element in the Google Drive Registry Root Array did not have property named {_googleDriveRegValPropName}");
217+
return null;
218+
}
228219

229-
var path = googleDriveRegValPropProp.GetString();
230-
if (path is null)
231-
continue;
220+
if (!googleDriveRegValProp.TryGetProperty(_googleDriveRegValPropPropName, out var googleDriveRegValPropProp))
221+
{
222+
_logger.LogWarning($"Value from {_googleDriveRegValPropName} did not have property named {_googleDriveRegValPropPropName}");
223+
return null;
224+
}
232225

233-
if (!AddMyDriveToPathAndValidate(ref path, addMyDriveToPath))
234-
{
235-
_logger.LogWarning($"Validation failed for {path} (registry)");
236-
continue;
237-
}
226+
var path = googleDriveRegValPropProp.GetString();
227+
if (path is not null)
228+
return ConvertDriveLetterToPathAndValidate(ref path) ? path : null;
238229

239-
App.AppModel.GoogleDrivePath = path;
240-
#if DEBUG
241-
Debug.WriteLine($"In GDCD in registry: App.AppModel.GoogleDrivePath being set to: {path}");
242-
#endif
230+
_logger.LogWarning($"Could not get string from value from {_googleDriveRegValPropPropName}");
231+
return null;
232+
}
243233

244-
var iconFile = await GetGoogleDriveIconFileAsync();
234+
/// <summary>
235+
/// If Google Drive is mounted as a drive, then the path found in the registry will be
236+
/// *just* the drive letter (e.g. just "G" as opposed to "G:\"), and therefore must be
237+
/// reformatted as a valid path.
238+
/// </summary>
239+
private static bool ConvertDriveLetterToPathAndValidate(ref string path)
240+
{
241+
if (path.Length > 1)
242+
return ValidatePath(path);
245243

246-
yield return new CloudProvider(CloudProviders.GoogleDrive)
247-
{
248-
Name = "Google Drive",
249-
SyncFolder = path,
250-
IconData = iconFile is not null ? await iconFile.ToByteArrayAsync() : null,
251-
};
244+
DriveInfo driveInfo;
245+
try
246+
{
247+
driveInfo = new DriveInfo(path);
248+
}
249+
catch (ArgumentException e)
250+
{
251+
_logger.LogWarning(e, $"Could not resolve drive letter '{path}' to a valid drive.");
252+
return false;
252253
}
254+
255+
path = driveInfo.RootDirectory.Name;
256+
return true;
257+
}
258+
259+
private static bool ValidatePath(string path)
260+
{
261+
if (Directory.Exists(path))
262+
return true;
263+
_logger.LogWarning($"Invalid path: {path}");
264+
return false;
253265
}
254266

255267
private static async Task<StorageFile?> GetGoogleDriveIconFileAsync()
@@ -264,56 +276,30 @@ public static async IAsyncEnumerable<ICloudProvider> GetGoogleDriveProvidersFrom
264276
return await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(iconPath).AsTask());
265277
}
266278

267-
private static bool AddMyDriveToPathAndValidate(ref string path, bool addMyDrive = true)
268-
{
269-
// If Google Drive is mounted as a drive, then the path found in the registry will be
270-
// *just* the drive letter (e.g. just "G" as opposed to "G:\"), and therefore must be
271-
// reformatted as a valid path.
272-
if (path.Length == 1)
273-
{
274-
DriveInfo temp;
275-
try
276-
{
277-
temp = new DriveInfo(path);
278-
}
279-
catch (ArgumentException e)
280-
{
281-
_logger.LogWarning(e, $"Could not resolve drive letter '{path}' to a valid drive.");
282-
return false;
283-
}
284-
285-
path = temp.RootDirectory.Name;
286-
}
287-
288-
if (addMyDrive)
289-
{
290-
// If `path` contains a shortcut named "My Drive", store its target in `shellFolderBaseFirst`.
291-
// This happens when "My Drive syncing options" is set to "Mirror files".
292-
// TODO: Avoid to use Vanara (#15000)
293-
using var shellFolderBase = ShellFolderExtensions.GetShellItemFromPathOrPIDL(path) as ShellFolder;
294-
var shellFolderBaseFirst = Environment.ExpandEnvironmentVariables((
295-
shellFolderBase?.FirstOrDefault(si =>
296-
si.Name?.Equals("My Drive") ?? false) as ShellLink)?.TargetPath
297-
?? string.Empty);
279+
private static bool AddMyDriveToPathAndValidate(ref string path)
280+
{
281+
// If `path` contains a shortcut named "My Drive", store its target in `shellFolderBaseFirst`.
282+
// This happens when "My Drive syncing options" is set to "Mirror files".
283+
// TODO: Avoid to use Vanara (#15000)
284+
using var rootFolder = ShellFolderExtensions.GetShellItemFromPathOrPIDL(path) as ShellFolder;
285+
var myDriveFolder = Environment.ExpandEnvironmentVariables((
286+
rootFolder?.FirstOrDefault(si =>
287+
si.Name?.Equals("My Drive") ?? false) as ShellLink)?.TargetPath
288+
?? string.Empty);
298289

299290
#if DEBUG
300-
Debug.WriteLine("INVALID PATHS LOGGER");
301-
shellFolderBase?.ForEach(si => Debug.WriteLine(si.Name));
291+
Debug.WriteLine("SHELL FOLDER LOGGING");
292+
rootFolder?.ForEach(si => Debug.WriteLine(si.Name));
302293
#endif
303294

304-
if (!string.IsNullOrEmpty(shellFolderBaseFirst))
305-
{
306-
path = shellFolderBaseFirst;
307-
return true;
308-
}
309-
310-
path = Path.Combine(path, "My Drive");
295+
if (!string.IsNullOrEmpty(myDriveFolder))
296+
{
297+
path = myDriveFolder;
298+
return true;
311299
}
312300

313-
if (Directory.Exists(path))
314-
return true;
315-
_logger.LogWarning($"Invalid Google Drive mount point path: {path}");
316-
return false;
301+
path = Path.Combine(path, "My Drive");
302+
return ValidatePath(path);
317303
}
318304
}
319305
}

0 commit comments

Comments
 (0)