Skip to content

Commit 221024f

Browse files
committed
Sync with main
2 parents f828cc3 + d35cb26 commit 221024f

File tree

106 files changed

+8024
-2279
lines changed

Some content is hidden

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

106 files changed

+8024
-2279
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/DeviceWatcher.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ namespace FilesFullTrust
1010
public class DeviceWatcher : IDisposable
1111
{
1212
private ManagementEventWatcher insertWatcher, removeWatcher, modifyWatcher;
13-
private NamedPipeServerStream connection;
13+
private PipeStream connection;
1414

1515
private const string WpdGuid = "{6ac27878-a6fa-4155-ba85-f98f491d4f33}";
1616

17-
public DeviceWatcher(NamedPipeServerStream connection)
17+
public DeviceWatcher(PipeStream connection)
1818
{
1919
this.connection = connection;
2020
}

Files.Launcher/Files.Launcher.csproj

Lines changed: 5 additions & 3 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" />
@@ -145,7 +147,7 @@
145147
</ItemGroup>
146148
<ItemGroup>
147149
<PackageReference Include="Microsoft.Windows.SDK.Contracts">
148-
<Version>10.0.22000.194</Version>
150+
<Version>10.0.22000.196</Version>
149151
</PackageReference>
150152
<PackageReference Include="Newtonsoft.Json">
151153
<Version>13.0.1</Version>
@@ -160,10 +162,10 @@
160162
<Version>3.0.7</Version>
161163
</PackageReference>
162164
<PackageReference Include="Vanara.PInvoke.Mpr">
163-
<Version>3.3.13</Version>
165+
<Version>3.3.14</Version>
164166
</PackageReference>
165167
<PackageReference Include="Vanara.Windows.Shell">
166-
<Version>3.3.13</Version>
168+
<Version>3.3.14</Version>
167169
</PackageReference>
168170
</ItemGroup>
169171
<ItemGroup>
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/ApplicationLaunchHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ namespace FilesFullTrust.MessageHandlers
1616
{
1717
public class ApplicationLaunchHandler : IMessageHandler
1818
{
19-
public void Initialize(NamedPipeServerStream connection)
19+
public void Initialize(PipeStream connection)
2020
{
2121
}
2222

23-
public Task ParseArgumentsAsync(NamedPipeServerStream connection, Dictionary<string, object> message, string arguments)
23+
public Task ParseArgumentsAsync(PipeStream connection, Dictionary<string, object> message, string arguments)
2424
{
2525
switch (arguments)
2626
{

0 commit comments

Comments
 (0)