From c420f1958625457667b38a7d3158fcc23c548d75 Mon Sep 17 00:00:00 2001 From: 0x5BFA Date: Sat, 21 Sep 2024 13:09:49 +0900 Subject: [PATCH 1/2] Init --- src/Files.App.CsWin32/NativeMethods.txt | 5 + .../Services/Storage/StorageNetworkService.cs | 216 ++++++++++++------ 2 files changed, 150 insertions(+), 71 deletions(-) diff --git a/src/Files.App.CsWin32/NativeMethods.txt b/src/Files.App.CsWin32/NativeMethods.txt index 9d7c337d7c4d..a64db000acfd 100644 --- a/src/Files.App.CsWin32/NativeMethods.txt +++ b/src/Files.App.CsWin32/NativeMethods.txt @@ -133,3 +133,8 @@ IFileOperation IShellItem2 PSGetPropertyKeyFromName ShellExecuteEx +BHID_EnumItems +CoTaskMemFree +FOLDERID_NetHood +IShellLinkW +SLGP_FLAGS diff --git a/src/Files.App/Services/Storage/StorageNetworkService.cs b/src/Files.App/Services/Storage/StorageNetworkService.cs index d784303d7611..75de4fdebf70 100644 --- a/src/Files.App/Services/Storage/StorageNetworkService.cs +++ b/src/Files.App/Services/Storage/StorageNetworkService.cs @@ -1,14 +1,13 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using System.Runtime.InteropServices; using System.Text; -using Vanara.PInvoke; -using Vanara.Windows.Shell; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.NetworkManagement.WNet; using Windows.Win32.Security.Credentials; +using Windows.Win32.System.SystemServices; +using Windows.Win32.UI.Shell; namespace Files.App.Services { @@ -16,9 +15,6 @@ public sealed class NetworkService : ObservableObject, INetworkService { private ICommonDialogService CommonDialogService { get; } = Ioc.Default.GetRequiredService(); - private readonly static string guid = "::{f02c1a0d-be21-4350-88b0-7367fc96ef3c}"; - - private ObservableCollection _Computers = []; /// public ObservableCollection Computers @@ -40,102 +36,180 @@ public ObservableCollection Shortcuts /// public NetworkService() { - var networkItem = new DriveItem() + var item = new DriveItem() { DeviceID = "network-folder", Text = "Network".GetLocalizedResource(), Path = Constants.UserEnvironmentPaths.NetworkFolderPath, Type = DriveType.Network, ItemType = NavigationControlItemType.Drive, + MenuOptions = new ContextMenuOptions() + { + IsLocationItem = true, + ShowShellItems = true, + ShowProperties = true, + }, }; - networkItem.MenuOptions = new ContextMenuOptions() - { - IsLocationItem = true, - ShowEjectDevice = networkItem.IsRemovable, - ShowShellItems = true, - ShowProperties = true, - }; + item.MenuOptions.ShowEjectDevice = item.IsRemovable; + lock (_Computers) - _Computers.Add(networkItem); + _Computers.Add(item); } /// public async Task> GetComputersAsync() { - var result = await Win32Helper.GetShellFolderAsync(guid, false, true, 0, int.MaxValue); + return await Task.Run(GetComputers); - return result.Enumerate.Where(item => item.IsFolder).Select(item => + unsafe IEnumerable GetComputers() { - var networkItem = new DriveItem() + HRESULT hr = default; + + // Get IShellItem of the shell folder + var shellItemIid = typeof(IShellItem).GUID; + using ComPtr pFolderShellItem = default; + fixed (char* pszFolderShellPath = "Shell:::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}") + hr = PInvoke.SHCreateItemFromParsingName(pszFolderShellPath, null, &shellItemIid, (void**)pFolderShellItem.GetAddressOf()); + + // Get IEnumShellItems of the shell folder + var enumItemsBHID = PInvoke.BHID_EnumItems; + Guid enumShellItemIid = typeof(IEnumShellItems).GUID; + using ComPtr pEnumShellItems = default; + hr = pFolderShellItem.Get()->BindToHandler(null, &enumItemsBHID, &enumShellItemIid, (void**)pEnumShellItems.GetAddressOf()); + + // Enumerate items and populate the list + List items = []; + using ComPtr pShellItem = default; + while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK) { - Text = item.FileName, - Path = item.FilePath, - DeviceID = item.FilePath, - Type = DriveType.Network, - ItemType = NavigationControlItemType.Drive, - }; - - networkItem.MenuOptions = new ContextMenuOptions() - { - IsLocationItem = true, - ShowEjectDevice = networkItem.IsRemovable, - ShowShellItems = true, - ShowProperties = true, - }; + // Get only folders + if (pShellItem.Get()->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var attribute) == HRESULT.S_OK && + (attribute & SFGAO_FLAGS.SFGAO_FOLDER) is not SFGAO_FLAGS.SFGAO_FOLDER) + continue; + + // Get the display name + pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var szDisplayName); + var fileName = szDisplayName.ToString(); + PInvoke.CoTaskMemFree(szDisplayName.Value); + + // Get the file system path on disk + pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out szDisplayName); + var filePath = szDisplayName.ToString(); + PInvoke.CoTaskMemFree(szDisplayName.Value); + + var item = new DriveItem() + { + Text = fileName, + Path = filePath, + DeviceID = filePath, + Type = DriveType.Network, + ItemType = NavigationControlItemType.Drive, + MenuOptions = new() + { + IsLocationItem = true, + ShowShellItems = true, + ShowProperties = true, + }, + }; - return networkItem; - }); + item.MenuOptions.ShowEjectDevice = item.IsRemovable; + + items.Add(item); + } + + return items; + } } /// public async Task> GetShortcutsAsync() { - var networkLocations = await Win32Helper.StartSTATask(() => + return await Task.Run(GetShortcuts); + + unsafe IEnumerable GetShortcuts() { - var locations = new List(); - using (var netHood = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_NetHood)) + // Get IShellItem of the known folder + using ComPtr pShellFolder = default; + var folderId = PInvoke.FOLDERID_NetHood; + var shellItemIid = typeof(IShellItem).GUID; + HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, &shellItemIid, (void**)pShellFolder.GetAddressOf()); + + // Get IEnumShellItems for Recycle Bin folder + using ComPtr pEnumShellItems = default; + Guid enumShellItemGuid = typeof(IEnumShellItems).GUID; + var enumItemsBHID = BHID.BHID_EnumItems; + hr = pShellFolder.Get()->BindToHandler(null, &enumItemsBHID, &enumShellItemGuid, (void**)pEnumShellItems.GetAddressOf()); + + List items = []; + using ComPtr pShellItem = default; + while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK) { - foreach (var item in netHood) + // Get the target path + using ComPtr pShellLink = default; + var shellLinkIid = typeof(IShellLinkW).GUID; + pShellItem.Get()->QueryInterface(&shellLinkIid, (void**)pShellLink.GetAddressOf()); + string targetPath = string.Empty; + if (pShellLink.IsNull) { - if (item is ShellLink link) - { - locations.Add(ShellFolderExtensions.GetShellLinkItem(link)); - } - else + using ComPtr pShellItem2 = default; + var shellItem2Iid = typeof(IShellItem2).GUID; + pShellItem.Get()->QueryInterface(&shellItem2Iid, (void**)pShellItem2.GetAddressOf()); + PInvoke.PSGetPropertyKeyFromName("System.Link.TargetParsingPath", out var propertyKey); + pShellItem2.Get()->GetString(propertyKey, out var pszTargetPath); + targetPath = Environment.ExpandEnvironmentVariables(pszTargetPath.ToString()); + + // Test 1 + pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var szDisplayNameTest); + var filePathTest = szDisplayNameTest.ToString(); + PInvoke.CoTaskMemFree(szDisplayNameTest.Value); // break here + + // Test 2 + pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEPARSING, out szDisplayNameTest); + filePathTest = szDisplayNameTest.ToString(); + PInvoke.CoTaskMemFree(szDisplayNameTest.Value); // break here + } + else + { + fixed (char* pszTargetPath = new char[1024]) { - var linkPath = (string?)item?.Properties["System.Link.TargetParsingPath"]; - if (linkPath is not null) - { - var linkItem = ShellFolderExtensions.GetShellFileItem(item); - locations.Add(new(linkItem) { TargetPath = linkPath }); - } + hr = pShellLink.Get()->GetPath(pszTargetPath, 1024, null, (uint)SLGP_FLAGS.SLGP_RAWPATH); + targetPath = Environment.ExpandEnvironmentVariables(new PWSTR(pszTargetPath).ToString()); } } + + // Get the display name + pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var szDisplayName); + var fileName = szDisplayName.ToString(); + PInvoke.CoTaskMemFree(szDisplayName.Value); + + // Get the file system path on disk + pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out szDisplayName); + var filePath = szDisplayName.ToString(); + PInvoke.CoTaskMemFree(szDisplayName.Value); + + var item = new DriveItem() + { + Text = fileName, + Path = targetPath, + DeviceID = filePath, + Type = DriveType.Network, + ItemType = NavigationControlItemType.Drive, + MenuOptions = new() + { + IsLocationItem = true, + ShowShellItems = true, + ShowProperties = true, + }, + }; + + item.MenuOptions.ShowEjectDevice = item.IsRemovable; + + items.Add(item); } - return locations; - }); - return (networkLocations ?? Enumerable.Empty()).Select(item => - { - var networkItem = new DriveItem() - { - Text = item.FileName, - Path = item.TargetPath, - DeviceID = item.FilePath, - Type = DriveType.Network, - ItemType = NavigationControlItemType.Drive, - }; - - networkItem.MenuOptions = new ContextMenuOptions() - { - IsLocationItem = true, - ShowEjectDevice = networkItem.IsRemovable, - ShowShellItems = true, - ShowProperties = true, - }; - return networkItem; - }); + return items; + } } /// From fc3499562b5695708af1db9eccb512d033697af2 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:19:58 +0900 Subject: [PATCH 2/2] Update StorageNetworkService.cs --- .../Services/Storage/StorageNetworkService.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Files.App/Services/Storage/StorageNetworkService.cs b/src/Files.App/Services/Storage/StorageNetworkService.cs index 75de4fdebf70..b2c6b42a6817 100644 --- a/src/Files.App/Services/Storage/StorageNetworkService.cs +++ b/src/Files.App/Services/Storage/StorageNetworkService.cs @@ -158,16 +158,6 @@ unsafe IEnumerable GetShortcuts() PInvoke.PSGetPropertyKeyFromName("System.Link.TargetParsingPath", out var propertyKey); pShellItem2.Get()->GetString(propertyKey, out var pszTargetPath); targetPath = Environment.ExpandEnvironmentVariables(pszTargetPath.ToString()); - - // Test 1 - pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var szDisplayNameTest); - var filePathTest = szDisplayNameTest.ToString(); - PInvoke.CoTaskMemFree(szDisplayNameTest.Value); // break here - - // Test 2 - pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEPARSING, out szDisplayNameTest); - filePathTest = szDisplayNameTest.ToString(); - PInvoke.CoTaskMemFree(szDisplayNameTest.Value); // break here } else {