11// Copyright (c) 2024 Files Community
22// Licensed under the MIT License. See the LICENSE.
33
4- using System . Runtime . InteropServices ;
54using System . Text ;
6- using Vanara . PInvoke ;
7- using Vanara . Windows . Shell ;
85using Windows . Win32 ;
96using Windows . Win32 . Foundation ;
107using Windows . Win32 . NetworkManagement . WNet ;
118using Windows . Win32 . Security . Credentials ;
9+ using Windows . Win32 . System . SystemServices ;
10+ using Windows . Win32 . UI . Shell ;
1211
1312namespace 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