Skip to content

Commit d35cb26

Browse files
authored
Move registry usage to FTP (#6657)
1 parent 0e7528f commit d35cb26

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+448
-430
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
##
44
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
55

6+
# Custom files
7+
CustomOpenDialog*.dll
8+
CustomSaveDialog*.dll
9+
610
# User-specific files
711
*.suo
812
*.user

Files/Filesystem/Cloud/CloudProvider.cs renamed to Common/CloudProvider.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
using Files.Enums;
2-
using System;
1+
using System;
32

4-
namespace Files.Filesystem.Cloud
3+
namespace Files.Common
54
{
65
public class CloudProvider : IEquatable<CloudProvider>
76
{
@@ -30,4 +29,16 @@ public bool Equals(CloudProvider other)
3029
return other != null && other.ID == ID && other.SyncFolder == SyncFolder;
3130
}
3231
}
32+
33+
public enum CloudProviders
34+
{
35+
OneDrive,
36+
OneDriveCommercial,
37+
Mega,
38+
GoogleDrive,
39+
DropBox,
40+
AppleCloud,
41+
AmazonDrive,
42+
Box
43+
}
3344
}

Common/ShellNewEntry.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Files.Common
2+
{
3+
public class ShellNewEntry
4+
{
5+
public string Extension { get; set; }
6+
public string Name { get; set; }
7+
public string Command { get; set; }
8+
public string IconBase64 { get; set; }
9+
public byte[] Data { get; set; }
10+
public string Template { get; set; }
11+
}
12+
}
Binary file not shown.
Binary file not shown.

Files.Launcher/ContextMenu.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,41 @@ private static ContextMenu GetContextMenuForFiles(ShellItem[] shellItems, Shell3
122122
return contextMenu;
123123
}
124124

125+
public static ContextMenu GetContextMenuForFolder(string folderPath, Shell32.CMF flags, Func<string, bool> itemFilter = null)
126+
{
127+
ShellFolder fsi = null;
128+
try
129+
{
130+
fsi = new ShellFolder(folderPath);
131+
return GetContextMenuForFolder(fsi, flags, itemFilter);
132+
}
133+
catch (Exception ex) when (ex is ArgumentException || ex is FileNotFoundException)
134+
{
135+
// Return empty context menu
136+
return null;
137+
}
138+
finally
139+
{
140+
fsi?.Dispose();
141+
}
142+
}
143+
144+
private static ContextMenu GetContextMenuForFolder(ShellFolder shellFolder, Shell32.CMF flags, Func<string, bool> itemFilter = null)
145+
{
146+
if (shellFolder == null)
147+
{
148+
return null;
149+
}
150+
151+
var sv = shellFolder.GetViewObject<Shell32.IShellView>(null);
152+
Shell32.IContextMenu menu = sv.GetItemObject<Shell32.IContextMenu>(Shell32.SVGIO.SVGIO_BACKGROUND);
153+
var hMenu = User32.CreatePopupMenu();
154+
menu.QueryContextMenu(hMenu, 0, 1, 0x7FFF, flags);
155+
var contextMenu = new ContextMenu(menu, hMenu, new[] { shellFolder.ParsingName });
156+
ContextMenu.EnumMenuItems(menu, hMenu, contextMenu.Items, itemFilter);
157+
return contextMenu;
158+
}
159+
125160
#endregion FactoryMethods
126161

127162
private static void EnumMenuItems(

Files.Launcher/Files.Launcher.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,10 @@
118118
</ItemGroup>
119119
<ItemGroup>
120120
<Compile Include="DeviceWatcher.cs" />
121+
<Compile Include="Helpers\CloudDrivesDetector.cs" />
121122
<Compile Include="Helpers\DisposableDictionary.cs" />
122123
<Compile Include="Helpers\ShellFolderHelpers.cs" />
124+
<Compile Include="Helpers\ShellNewMenuHelper.cs" />
123125
<Compile Include="Helpers\ThreadWithMessageQueue.cs" />
124126
<Compile Include="MessageHandlers\FileTagsHandler.cs" />
125127
<Compile Include="MessageHandlers\LibrariesHandler.cs" />
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using Files.Common;
2+
using Microsoft.Win32;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
9+
namespace FilesFullTrust.Helpers
10+
{
11+
public class CloudDrivesDetector
12+
{
13+
public static async Task<List<CloudProvider>> DetectCloudDrives()
14+
{
15+
var tasks = new List<Task<List<CloudProvider>>>()
16+
{
17+
Extensions.IgnoreExceptions(DetectOneDrive, Program.Logger),
18+
Extensions.IgnoreExceptions(DetectSharepoint, Program.Logger),
19+
Extensions.IgnoreExceptions(DetectGenericCloudDrive, Program.Logger)
20+
};
21+
22+
await Task.WhenAll(tasks);
23+
24+
return tasks.Where(o => o.Result != null).SelectMany(o => o.Result).OrderBy(o => o.ID.ToString()).ThenBy(o => o.Name).Distinct().ToList();
25+
}
26+
27+
private static async Task<List<CloudProvider>> DetectGenericCloudDrive()
28+
{
29+
var results = new List<CloudProvider>();
30+
using var clsidKey = Registry.ClassesRoot.OpenSubKey(@"CLSID");
31+
foreach (var subKeyName in clsidKey.GetSubKeyNames())
32+
{
33+
using var subKey = Extensions.IgnoreExceptions(() => clsidKey.OpenSubKey(subKeyName));
34+
if (subKey != null && (int?)subKey.GetValue("System.IsPinnedToNameSpaceTree") == 1)
35+
{
36+
using var namespaceKey = Registry.CurrentUser.OpenSubKey($@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{subKeyName}");
37+
var driveType = (string)namespaceKey?.GetValue("");
38+
if (driveType == null)
39+
{
40+
continue;
41+
}
42+
43+
using var bagKey = subKey.OpenSubKey(@"Instance\InitPropertyBag");
44+
var syncedFolder = (string)bagKey?.GetValue("TargetFolderPath");
45+
if (syncedFolder == null)
46+
{
47+
continue;
48+
}
49+
50+
// Also works for OneDrive, Box, Amazon Drive, iCloudDrive, Dropbox
51+
CloudProviders? driveID = driveType switch
52+
{
53+
"MEGA" => CloudProviders.Mega,
54+
"Amazon Drive" => CloudProviders.AmazonDrive,
55+
_ => null
56+
};
57+
if (driveID == null)
58+
{
59+
continue;
60+
}
61+
62+
results.Add(new CloudProvider()
63+
{
64+
ID = driveID.Value,
65+
Name = driveID switch
66+
{
67+
CloudProviders.Mega => $"MEGA ({Path.GetFileName(syncedFolder.TrimEnd('\\'))})",
68+
CloudProviders.AmazonDrive => $"Amazon Drive",
69+
_ => null
70+
},
71+
SyncFolder = syncedFolder
72+
});
73+
}
74+
}
75+
return await Task.FromResult(results);
76+
}
77+
78+
private static async Task<List<CloudProvider>> DetectOneDrive()
79+
{
80+
using var oneDriveAccountsKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\OneDrive\Accounts");
81+
82+
if (oneDriveAccountsKey == null)
83+
{
84+
return null;
85+
}
86+
87+
var oneDriveAccounts = new List<CloudProvider>();
88+
foreach (var account in oneDriveAccountsKey.GetSubKeyNames())
89+
{
90+
var accountKeyName = @$"{oneDriveAccountsKey.Name}\{account}";
91+
var displayName = (string)Registry.GetValue(accountKeyName, "DisplayName", null);
92+
var userFolder = (string)Registry.GetValue(accountKeyName, "UserFolder", null);
93+
var accountName = string.IsNullOrWhiteSpace(displayName) ? "OneDrive" : $"OneDrive - {displayName}";
94+
if (!string.IsNullOrWhiteSpace(userFolder) && !oneDriveAccounts.Any(x => x.Name == accountName))
95+
{
96+
oneDriveAccounts.Add(new CloudProvider()
97+
{
98+
ID = CloudProviders.OneDrive,
99+
Name = accountName,
100+
SyncFolder = userFolder
101+
});
102+
}
103+
}
104+
return await Task.FromResult(oneDriveAccounts);
105+
}
106+
107+
private static async Task<List<CloudProvider>> DetectSharepoint()
108+
{
109+
using var oneDriveAccountsKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\OneDrive\Accounts");
110+
111+
if (oneDriveAccountsKey == null)
112+
{
113+
return null;
114+
}
115+
116+
var sharepointAccounts = new List<CloudProvider>();
117+
118+
foreach (var account in oneDriveAccountsKey.GetSubKeyNames())
119+
{
120+
var accountKeyName = @$"{oneDriveAccountsKey.Name}\{account}";
121+
var displayName = (string)Registry.GetValue(accountKeyName, "DisplayName", null);
122+
var userFolderToExcludeFromResults = (string)Registry.GetValue(accountKeyName, "UserFolder", null);
123+
var accountName = string.IsNullOrWhiteSpace(displayName) ? "SharePoint" : $"SharePoint - {displayName}";
124+
125+
var sharePointSyncFolders = new List<string>();
126+
var mountPointKeyName = @$"SOFTWARE\Microsoft\OneDrive\Accounts\{account}\ScopeIdToMountPointPathCache";
127+
using (var mountPointsKey = Registry.CurrentUser.OpenSubKey(mountPointKeyName))
128+
{
129+
if (mountPointsKey == null)
130+
{
131+
continue;
132+
}
133+
134+
var valueNames = mountPointsKey.GetValueNames();
135+
foreach (var valueName in valueNames)
136+
{
137+
var value = (string)Registry.GetValue(@$"HKEY_CURRENT_USER\{mountPointKeyName}", valueName, null);
138+
if (!string.Equals(value, userFolderToExcludeFromResults, StringComparison.OrdinalIgnoreCase))
139+
{
140+
sharePointSyncFolders.Add(value);
141+
}
142+
}
143+
}
144+
145+
foreach (var sharePointSyncFolder in sharePointSyncFolders.OrderBy(o => o))
146+
{
147+
var parentFolder = Directory.GetParent(sharePointSyncFolder)?.FullName ?? string.Empty;
148+
if (!sharepointAccounts.Any(acc => string.Equals(acc.Name, accountName, StringComparison.OrdinalIgnoreCase)) && !string.IsNullOrWhiteSpace(parentFolder))
149+
{
150+
sharepointAccounts.Add(new CloudProvider()
151+
{
152+
ID = CloudProviders.OneDriveCommercial,
153+
Name = accountName,
154+
SyncFolder = parentFolder
155+
});
156+
}
157+
}
158+
}
159+
160+
return await Task.FromResult(sharepointAccounts);
161+
}
162+
}
163+
}

Files/Helpers/RegistryHelper.cs renamed to Files.Launcher/Helpers/ShellNewMenuHelper.cs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using Files.Common;
2-
using Files.DataModels;
3-
using Files.Filesystem;
42
using Microsoft.Win32;
53
using System;
64
using System.Collections.Generic;
@@ -11,9 +9,9 @@
119
using System.Threading.Tasks;
1210
using Windows.Storage;
1311

14-
namespace Files.Helpers
12+
namespace FilesFullTrust.Helpers
1513
{
16-
public static class RegistryHelper
14+
public static class ShellNewMenuHelper
1715
{
1816
public static async Task<List<ShellNewEntry>> GetNewContextMenuEntries()
1917
{
@@ -97,21 +95,28 @@ private static async Task<ShellNewEntry> ParseShellNewRegistryEntry(RegistryKey
9795
}
9896
}
9997

100-
var sampleFile = await FilesystemTasks.Wrap(() => ApplicationData.Current.LocalFolder.CreateFolderAsync("extensions", CreationCollisionOption.OpenIfExists).AsTask())
101-
.OnSuccess(t => t.CreateFileAsync("file" + extension, CreationCollisionOption.OpenIfExists).AsTask());
98+
var folder = await Extensions.IgnoreExceptions(() => ApplicationData.Current.LocalFolder.CreateFolderAsync("extensions", CreationCollisionOption.OpenIfExists).AsTask());
99+
var sampleFile = folder != null ? await Extensions.IgnoreExceptions(() => folder.CreateFileAsync("file" + extension, CreationCollisionOption.OpenIfExists).AsTask()) : null;
102100

103-
var displayType = sampleFile ? sampleFile.Result.DisplayType : string.Format("{0} {1}", "file", extension);
104-
var thumbnail = sampleFile ? await FilesystemTasks.Wrap(() => sampleFile.Result.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.ListView, 24, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale).AsTask()) : null;
101+
var displayType = sampleFile != null ? sampleFile.DisplayType : string.Format("{0} {1}", "file", extension);
102+
var thumbnail = sampleFile != null ? await Extensions.IgnoreExceptions(() => sampleFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.ListView, 24, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale).AsTask()) : null;
103+
104+
string iconString = null;
105+
if (thumbnail != null)
106+
{
107+
var readStream = thumbnail.AsStreamForRead();
108+
var bitmapData = new byte[readStream.Length];
109+
await readStream.ReadAsync(bitmapData, 0, bitmapData.Length);
110+
iconString = Convert.ToBase64String(bitmapData, 0, bitmapData.Length);
111+
}
105112

106113
var entry = new ShellNewEntry()
107114
{
108115
Extension = extension,
109116
Template = fileName,
110117
Name = displayType,
111118
Command = (string)key.GetValue("Command"),
112-
//Name = (string)key.GetValue("ItemName"),
113-
//IconPath = (string)key.GetValue("IconPath"),
114-
Icon = thumbnail?.Result,
119+
IconBase64 = iconString,
115120
Data = data
116121
};
117122

Files.Launcher/MessageHandlers/ContextMenuHandler.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using System.Threading.Tasks;
1010
using Vanara.PInvoke;
1111
using Windows.Foundation.Collections;
12-
using Windows.Storage;
1312

1413
namespace FilesFullTrust.MessageHandlers
1514
{
@@ -37,7 +36,6 @@ public async Task ParseArgumentsAsync(PipeStream connection, Dictionary<string,
3736
var cMenuLoad = await loadThreadWithMessageQueue.PostMessageAsync<ContextMenu>(message);
3837
contextMenuResponse.Add("Handle", handleTable.AddValue(loadThreadWithMessageQueue));
3938
contextMenuResponse.Add("ContextMenu", JsonConvert.SerializeObject(cMenuLoad));
40-
var serializedCm = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(contextMenuResponse));
4139
await Win32API.SendMessageAsync(connection, contextMenuResponse, message.Get("RequestID", (string)null));
4240
break;
4341

@@ -65,6 +63,17 @@ public async Task ParseArgumentsAsync(PipeStream connection, Dictionary<string,
6563
await Win32API.SendMessageAsync(connection, new ValueSet() { { "Success", result } }, message.Get("RequestID", (string)null));
6664
}
6765
break;
66+
67+
case "GetNewContextMenuEntries":
68+
var entries = await Extensions.IgnoreExceptions(() => ShellNewMenuHelper.GetNewContextMenuEntries(), Program.Logger);
69+
await Win32API.SendMessageAsync(connection, new ValueSet() { { "Entries", JsonConvert.SerializeObject(entries) } }, message.Get("RequestID", (string)null));
70+
break;
71+
72+
case "GetNewContextMenuEntryForType":
73+
var fileExtension = (string)message["extension"];
74+
var entry = await Extensions.IgnoreExceptions(() => ShellNewMenuHelper.GetNewContextMenuEntryForType(fileExtension), Program.Logger);
75+
await Win32API.SendMessageAsync(connection, new ValueSet() { { "Entry", JsonConvert.SerializeObject(entry) } }, message.Get("RequestID", (string)null));
76+
break;
6877
}
6978
}
7079

0 commit comments

Comments
 (0)