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 ;
5+ using Windows . Networking . Connectivity ;
86using Windows . Win32 ;
97using Windows . Win32 . Foundation ;
108using Windows . Win32 . NetworkManagement . WNet ;
119using Windows . Win32 . Security . Credentials ;
10+ using Windows . Win32 . System . SystemServices ;
11+ using Windows . Win32 . UI . Shell ;
1212
1313namespace 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