Skip to content

Commit c7ac2d3

Browse files
committed
Init
1 parent 56df483 commit c7ac2d3

File tree

2 files changed

+136
-73
lines changed

2 files changed

+136
-73
lines changed

src/Files.App.CsWin32/NativeMethods.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,8 @@ IFileOperation
133133
IShellItem2
134134
PSGetPropertyKeyFromName
135135
ShellExecuteEx
136+
BHID_EnumItems
137+
CoTaskMemFree
138+
FOLDERID_NetHood
139+
IShellLinkW
140+
SLGP_FLAGS

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

Lines changed: 131 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
// Copyright (c) 2024 Files Community
22
// Licensed under the MIT License. See the LICENSE.
33

4-
using System.Runtime.InteropServices;
54
using System.Text;
6-
using Vanara.PInvoke;
7-
using Vanara.Windows.Shell;
85
using Windows.Win32;
96
using Windows.Win32.Foundation;
107
using Windows.Win32.NetworkManagement.WNet;
118
using Windows.Win32.Security.Credentials;
9+
using Windows.Win32.System.SystemServices;
10+
using Windows.Win32.UI.Shell;
1211

1312
namespace Files.App.Services
1413
{
1514
public sealed class NetworkService : ObservableObject, INetworkService
1615
{
1716
private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService<ICommonDialogService>();
1817

19-
private readonly static string guid = "::{f02c1a0d-be21-4350-88b0-7367fc96ef3c}";
20-
21-
2218
private ObservableCollection<ILocatableFolder> _Computers = [];
2319
/// <inheritdoc/>
2420
public ObservableCollection<ILocatableFolder> Computers
@@ -40,102 +36,164 @@ public ObservableCollection<ILocatableFolder> Shortcuts
4036
/// </summary>
4137
public NetworkService()
4238
{
43-
var networkItem = new DriveItem()
39+
var item = new DriveItem()
4440
{
4541
DeviceID = "network-folder",
4642
Text = "Network".GetLocalizedResource(),
4743
Path = Constants.UserEnvironmentPaths.NetworkFolderPath,
4844
Type = DriveType.Network,
4945
ItemType = NavigationControlItemType.Drive,
46+
MenuOptions = new ContextMenuOptions()
47+
{
48+
IsLocationItem = true,
49+
ShowShellItems = true,
50+
ShowProperties = true,
51+
},
5052
};
5153

52-
networkItem.MenuOptions = new ContextMenuOptions()
53-
{
54-
IsLocationItem = true,
55-
ShowEjectDevice = networkItem.IsRemovable,
56-
ShowShellItems = true,
57-
ShowProperties = true,
58-
};
54+
item.MenuOptions.ShowEjectDevice = item.IsRemovable;
55+
5956
lock (_Computers)
60-
_Computers.Add(networkItem);
57+
_Computers.Add(item);
6158
}
6259

6360
/// <inheritdoc/>
6461
public async Task<IEnumerable<ILocatableFolder>> GetComputersAsync()
6562
{
66-
var result = await Win32Helper.GetShellFolderAsync(guid, false, true, 0, int.MaxValue);
63+
return await Task.Run(GetComputers);
6764

68-
return result.Enumerate.Where(item => item.IsFolder).Select(item =>
65+
unsafe IEnumerable<ILocatableFolder> GetComputers()
6966
{
70-
var networkItem = new DriveItem()
67+
HRESULT hr = default;
68+
69+
// Get IShellItem of the shell folder
70+
var shellItemIid = typeof(IShellItem).GUID;
71+
using ComPtr<IShellItem> pFolderShellItem = default;
72+
fixed (char* pszFolderShellPath = "Shell:::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}")
73+
hr = PInvoke.SHCreateItemFromParsingName(pszFolderShellPath, null, &shellItemIid, (void**)pFolderShellItem.GetAddressOf());
74+
75+
// Get IEnumShellItems of the shell folder
76+
var enumItemsBHID = PInvoke.BHID_EnumItems;
77+
Guid enumShellItemIid = typeof(IEnumShellItems).GUID;
78+
using ComPtr<IEnumShellItems> pEnumShellItems = default;
79+
hr = pFolderShellItem.Get()->BindToHandler(null, &enumItemsBHID, &enumShellItemIid, (void**)pEnumShellItems.GetAddressOf());
80+
81+
// Enumerate items and populate the list
82+
List<ILocatableFolder> items = [];
83+
using ComPtr<IShellItem> pShellItem = default;
84+
while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK)
7185
{
72-
Text = item.FileName,
73-
Path = item.FilePath,
74-
DeviceID = item.FilePath,
75-
Type = DriveType.Network,
76-
ItemType = NavigationControlItemType.Drive,
77-
};
78-
79-
networkItem.MenuOptions = new ContextMenuOptions()
80-
{
81-
IsLocationItem = true,
82-
ShowEjectDevice = networkItem.IsRemovable,
83-
ShowShellItems = true,
84-
ShowProperties = true,
85-
};
86+
// Get only folders
87+
if (pShellItem.Get()->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var attribute) == HRESULT.S_OK &&
88+
(attribute & SFGAO_FLAGS.SFGAO_FOLDER) is not SFGAO_FLAGS.SFGAO_FOLDER)
89+
continue;
90+
91+
// Get the target path
92+
using ComPtr<IShellLinkW> pShellLink = default;
93+
var shellLinkIid = typeof(IShellLinkW).GUID;
94+
pShellItem.Get()->QueryInterface(&shellLinkIid, (void**)pShellLink.GetAddressOf());
95+
string targetPath = string.Empty;
96+
fixed (char* pszTargetPath = new char[1024])
97+
{
98+
hr = pShellLink.Get()->GetPath(pszTargetPath, 1024, null, (uint)SLGP_FLAGS.SLGP_RAWPATH);
99+
targetPath = Environment.ExpandEnvironmentVariables(new PWSTR(pszTargetPath).ToString());
100+
}
86101

87-
return networkItem;
88-
});
102+
// Get the display name
103+
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var szDisplayName);
104+
var fileName = szDisplayName.ToString();
105+
PInvoke.CoTaskMemFree(szDisplayName.Value);
106+
107+
var item = new DriveItem()
108+
{
109+
Text = fileName,
110+
Path = targetPath,
111+
DeviceID = targetPath,
112+
Type = DriveType.Network,
113+
ItemType = NavigationControlItemType.Drive,
114+
MenuOptions = new()
115+
{
116+
IsLocationItem = true,
117+
ShowShellItems = true,
118+
ShowProperties = true,
119+
},
120+
};
121+
122+
item.MenuOptions.ShowEjectDevice = item.IsRemovable;
123+
124+
items.Add(item);
125+
}
126+
127+
return items;
128+
}
89129
}
90130

91131
/// <inheritdoc/>
92132
public async Task<IEnumerable<ILocatableFolder>> GetShortcutsAsync()
93133
{
94-
var networkLocations = await Win32Helper.StartSTATask(() =>
134+
return await Task.Run(GetShortcuts);
135+
136+
unsafe IEnumerable<ILocatableFolder> GetShortcuts()
95137
{
96-
var locations = new List<ShellLinkItem>();
97-
using (var netHood = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_NetHood))
138+
// Get IShellItem of the known folder
139+
using ComPtr<IShellItem> pShellFolder = default;
140+
var folderId = PInvoke.FOLDERID_NetHood;
141+
var shellItemIid = typeof(IShellItem).GUID;
142+
HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, &shellItemIid, (void**)pShellFolder.GetAddressOf());
143+
144+
// Get IEnumShellItems for Recycle Bin folder
145+
using ComPtr<IEnumShellItems> pEnumShellItems = default;
146+
Guid enumShellItemGuid = typeof(IEnumShellItems).GUID;
147+
var enumItemsBHID = BHID.BHID_EnumItems;
148+
hr = pShellFolder.Get()->BindToHandler(null, &enumItemsBHID, &enumShellItemGuid, (void**)pEnumShellItems.GetAddressOf());
149+
150+
List<ILocatableFolder> items = [];
151+
using ComPtr<IShellItem> pShellItem = default;
152+
while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK)
98153
{
99-
foreach (var item in netHood)
154+
// Get the target path
155+
using ComPtr<IShellLinkW> pShellLink = default;
156+
var shellLinkIid = typeof(IShellLinkW).GUID;
157+
pShellItem.Get()->QueryInterface(&shellLinkIid, (void**)pShellLink.GetAddressOf());
158+
string targetPath = string.Empty;
159+
fixed (char* pszTargetPath = new char[1024])
100160
{
101-
if (item is ShellLink link)
102-
{
103-
locations.Add(ShellFolderExtensions.GetShellLinkItem(link));
104-
}
105-
else
106-
{
107-
var linkPath = (string?)item?.Properties["System.Link.TargetParsingPath"];
108-
if (linkPath is not null)
109-
{
110-
var linkItem = ShellFolderExtensions.GetShellFileItem(item);
111-
locations.Add(new(linkItem) { TargetPath = linkPath });
112-
}
113-
}
161+
hr = pShellLink.Get()->GetPath(pszTargetPath, 1024, null, (uint)SLGP_FLAGS.SLGP_RAWPATH);
162+
targetPath = Environment.ExpandEnvironmentVariables(new PWSTR(pszTargetPath).ToString());
114163
}
164+
165+
// Get the display name
166+
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var szDisplayName);
167+
var fileName = szDisplayName.ToString();
168+
PInvoke.CoTaskMemFree(szDisplayName.Value);
169+
170+
// Get the file system path on disk
171+
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out szDisplayName);
172+
var filePath = szDisplayName.ToString();
173+
PInvoke.CoTaskMemFree(szDisplayName.Value);
174+
175+
var item = new DriveItem()
176+
{
177+
Text = fileName,
178+
Path = targetPath,
179+
DeviceID = filePath,
180+
Type = DriveType.Network,
181+
ItemType = NavigationControlItemType.Drive,
182+
MenuOptions = new()
183+
{
184+
IsLocationItem = true,
185+
ShowShellItems = true,
186+
ShowProperties = true,
187+
},
188+
};
189+
190+
item.MenuOptions.ShowEjectDevice = item.IsRemovable;
191+
192+
items.Add(item);
115193
}
116-
return locations;
117-
});
118194

119-
return (networkLocations ?? Enumerable.Empty<ShellLinkItem>()).Select(item =>
120-
{
121-
var networkItem = new DriveItem()
122-
{
123-
Text = item.FileName,
124-
Path = item.TargetPath,
125-
DeviceID = item.FilePath,
126-
Type = DriveType.Network,
127-
ItemType = NavigationControlItemType.Drive,
128-
};
129-
130-
networkItem.MenuOptions = new ContextMenuOptions()
131-
{
132-
IsLocationItem = true,
133-
ShowEjectDevice = networkItem.IsRemovable,
134-
ShowShellItems = true,
135-
ShowProperties = true,
136-
};
137-
return networkItem;
138-
});
195+
return items;
196+
}
139197
}
140198

141199
/// <inheritdoc/>

0 commit comments

Comments
 (0)