Skip to content

Commit a318898

Browse files
authored
Add support for folder shortcuts in shell folders (#9163)
1 parent 12f32a3 commit a318898

File tree

8 files changed

+158
-36
lines changed

8 files changed

+158
-36
lines changed

src/Files.FullTrust/Helpers/ShellFolderHelpers.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ public static ShellFileItem GetShellFileItem(ShellItem folderItem)
5050
folderItem.Properties.TryGetValue<string>(
5151
Ole32.PROPERTYKEY.System.ItemNameDisplay, out var fileName);
5252
fileName ??= Path.GetFileName(folderItem.Name); // Original file name
53+
fileName ??= folderItem.GetDisplayName(ShellItemDisplayString.ParentRelativeParsing);
54+
var itemNameOrOriginalPath = folderItem.Name ?? fileName;
5355
string filePath = string.IsNullOrEmpty(Path.GetDirectoryName(parsingPath)) ? // Null if root
54-
parsingPath : Path.Combine(Path.GetDirectoryName(parsingPath), folderItem.Name); // In recycle bin "Name" contains original file path + name
56+
parsingPath : Path.Combine(Path.GetDirectoryName(parsingPath), itemNameOrOriginalPath); // In recycle bin "Name" contains original file path + name
5557
if (!isFolder && !string.IsNullOrEmpty(parsingPath) && Path.GetExtension(parsingPath) is string realExtension && !string.IsNullOrEmpty(realExtension))
5658
{
5759
if (!string.IsNullOrEmpty(fileName) && !fileName.EndsWith(realExtension, StringComparison.OrdinalIgnoreCase))

src/Files.FullTrust/MessageHandlers/Win32MessageHandler.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ public async Task ParseArgumentsAsync(PipeStream connection, Dictionary<string,
143143
{
144144
try
145145
{
146-
var shellFileItem = ShellFolderExtensions.GetShellFileItem(folderItem);
146+
var shellFileItem = folderItem is ShellLink link ?
147+
ShellFolderExtensions.GetShellLinkItem(link) :
148+
ShellFolderExtensions.GetShellFileItem(folderItem);
147149
flc.Add(shellFileItem);
148150
}
149151
catch (FileNotFoundException)
@@ -163,7 +165,10 @@ public async Task ParseArgumentsAsync(PipeStream connection, Dictionary<string,
163165
return (folder, flc);
164166
});
165167
sfResponseEnum.Add("Folder", JsonConvert.SerializeObject(folder));
166-
sfResponseEnum.Add("Enumerate", JsonConvert.SerializeObject(folderContentsList));
168+
sfResponseEnum.Add("Enumerate", JsonConvert.SerializeObject(folderContentsList, new JsonSerializerSettings
169+
{
170+
TypeNameHandling = TypeNameHandling.Objects
171+
}));
167172
await Win32API.SendMessageAsync(connection, sfResponseEnum, message.Get("RequestID", (string)null));
168173
break;
169174

src/Files.Uwp/Files.Uwp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@
230230
<Compile Include="Helpers\CommonPaths.cs" />
231231
<Compile Include="Helpers\FileExtensionHelpers.cs" />
232232
<Compile Include="Helpers\ItemListDisplayHelpers\BlockingListEnumerator.cs" />
233+
<Compile Include="Helpers\KnownTypesBinder.cs" />
233234
<Compile Include="Helpers\LocalizedEnumHelper.cs" />
234235
<Compile Include="Helpers\ResourceHelpers.cs" />
235236
<Compile Include="Helpers\FtpHelpers.cs" />

src/Files.Uwp/Filesystem/StorageEnumerators/UniversalStorageEnumerator.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,29 @@ public static async Task<ListedItem> AddFolderAsync(BaseStorageFolder folder, St
164164
var basicProperties = await folder.GetBasicPropertiesAsync();
165165
if (!cancellationToken.IsCancellationRequested)
166166
{
167-
if (folder is BinStorageFolder binFolder)
167+
if (folder is ShortcutStorageFolder linkFolder)
168+
{
169+
return new ShortcutItem(folder.FolderRelativeId, dateReturnFormat)
170+
{
171+
PrimaryItemAttribute = StorageItemTypes.Folder,
172+
IsHiddenItem = false,
173+
Opacity = 1,
174+
FileImage = null,
175+
LoadFileIcon = false,
176+
ItemNameRaw = folder.DisplayName,
177+
ItemDateModifiedReal = basicProperties.DateModified,
178+
ItemDateCreatedReal = folder.DateCreated,
179+
ItemType = folder.DisplayType,
180+
ItemPath = folder.Path,
181+
FileSize = null,
182+
FileSizeBytes = 0,
183+
TargetPath = linkFolder.TargetPath,
184+
Arguments = linkFolder.Arguments,
185+
WorkingDirectory = linkFolder.WorkingDirectory,
186+
RunAsAdmin = linkFolder.RunAsAdmin
187+
};
188+
}
189+
else if (folder is BinStorageFolder binFolder)
168190
{
169191
return new RecycleBinItem(folder.FolderRelativeId)
170192
{
@@ -240,7 +262,33 @@ CancellationToken cancellationToken
240262
}
241263
else
242264
{
243-
if (file is BinStorageFile binFile)
265+
if (file is ShortcutStorageFile linkFile)
266+
{
267+
var isUrl = linkFile.Name.EndsWith(".url", StringComparison.OrdinalIgnoreCase);
268+
return new ShortcutItem(file.FolderRelativeId, dateReturnFormat)
269+
{
270+
PrimaryItemAttribute = StorageItemTypes.File,
271+
FileExtension = itemFileExtension,
272+
IsHiddenItem = false,
273+
Opacity = 1,
274+
FileImage = null,
275+
LoadFileIcon = itemThumbnailImgVis,
276+
LoadWebShortcutGlyph = isUrl,
277+
ItemNameRaw = itemName,
278+
ItemDateModifiedReal = itemModifiedDate,
279+
ItemDateCreatedReal = itemCreatedDate,
280+
ItemType = itemType,
281+
ItemPath = itemPath,
282+
FileSize = itemSize,
283+
FileSizeBytes = (long)itemSizeBytes,
284+
TargetPath = linkFile.TargetPath,
285+
Arguments = linkFile.Arguments,
286+
WorkingDirectory = linkFile.WorkingDirectory,
287+
RunAsAdmin = linkFile.RunAsAdmin,
288+
IsUrl = isUrl,
289+
};
290+
}
291+
else if (file is BinStorageFile binFile)
244292
{
245293
return new RecycleBinItem(file.FolderRelativeId)
246294
{

src/Files.Uwp/Filesystem/StorageItems/ShellStorageFile.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@
1616

1717
namespace Files.Uwp.Filesystem.StorageItems
1818
{
19+
public class ShortcutStorageFile : ShellStorageFile, IShortcutStorageItem
20+
{
21+
public string TargetPath { get; }
22+
public string Arguments { get; }
23+
public string WorkingDirectory { get; }
24+
public bool RunAsAdmin { get; }
25+
26+
public ShortcutStorageFile(ShellLinkItem item) : base(item)
27+
{
28+
TargetPath = item.TargetPath;
29+
Arguments = item.Arguments;
30+
WorkingDirectory = item.WorkingDirectory;
31+
RunAsAdmin = item.RunAsAdmin;
32+
}
33+
}
34+
1935
public class BinStorageFile : ShellStorageFile, IBinStorageItem
2036
{
2137
public string OriginalPath { get; }
@@ -58,11 +74,18 @@ public ShellStorageFile(ShellFileItem item)
5874

5975
public static ShellStorageFile FromShellItem(ShellFileItem item)
6076
{
61-
if (item.RecyclePath != item.FilePath)
77+
if (item is ShellLinkItem linkItem)
78+
{
79+
return new ShortcutStorageFile(linkItem);
80+
}
81+
else if (item.RecyclePath.Contains("$Recycle.Bin", StringComparison.Ordinal))
6282
{
6383
return new BinStorageFile(item);
6484
}
65-
return new ShellStorageFile(item);
85+
else
86+
{
87+
return new ShellStorageFile(item);
88+
}
6689
}
6790

6891
public static IAsyncOperation<BaseStorageFile> FromPathAsync(string path)

src/Files.Uwp/Filesystem/StorageItems/ShellStorageFolder.cs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,30 @@
1818

1919
namespace Files.Uwp.Filesystem.StorageItems
2020
{
21+
public class ShortcutStorageFolder : ShellStorageFolder, IShortcutStorageItem
22+
{
23+
public string TargetPath { get; }
24+
public string Arguments { get; }
25+
public string WorkingDirectory { get; }
26+
public bool RunAsAdmin { get; }
27+
28+
public ShortcutStorageFolder(ShellLinkItem item) : base(item)
29+
{
30+
TargetPath = item.TargetPath;
31+
Arguments = item.Arguments;
32+
WorkingDirectory = item.WorkingDirectory;
33+
RunAsAdmin = item.RunAsAdmin;
34+
}
35+
}
36+
37+
public interface IShortcutStorageItem : IStorageItem
38+
{
39+
string TargetPath { get; }
40+
string Arguments { get; }
41+
string WorkingDirectory { get; }
42+
bool RunAsAdmin { get; }
43+
}
44+
2145
public class BinStorageFolder : ShellStorageFolder, IBinStorageItem
2246
{
2347
public string OriginalPath { get; }
@@ -58,19 +82,26 @@ public ShellStorageFolder(ShellFileItem item)
5882

5983
public static bool IsShellPath(string path)
6084
{
61-
return path is not null &&
62-
path.StartsWith("shell:", StringComparison.OrdinalIgnoreCase) ||
85+
return path is not null &&
86+
path.StartsWith("shell:", StringComparison.OrdinalIgnoreCase) ||
6387
path.StartsWith("::{", StringComparison.Ordinal) ||
6488
path.StartsWith(@"\\SHELL\", StringComparison.Ordinal);
6589
}
6690

6791
public static ShellStorageFolder FromShellItem(ShellFileItem item)
6892
{
69-
if (item.RecyclePath != item.FilePath)
93+
if (item is ShellLinkItem linkItem)
94+
{
95+
return new ShortcutStorageFolder(linkItem);
96+
}
97+
else if (item.RecyclePath.Contains("$Recycle.Bin", StringComparison.Ordinal))
7098
{
7199
return new BinStorageFolder(item);
72100
}
73-
return new ShellStorageFolder(item);
101+
else
102+
{
103+
return new ShellStorageFolder(item);
104+
}
74105
}
75106

76107
public static IAsyncOperation<BaseStorageFolder> FromPathAsync(string path)
@@ -106,7 +137,11 @@ public static IAsyncOperation<BaseStorageFolder> FromPathAsync(string path)
106137
if (status == AppServiceResponseStatus.Success)
107138
{
108139
var folder = JsonConvert.DeserializeObject<ShellFileItem>(response.Get("Folder", ""));
109-
var items = JsonConvert.DeserializeObject<List<ShellFileItem>>(response.Get("Enumerate", ""));
140+
var items = JsonConvert.DeserializeObject<List<ShellFileItem>>(response.Get("Enumerate", ""), new JsonSerializerSettings()
141+
{
142+
TypeNameHandling = TypeNameHandling.Objects,
143+
SerializationBinder = new KnownTypesBinder() { KnownTypes = { typeof(ShellFileItem), typeof(ShellLinkItem) } }
144+
});
110145
return (folder, items);
111146
}
112147
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Newtonsoft.Json.Serialization;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
6+
namespace Files.Uwp.Helpers
7+
{
8+
public class KnownTypesBinder : ISerializationBinder
9+
{
10+
public IList<Type> KnownTypes { get; } = new List<Type>();
11+
12+
public Type BindToType(string assemblyName, string typeName)
13+
{
14+
if (!KnownTypes.Any(x => x.Name == typeName || x.FullName == typeName))
15+
{
16+
throw new ArgumentException();
17+
}
18+
else
19+
{
20+
return KnownTypes.SingleOrDefault(t => t.Name == typeName || t.FullName == typeName);
21+
}
22+
}
23+
24+
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
25+
{
26+
assemblyName = null;
27+
typeName = serializedType.Name;
28+
}
29+
}
30+
}

src/Files.Uwp/UserControls/MultitaskingControl/TabItem/TabItem.cs

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System;
88
using System.Collections.Generic;
99
using System.Linq;
10+
using Files.Uwp.Helpers;
1011

1112
namespace Files.Uwp.UserControls.MultitaskingControl
1213
{
@@ -80,7 +81,7 @@ public class TabItemArguments
8081
{
8182
private static KnownTypesBinder TypesBinder = new KnownTypesBinder
8283
{
83-
KnownTypes = new List<Type> { typeof(PaneNavigationArguments) }
84+
KnownTypes = { typeof(PaneNavigationArguments) }
8485
};
8586

8687
public Type InitialPageType { get; set; }
@@ -98,27 +99,4 @@ public class TabItemArguments
9899
SerializationBinder = TypesBinder
99100
});
100101
}
101-
102-
public class KnownTypesBinder : ISerializationBinder
103-
{
104-
public IList<Type> KnownTypes { get; set; }
105-
106-
public Type BindToType(string assemblyName, string typeName)
107-
{
108-
if (!KnownTypes.Any(x => x.Name == typeName))
109-
{
110-
throw new ArgumentException();
111-
}
112-
else
113-
{
114-
return KnownTypes.SingleOrDefault(t => t.Name == typeName);
115-
}
116-
}
117-
118-
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
119-
{
120-
assemblyName = null;
121-
typeName = serializedType.Name;
122-
}
123-
}
124102
}

0 commit comments

Comments
 (0)