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,180 @@ 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 display name
92+ pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_NORMALDISPLAY , out var szDisplayName ) ;
93+ var fileName = szDisplayName . ToString ( ) ;
94+ PInvoke . CoTaskMemFree ( szDisplayName . Value ) ;
95+
96+ // Get the file system path on disk
97+ pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_FILESYSPATH , out szDisplayName ) ;
98+ var filePath = szDisplayName . ToString ( ) ;
99+ PInvoke . CoTaskMemFree ( szDisplayName . Value ) ;
100+
101+ var item = new DriveItem ( )
102+ {
103+ Text = fileName ,
104+ Path = filePath ,
105+ DeviceID = filePath ,
106+ Type = DriveType . Network ,
107+ ItemType = NavigationControlItemType . Drive ,
108+ MenuOptions = new ( )
109+ {
110+ IsLocationItem = true ,
111+ ShowShellItems = true ,
112+ ShowProperties = true ,
113+ } ,
114+ } ;
86115
87- return networkItem ;
88- } ) ;
116+ item . MenuOptions . ShowEjectDevice = item . IsRemovable ;
117+
118+ items . Add ( item ) ;
119+ }
120+
121+ return items ;
122+ }
89123 }
90124
91125 /// <inheritdoc/>
92126 public async Task < IEnumerable < ILocatableFolder > > GetShortcutsAsync ( )
93127 {
94- var networkLocations = await Win32Helper . StartSTATask ( ( ) =>
128+ return await Task . Run ( GetShortcuts ) ;
129+
130+ unsafe IEnumerable < ILocatableFolder > GetShortcuts ( )
95131 {
96- var locations = new List < ShellLinkItem > ( ) ;
97- using ( var netHood = new ShellFolder ( Shell32 . KNOWNFOLDERID . FOLDERID_NetHood ) )
132+ // Get IShellItem of the known folder
133+ using ComPtr < IShellItem > pShellFolder = default ;
134+ var folderId = PInvoke . FOLDERID_NetHood ;
135+ var shellItemIid = typeof ( IShellItem ) . GUID ;
136+ HRESULT hr = PInvoke . SHGetKnownFolderItem ( & folderId , KNOWN_FOLDER_FLAG . KF_FLAG_DEFAULT , HANDLE . Null , & shellItemIid , ( void * * ) pShellFolder . GetAddressOf ( ) ) ;
137+
138+ // Get IEnumShellItems for Recycle Bin folder
139+ using ComPtr < IEnumShellItems > pEnumShellItems = default ;
140+ Guid enumShellItemGuid = typeof ( IEnumShellItems ) . GUID ;
141+ var enumItemsBHID = BHID . BHID_EnumItems ;
142+ hr = pShellFolder . Get ( ) ->BindToHandler ( null , & enumItemsBHID , & enumShellItemGuid , ( void * * ) pEnumShellItems . GetAddressOf ( ) ) ;
143+
144+ List < ILocatableFolder > items = [ ] ;
145+ using ComPtr < IShellItem > pShellItem = default ;
146+ while ( pEnumShellItems . Get ( ) ->Next ( 1 , pShellItem . GetAddressOf ( ) ) == HRESULT . S_OK )
98147 {
99- foreach ( var item in netHood )
148+ // Get the target path
149+ using ComPtr < IShellLinkW > pShellLink = default ;
150+ var shellLinkIid = typeof ( IShellLinkW ) . GUID ;
151+ pShellItem . Get ( ) ->QueryInterface ( & shellLinkIid , ( void * * ) pShellLink . GetAddressOf ( ) ) ;
152+ string targetPath = string . Empty ;
153+ if ( pShellLink . IsNull )
100154 {
101- if ( item is ShellLink link )
102- {
103- locations . Add ( ShellFolderExtensions . GetShellLinkItem ( link ) ) ;
104- }
105- else
155+ using ComPtr < IShellItem2 > pShellItem2 = default ;
156+ var shellItem2Iid = typeof ( IShellItem2 ) . GUID ;
157+ pShellItem . Get ( ) ->QueryInterface ( & shellItem2Iid , ( void * * ) pShellItem2 . GetAddressOf ( ) ) ;
158+ PInvoke . PSGetPropertyKeyFromName ( "System.Link.TargetParsingPath" , out var propertyKey ) ;
159+ pShellItem2 . Get ( ) ->GetString ( propertyKey , out var pszTargetPath ) ;
160+ targetPath = Environment . ExpandEnvironmentVariables ( pszTargetPath . ToString ( ) ) ;
161+
162+ // Test 1
163+ pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_DESKTOPABSOLUTEPARSING , out var szDisplayNameTest ) ;
164+ var filePathTest = szDisplayNameTest . ToString ( ) ;
165+ PInvoke . CoTaskMemFree ( szDisplayNameTest . Value ) ; // break here
166+
167+ // Test 2
168+ pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_PARENTRELATIVEPARSING , out szDisplayNameTest ) ;
169+ filePathTest = szDisplayNameTest . ToString ( ) ;
170+ PInvoke . CoTaskMemFree ( szDisplayNameTest . Value ) ; // break here
171+ }
172+ else
173+ {
174+ fixed ( char * pszTargetPath = new char [ 1024 ] )
106175 {
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- }
176+ hr = pShellLink . Get ( ) ->GetPath ( pszTargetPath , 1024 , null , ( uint ) SLGP_FLAGS . SLGP_RAWPATH ) ;
177+ targetPath = Environment . ExpandEnvironmentVariables ( new PWSTR ( pszTargetPath ) . ToString ( ) ) ;
113178 }
114179 }
180+
181+ // Get the display name
182+ pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_NORMALDISPLAY , out var szDisplayName ) ;
183+ var fileName = szDisplayName . ToString ( ) ;
184+ PInvoke . CoTaskMemFree ( szDisplayName . Value ) ;
185+
186+ // Get the file system path on disk
187+ pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_FILESYSPATH , out szDisplayName ) ;
188+ var filePath = szDisplayName . ToString ( ) ;
189+ PInvoke . CoTaskMemFree ( szDisplayName . Value ) ;
190+
191+ var item = new DriveItem ( )
192+ {
193+ Text = fileName ,
194+ Path = targetPath ,
195+ DeviceID = filePath ,
196+ Type = DriveType . Network ,
197+ ItemType = NavigationControlItemType . Drive ,
198+ MenuOptions = new ( )
199+ {
200+ IsLocationItem = true ,
201+ ShowShellItems = true ,
202+ ShowProperties = true ,
203+ } ,
204+ } ;
205+
206+ item . MenuOptions . ShowEjectDevice = item . IsRemovable ;
207+
208+ items . Add ( item ) ;
115209 }
116- return locations ;
117- } ) ;
118210
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- } ) ;
211+ return items ;
212+ }
139213 }
140214
141215 /// <inheritdoc/>
0 commit comments