Skip to content

Commit fe684bc

Browse files
authored
Show network shortcuts on the sidebar (#7185)
1 parent 03d5fb2 commit fe684bc

File tree

9 files changed

+109
-58
lines changed

9 files changed

+109
-58
lines changed

Common/ShellLinkItem.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace Files.Common
2+
{
3+
public class ShellLinkItem : ShellFileItem
4+
{
5+
public string TargetPath;
6+
public string Arguments;
7+
public string WorkingDirectory;
8+
public bool RunAsAdmin;
9+
10+
public ShellLinkItem()
11+
{
12+
}
13+
14+
public ShellLinkItem(ShellFileItem baseItem)
15+
{
16+
this.RecyclePath = baseItem.RecyclePath;
17+
this.FileName = baseItem.FileName;
18+
this.FilePath = baseItem.FilePath;
19+
this.RecycleDate = baseItem.RecycleDate;
20+
this.ModifiedDate = baseItem.ModifiedDate;
21+
this.CreatedDate = baseItem.CreatedDate;
22+
this.FileSize = baseItem.FileSize;
23+
this.FileSizeBytes = baseItem.FileSizeBytes;
24+
this.FileType = baseItem.FileType;
25+
}
26+
}
27+
}

Files.Launcher/Helpers/ShellFolderHelpers.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public static ShellFileItem GetShellFileItem(ShellItem folderItem)
4444
folderItem.Properties.TryGetValue<string>(
4545
Ole32.PROPERTYKEY.System.ItemNameDisplay, out var fileName);
4646
fileName ??= Path.GetFileName(folderItem.Name); // Original file name
47-
string filePath = folderItem.Name; // Original file path + name (recycle bin only)
47+
string filePath = Path.Combine(Path.GetDirectoryName(parsingPath), folderItem.Name); // In recycle bin "Name" contains original file path + name
4848
if (!isFolder && !string.IsNullOrEmpty(parsingPath) && Path.GetExtension(parsingPath) is string realExtension && !string.IsNullOrEmpty(realExtension))
4949
{
5050
if (!string.IsNullOrEmpty(fileName) && !fileName.EndsWith(realExtension, StringComparison.OrdinalIgnoreCase))
@@ -72,5 +72,25 @@ public static ShellFileItem GetShellFileItem(ShellItem folderItem)
7272
Ole32.PROPERTYKEY.System.ItemTypeText, out var fileType);
7373
return new ShellFileItem(isFolder, parsingPath, fileName, filePath, recycleDate, modifiedDate, createdDate, fileSize, fileSizeBytes ?? 0, fileType);
7474
}
75+
76+
public static ShellLinkItem GetShellLinkItem(ShellLink linkItem)
77+
{
78+
if (linkItem == null)
79+
{
80+
return null;
81+
}
82+
var baseItem = GetShellFileItem(linkItem);
83+
if (baseItem == null)
84+
{
85+
return null;
86+
}
87+
var link = new ShellLinkItem(baseItem);
88+
link.IsFolder = !string.IsNullOrEmpty(link.TargetPath) && linkItem.Target.IsFolder;
89+
link.RunAsAdmin = linkItem.RunAsAdministrator;
90+
link.Arguments = linkItem.Arguments;
91+
link.WorkingDirectory = linkItem.WorkingDirectory;
92+
link.TargetPath = linkItem.TargetPath;
93+
return link;
94+
}
7595
}
7696
}

Files.Launcher/MessageHandlers/FileOperationsHandler.cs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -507,11 +507,7 @@ await Win32API.StartSTATask(() =>
507507
using var link = new ShellLink(linkPath, LinkResolution.NoUIWithMsgPump, null, TimeSpan.FromMilliseconds(100));
508508
await Win32API.SendMessageAsync(connection, new ValueSet()
509509
{
510-
{ "TargetPath", link.TargetPath },
511-
{ "Arguments", link.Arguments },
512-
{ "WorkingDirectory", link.WorkingDirectory },
513-
{ "RunAsAdmin", link.RunAsAdministrator },
514-
{ "IsFolder", !string.IsNullOrEmpty(link.TargetPath) && link.Target.IsFolder }
510+
{ "ShortcutInfo", JsonConvert.SerializeObject(ShellFolderExtensions.GetShellLinkItem(link)) }
515511
}, message.Get("RequestID", (string)null));
516512
}
517513
else if (linkPath.EndsWith(".url", StringComparison.OrdinalIgnoreCase))
@@ -525,11 +521,7 @@ await Win32API.StartSTATask(() =>
525521
});
526522
await Win32API.SendMessageAsync(connection, new ValueSet()
527523
{
528-
{ "TargetPath", linkUrl },
529-
{ "Arguments", null },
530-
{ "WorkingDirectory", null },
531-
{ "RunAsAdmin", false },
532-
{ "IsFolder", false }
524+
{ "ShortcutInfo", JsonConvert.SerializeObject(new ShellLinkItem() { TargetPath = linkUrl }) }
533525
}, message.Get("RequestID", (string)null));
534526
}
535527
}
@@ -539,11 +531,7 @@ await Win32API.StartSTATask(() =>
539531
Program.Logger.Warn(ex, ex.Message);
540532
await Win32API.SendMessageAsync(connection, new ValueSet()
541533
{
542-
{ "TargetPath", null },
543-
{ "Arguments", null },
544-
{ "WorkingDirectory", null },
545-
{ "RunAsAdmin", false },
546-
{ "IsFolder", false }
534+
{ "ShortcutInfo", JsonConvert.SerializeObject(null) }
547535
}, message.Get("RequestID", (string)null));
548536
}
549537
break;

Files.Launcher/MessageHandlers/NetworkDrivesHandler.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,31 @@ private async Task ParseNetworkDriveOperationAsync(PipeStream connection, Dictio
4545
case "GetNetworkLocations":
4646
var networkLocations = await Win32API.StartSTATask(() =>
4747
{
48-
var netl = new ValueSet();
48+
var locations = new List<ShellLinkItem>();
4949
using (var nethood = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_NetHood))
5050
{
51-
foreach (var link in nethood)
51+
foreach (var item in nethood)
5252
{
53-
var linkPath = (string)link.Properties["System.Link.TargetParsingPath"];
54-
if (linkPath != null)
53+
if (item is ShellLink link)
5554
{
56-
netl.Add(link.Name, linkPath);
55+
locations.Add(ShellFolderExtensions.GetShellLinkItem(link));
56+
}
57+
else
58+
{
59+
var linkPath = (string)item.Properties["System.Link.TargetParsingPath"];
60+
if (linkPath != null)
61+
{
62+
var linkItem = ShellFolderExtensions.GetShellFileItem(item);
63+
locations.Add(new ShellLinkItem(linkItem) { TargetPath = linkPath });
64+
}
5765
}
5866
}
5967
}
60-
return netl;
68+
return locations;
6169
});
62-
networkLocations ??= new ValueSet();
63-
networkLocations.Add("Count", networkLocations.Count);
64-
await Win32API.SendMessageAsync(connection, networkLocations, message.Get("RequestID", (string)null));
70+
var response = new ValueSet();
71+
response.Add("NetworkLocations", JsonConvert.SerializeObject(networkLocations));
72+
await Win32API.SendMessageAsync(connection, response, message.Get("RequestID", (string)null));
6573
break;
6674

6775
case "OpenMapNetworkDriveDialog":

Files/App.xaml.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ protected override async void OnLaunched(LaunchActivatedEventArgs e)
203203
bool canEnablePrelaunch = ApiInformation.IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", "EnablePrelaunch");
204204

205205
await EnsureSettingsAndConfigurationAreBootstrapped();
206-
InitializeAppComponentsAsync().ContinueWith(t => Logger.Warn(t.Exception, "Error during LoadOtherStuffAsync()"), TaskContinuationOptions.OnlyOnFaulted);
206+
_ = InitializeAppComponentsAsync().ContinueWith(t => Logger.Warn(t.Exception, "Error during InitializeAppComponentsAsync()"), TaskContinuationOptions.OnlyOnFaulted);
207207

208208
var rootFrame = EnsureWindowIsInitialized();
209209

@@ -261,7 +261,7 @@ protected override async void OnFileActivated(FileActivatedEventArgs e)
261261
SystemInformation.Instance.TrackAppUse(e);
262262

263263
await EnsureSettingsAndConfigurationAreBootstrapped();
264-
InitializeAppComponentsAsync().ContinueWith(t => Logger.Warn(t.Exception, "Error during LoadOtherStuffAsync()"), TaskContinuationOptions.OnlyOnFaulted);
264+
_ = InitializeAppComponentsAsync().ContinueWith(t => Logger.Warn(t.Exception, "Error during InitializeAppComponentsAsync()"), TaskContinuationOptions.OnlyOnFaulted);
265265

266266
var rootFrame = EnsureWindowIsInitialized();
267267

@@ -327,7 +327,7 @@ protected override async void OnActivated(IActivatedEventArgs args)
327327
Logger.Info($"App activated by {args.Kind.ToString()}");
328328

329329
await EnsureSettingsAndConfigurationAreBootstrapped();
330-
InitializeAppComponentsAsync().ContinueWith(t => Logger.Warn(t.Exception, "Error during LoadOtherStuffAsync()"), TaskContinuationOptions.OnlyOnFaulted);
330+
_ = InitializeAppComponentsAsync().ContinueWith(t => Logger.Warn(t.Exception, "Error during InitializeAppComponentsAsync()"), TaskContinuationOptions.OnlyOnFaulted);
331331

332332
var rootFrame = EnsureWindowIsInitialized();
333333

Files/Filesystem/NetworkDrivesManager.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.Toolkit.Mvvm.ComponentModel;
88
using Microsoft.Toolkit.Mvvm.DependencyInjection;
99
using Microsoft.Toolkit.Uwp;
10+
using Newtonsoft.Json;
1011
using System;
1112
using System.Collections.Generic;
1213
using System.Collections.ObjectModel;
@@ -68,15 +69,16 @@ public async Task EnumerateDrivesAsync()
6869
{ "Arguments", "NetworkDriveOperation" },
6970
{ "netdriveop", "GetNetworkLocations" }
7071
});
71-
if (status == AppServiceResponseStatus.Success && response.ContainsKey("Count"))
72+
if (status == AppServiceResponseStatus.Success && response.ContainsKey("NetworkLocations"))
7273
{
73-
foreach (var key in response.Keys
74-
.Where(k => k != "Count" && k != "RequestID"))
74+
var items = JsonConvert.DeserializeObject<List<ShellLinkItem>>((string)response["NetworkLocations"]);
75+
foreach (var item in items ?? new())
7576
{
7677
var networkItem = new DriveItem()
7778
{
78-
Text = key,
79-
Path = (string)response[key],
79+
Text = System.IO.Path.GetFileNameWithoutExtension(item.FileName),
80+
Path = item.TargetPath,
81+
DeviceID = item.FilePath,
8082
Type = DriveType.Network,
8183
ItemType = NavigationControlItemType.Drive
8284
};
@@ -147,12 +149,16 @@ await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPrio
147149
.OrderByDescending(o => string.Equals(o.Text, "Network".GetLocalized(), StringComparison.OrdinalIgnoreCase))
148150
.ThenBy(o => o.Text))
149151
{
150-
var resource = await UIHelpers.GetIconResourceInfo(Constants.ImageRes.Folder);
151-
if (resource != null)
152+
if (!string.IsNullOrEmpty(drive.DeviceID))
152153
{
153-
drive.IconData = resource.IconDataBytes;
154-
drive.Icon = await drive.IconData.ToBitmapAsync();
154+
drive.IconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(drive.DeviceID, 24);
155155
}
156+
if (drive.IconData == null)
157+
{
158+
var resource = await UIHelpers.GetIconResourceInfo(Constants.ImageRes.Folder);
159+
drive.IconData = resource?.IconDataBytes;
160+
}
161+
drive.Icon = await drive.IconData.ToBitmapAsync();
156162
if (!section.ChildItems.Contains(drive))
157163
{
158164
section.ChildItems.Add(drive);

Files/Filesystem/StorageEnumerators/Win32StorageEnumerator.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
using ByteSizeLib;
2+
using Files.Common;
23
using Files.Extensions;
34
using Files.Filesystem.StorageItems;
45
using Files.Helpers;
56
using Files.Helpers.FileListCache;
67
using Files.Services;
78
using Microsoft.Toolkit.Mvvm.DependencyInjection;
89
using Microsoft.Toolkit.Uwp;
10+
using Newtonsoft.Json;
911
using System;
1012
using System.Collections.Generic;
1113
using System.IO;
@@ -259,21 +261,20 @@ CancellationToken cancellationToken
259261
{
260262
return null;
261263
}
262-
if (status == AppServiceResponseStatus.Success
263-
&& response.ContainsKey("TargetPath"))
264+
if (status == AppServiceResponseStatus.Success && response.ContainsKey("ShortcutInfo"))
264265
{
265266
var isUrl = findData.cFileName.EndsWith(".url", StringComparison.OrdinalIgnoreCase);
266-
string target = (string)response["TargetPath"];
267+
var shInfo = JsonConvert.DeserializeObject<ShellLinkItem>((string)response["ShortcutInfo"]);
267268

268269
return new ShortcutItem(null, dateReturnFormat)
269270
{
270-
PrimaryItemAttribute = (bool)response["IsFolder"] ? StorageItemTypes.Folder : StorageItemTypes.File,
271+
PrimaryItemAttribute = shInfo.IsFolder ? StorageItemTypes.Folder : StorageItemTypes.File,
271272
FileExtension = itemFileExtension,
272273
IsHiddenItem = isHidden,
273274
Opacity = opacity,
274275
FileImage = null,
275-
LoadFileIcon = !(bool)response["IsFolder"] && itemThumbnailImgVis,
276-
LoadWebShortcutGlyph = !(bool)response["IsFolder"] && isUrl && itemEmptyImgVis,
276+
LoadFileIcon = !shInfo.IsFolder && itemThumbnailImgVis,
277+
LoadWebShortcutGlyph = !shInfo.IsFolder && isUrl && itemEmptyImgVis,
277278
ItemNameRaw = itemName,
278279
ItemDateModifiedReal = itemModifiedDate,
279280
ItemDateAccessedReal = itemLastAccessDate,
@@ -282,10 +283,10 @@ CancellationToken cancellationToken
282283
ItemPath = itemPath,
283284
FileSize = itemSize,
284285
FileSizeBytes = itemSizeBytes,
285-
TargetPath = target,
286-
Arguments = (string)response["Arguments"],
287-
WorkingDirectory = (string)response["WorkingDirectory"],
288-
RunAsAdmin = (bool)response["RunAsAdmin"],
286+
TargetPath = shInfo.TargetPath,
287+
Arguments = shInfo.Arguments,
288+
WorkingDirectory = shInfo.WorkingDirectory,
289+
RunAsAdmin = shInfo.RunAsAdmin,
289290
IsUrl = isUrl,
290291
};
291292
}

Files/Helpers/NavigationHelpers.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Files.Views;
88
using Microsoft.Toolkit.Mvvm.DependencyInjection;
99
using Microsoft.Toolkit.Uwp;
10+
using Newtonsoft.Json;
1011
using System;
1112
using System.Collections.Generic;
1213
using System.Linq;
@@ -132,7 +133,7 @@ public static async Task<bool> OpenPath(string path, IShellPage associatedInstan
132133
bool isShortcutItem = path.EndsWith(".lnk", StringComparison.Ordinal) || path.EndsWith(".url", StringComparison.Ordinal);
133134
FilesystemResult opened = (FilesystemResult)false;
134135

135-
var shortcutInfo = new ShortcutItem();
136+
var shortcutInfo = new ShellLinkItem();
136137
if (itemType == null || isShortcutItem || isHiddenItem || isReparsePoint)
137138
{
138139
if (isShortcutItem)
@@ -149,15 +150,14 @@ public static async Task<bool> OpenPath(string path, IShellPage associatedInstan
149150
{ "filepath", path }
150151
});
151152

152-
if (status == AppServiceResponseStatus.Success)
153+
if (status == AppServiceResponseStatus.Success && response.ContainsKey("ShortcutInfo"))
153154
{
154-
shortcutInfo.TargetPath = response.Get("TargetPath", string.Empty);
155-
shortcutInfo.Arguments = response.Get("Arguments", string.Empty);
156-
shortcutInfo.WorkingDirectory = response.Get("WorkingDirectory", string.Empty);
157-
shortcutInfo.RunAsAdmin = response.Get("RunAsAdmin", false);
158-
shortcutInfo.PrimaryItemAttribute = response.Get("IsFolder", false) ? StorageItemTypes.Folder : StorageItemTypes.File;
159-
160-
itemType = response.Get("IsFolder", false) ? FilesystemItemType.Directory : FilesystemItemType.File;
155+
var shInfo = JsonConvert.DeserializeObject<ShellLinkItem>((string)response["ShortcutInfo"]);
156+
if (shInfo != null)
157+
{
158+
shortcutInfo = shInfo;
159+
}
160+
itemType = shInfo != null && shInfo.IsFolder ? FilesystemItemType.Directory : FilesystemItemType.File;
161161
}
162162
else
163163
{
@@ -262,7 +262,7 @@ private static async Task<FilesystemResult> OpenLibrary(string path, IShellPage
262262
return opened;
263263
}
264264

265-
private static async Task<FilesystemResult> OpenDirectory(string path, IShellPage associatedInstance, IEnumerable<string> selectItems, ShortcutItem shortcutInfo)
265+
private static async Task<FilesystemResult> OpenDirectory(string path, IShellPage associatedInstance, IEnumerable<string> selectItems, ShellLinkItem shortcutInfo)
266266
{
267267
IUserSettingsService userSettingsService = Ioc.Default.GetService<IUserSettingsService>();
268268

@@ -352,7 +352,7 @@ private static async Task<FilesystemResult> OpenDirectory(string path, IShellPag
352352
return opened;
353353
}
354354

355-
private static async Task<FilesystemResult> OpenFile(string path, IShellPage associatedInstance, IEnumerable<string> selectItems, ShortcutItem shortcutInfo, bool openViaApplicationPicker = false, string args = default)
355+
private static async Task<FilesystemResult> OpenFile(string path, IShellPage associatedInstance, IEnumerable<string> selectItems, ShellLinkItem shortcutInfo, bool openViaApplicationPicker = false, string args = default)
356356
{
357357
var opened = (FilesystemResult)false;
358358
bool isHiddenItem = NativeFileOperationsHelper.HasFileAttribute(path, System.IO.FileAttributes.Hidden);

Files/ViewModels/Properties/DriveProperties.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public async override void GetSpecialProperties()
5252
{
5353
ViewModel.IconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(Drive.Path, 80);
5454
}
55+
ViewModel.IconData ??= await FileThumbnailHelper.LoadIconWithoutOverlayAsync(Drive.DeviceID, 80); // For network shortcuts
5556
}
5657

5758
if (diskRoot == null || diskRoot.Properties == null)

0 commit comments

Comments
 (0)