Skip to content

Commit cc0c966

Browse files
committed
Init
1 parent 56df483 commit cc0c966

File tree

2 files changed

+137
-73
lines changed

2 files changed

+137
-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: 132 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
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;
5+
using Windows.Networking.Connectivity;
86
using Windows.Win32;
97
using Windows.Win32.Foundation;
108
using Windows.Win32.NetworkManagement.WNet;
119
using Windows.Win32.Security.Credentials;
10+
using Windows.Win32.System.SystemServices;
11+
using Windows.Win32.UI.Shell;
1212

1313
namespace Files.App.Services
1414
{
1515
public sealed class NetworkService : ObservableObject, INetworkService
1616
{
1717
private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService<ICommonDialogService>();
1818

19-
private readonly static string guid = "::{f02c1a0d-be21-4350-88b0-7367fc96ef3c}";
20-
21-
2219
private ObservableCollection<ILocatableFolder> _Computers = [];
2320
/// <inheritdoc/>
2421
public ObservableCollection<ILocatableFolder> Computers
@@ -40,102 +37,164 @@ public ObservableCollection<ILocatableFolder> Shortcuts
4037
/// </summary>
4138
public NetworkService()
4239
{
43-
var networkItem = new DriveItem()
40+
var item = new DriveItem()
4441
{
4542
DeviceID = "network-folder",
4643
Text = "Network".GetLocalizedResource(),
4744
Path = Constants.UserEnvironmentPaths.NetworkFolderPath,
4845
Type = DriveType.Network,
4946
ItemType = NavigationControlItemType.Drive,
47+
MenuOptions = new ContextMenuOptions()
48+
{
49+
IsLocationItem = true,
50+
ShowShellItems = true,
51+
ShowProperties = true,
52+
},
5053
};
5154

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

6361
/// <inheritdoc/>
6462
public async Task<IEnumerable<ILocatableFolder>> GetComputersAsync()
6563
{
66-
var result = await Win32Helper.GetShellFolderAsync(guid, false, true, 0, int.MaxValue);
64+
return await Task.Run(GetComputers);
6765

68-
return result.Enumerate.Where(item => item.IsFolder).Select(item =>
66+
unsafe IEnumerable<ILocatableFolder> GetComputers()
6967
{
70-
var networkItem = new DriveItem()
68+
HRESULT hr = default;
69+
70+
// Get IShellItem of the shell folder
71+
var shellItemIid = typeof(IShellItem).GUID;
72+
using ComPtr<IShellItem> pFolderShellItem = default;
73+
fixed (char* pszFolderShellPath = "Shell:::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}")
74+
hr = PInvoke.SHCreateItemFromParsingName(pszFolderShellPath, null, &shellItemIid, (void**)pFolderShellItem.GetAddressOf());
75+
76+
// Get IEnumShellItems of the shell folder
77+
var enumItemsBHID = PInvoke.BHID_EnumItems;
78+
Guid enumShellItemIid = typeof(IEnumShellItems).GUID;
79+
using ComPtr<IEnumShellItems> pEnumShellItems = default;
80+
hr = pFolderShellItem.Get()->BindToHandler(null, &enumItemsBHID, &enumShellItemIid, (void**)pEnumShellItems.GetAddressOf());
81+
82+
// Enumerate items and populate the list
83+
List<ILocatableFolder> items = [];
84+
using ComPtr<IShellItem> pShellItem = default;
85+
while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK)
7186
{
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-
};
87+
// Get only folders
88+
if (pShellItem.Get()->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var attribute) == HRESULT.S_OK &&
89+
(attribute & SFGAO_FLAGS.SFGAO_FOLDER) is not SFGAO_FLAGS.SFGAO_FOLDER)
90+
continue;
91+
92+
// Get the target path
93+
using ComPtr<IShellLinkW> pShellLink = default;
94+
var shellLinkIid = typeof(IShellLinkW).GUID;
95+
pShellItem.Get()->QueryInterface(&shellLinkIid, (void**)pShellLink.GetAddressOf());
96+
string targetPath = string.Empty;
97+
fixed (char* pszTargetPath = new char[1024])
98+
{
99+
hr = pShellLink.Get()->GetPath(pszTargetPath, 1024, null, SLGP_FLAGS.SLGP_RAWPATH);
100+
targetPath = Environment.ExpandEnvironmentVariables(new PWSTR(pszTargetPath).ToString());
101+
}
86102

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

91132
/// <inheritdoc/>
92133
public async Task<IEnumerable<ILocatableFolder>> GetShortcutsAsync()
93134
{
94-
var networkLocations = await Win32Helper.StartSTATask(() =>
135+
return await Task.Run(GetShortcuts);
136+
137+
unsafe IEnumerable<ILocatableFolder> GetShortcuts()
95138
{
96-
var locations = new List<ShellLinkItem>();
97-
using (var netHood = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_NetHood))
139+
// Get IShellItem of the known folder
140+
using ComPtr<IShellItem> pShellFolder = default;
141+
var folderId = PInvoke.FOLDERID_NetHood;
142+
var shellItemIid = typeof(IShellItem).GUID;
143+
HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, &shellItemIid, (void**)pShellFolder.GetAddressOf());
144+
145+
// Get IEnumShellItems for Recycle Bin folder
146+
using ComPtr<IEnumShellItems> pEnumShellItems = default;
147+
Guid enumShellItemGuid = typeof(IEnumShellItems).GUID;
148+
var enumItemsBHID = BHID.BHID_EnumItems;
149+
hr = pShellFolder.Get()->BindToHandler(null, &enumItemsBHID, &enumShellItemGuid, (void**)pEnumShellItems.GetAddressOf());
150+
151+
List<ILocatableFolder> items = [];
152+
using ComPtr<IShellItem> pShellItem = default;
153+
while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK)
98154
{
99-
foreach (var item in netHood)
155+
// Get the target path
156+
using ComPtr<IShellLinkW> pShellLink = default;
157+
var shellLinkIid = typeof(IShellLinkW).GUID;
158+
pShellItem.Get()->QueryInterface(&shellLinkIid, (void**)pShellLink.GetAddressOf());
159+
string targetPath = string.Empty;
160+
fixed (char* pszTargetPath = new char[1024])
100161
{
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-
}
162+
hr = pShellLink.Get()->GetPath(pszTargetPath, 1024, null, SLGP_FLAGS.SLGP_RAWPATH);
163+
targetPath = Environment.ExpandEnvironmentVariables(new PWSTR(pszTargetPath).ToString());
114164
}
165+
166+
// Get the display name
167+
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var szDisplayName);
168+
var fileName = szDisplayName.ToString();
169+
PInvoke.CoTaskMemFree(szDisplayName.Value);
170+
171+
// Get the file system path on disk
172+
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out szDisplayName);
173+
var filePath = szDisplayName.ToString();
174+
PInvoke.CoTaskMemFree(szDisplayName.Value);
175+
176+
var item = new DriveItem()
177+
{
178+
Text = fileName,
179+
Path = targetPath,
180+
DeviceID = filePath,
181+
Type = DriveType.Network,
182+
ItemType = NavigationControlItemType.Drive,
183+
MenuOptions = new()
184+
{
185+
IsLocationItem = true,
186+
ShowShellItems = true,
187+
ShowProperties = true,
188+
},
189+
};
190+
191+
item.MenuOptions.ShowEjectDevice = item.IsRemovable;
192+
193+
items.Add(item);
115194
}
116-
return locations;
117-
});
118195

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-
});
196+
return items;
197+
}
139198
}
140199

141200
/// <inheritdoc/>

0 commit comments

Comments
 (0)