22// Licensed under the MIT License.
33
44using System . Runtime . CompilerServices ;
5+ using System . Runtime . InteropServices ;
56using System . Text ;
67using Windows . Win32 ;
78using Windows . Win32 . Foundation ;
89using Windows . Win32 . System . Com ;
910using Windows . Win32 . System . SystemServices ;
1011using Windows . Win32 . UI . Shell ;
12+ using Windows . Win32 . UI . Shell . Common ;
1113using Windows . Win32 . UI . Shell . PropertiesSystem ;
1214using Windows . Win32 . UI . WindowsAndMessaging ;
1315
1416namespace Files . App . Storage
1517{
16- public static partial class WindowsStorableHelpers
18+ public unsafe static partial class WindowsStorableHelpers
1719 {
18- public unsafe static HRESULT GetPropertyValue < TValue > ( this IWindowsStorable storable , string propKey , out TValue value )
20+ public static HRESULT GetPropertyValue < TValue > ( this IWindowsStorable storable , string propKey , out TValue value )
1921 {
2022 using ComPtr < IShellItem2 > pShellItem2 = default ;
2123 HRESULT hr = storable . ThisPtr ->QueryInterface ( IID . IID_IShellItem2 , ( void * * ) pShellItem2 . GetAddressOf ( ) ) ;
@@ -47,12 +49,12 @@ public unsafe static HRESULT GetPropertyValue<TValue>(this IWindowsStorable stor
4749 }
4850 }
4951
50- public unsafe static bool HasShellAttributes ( this IWindowsStorable storable , SFGAO_FLAGS attributes )
52+ public static bool HasShellAttributes ( this IWindowsStorable storable , SFGAO_FLAGS attributes )
5153 {
5254 return storable . ThisPtr ->GetAttributes ( attributes , out var dwRetAttributes ) . Succeeded && dwRetAttributes == attributes ;
5355 }
5456
55- public unsafe static string GetDisplayName ( this IWindowsStorable storable , SIGDN options = SIGDN . SIGDN_FILESYSPATH )
57+ public static string GetDisplayName ( this IWindowsStorable storable , SIGDN options = SIGDN . SIGDN_FILESYSPATH )
5658 {
5759 using ComHeapPtr < PWSTR > pszName = default ;
5860 HRESULT hr = storable . ThisPtr ->GetDisplayName ( options , ( PWSTR * ) pszName . GetAddressOf ( ) ) ;
@@ -62,7 +64,7 @@ public unsafe static string GetDisplayName(this IWindowsStorable storable, SIGDN
6264 : string . Empty ;
6365 }
6466
65- public unsafe static HRESULT TryInvokeContextMenuVerb ( this IWindowsStorable storable , string verbName )
67+ public static HRESULT TryInvokeContextMenuVerb ( this IWindowsStorable storable , string verbName )
6668 {
6769 Debug . Assert ( Thread . CurrentThread . GetApartmentState ( ) is ApartmentState . STA ) ;
6870
@@ -87,7 +89,7 @@ public unsafe static HRESULT TryInvokeContextMenuVerb(this IWindowsStorable stor
8789 }
8890 }
8991
90- public unsafe static HRESULT TryInvokeContextMenuVerbs ( this IWindowsStorable storable , string [ ] verbNames , bool earlyReturnOnSuccess )
92+ public static HRESULT TryInvokeContextMenuVerbs ( this IWindowsStorable storable , string [ ] verbNames , bool earlyReturnOnSuccess )
9193 {
9294 Debug . Assert ( Thread . CurrentThread . GetApartmentState ( ) is ApartmentState . STA ) ;
9395
@@ -118,7 +120,7 @@ public unsafe static HRESULT TryInvokeContextMenuVerbs(this IWindowsStorable sto
118120 return hr ;
119121 }
120122
121- public unsafe static HRESULT TryGetShellTooltip ( this IWindowsStorable storable , out string ? tooltip )
123+ public static HRESULT TryGetShellTooltip ( this IWindowsStorable storable , out string ? tooltip )
122124 {
123125 tooltip = null ;
124126
@@ -137,7 +139,7 @@ public unsafe static HRESULT TryGetShellTooltip(this IWindowsStorable storable,
137139 return HRESULT . S_OK ;
138140 }
139141
140- public unsafe static HRESULT TryPinFolderToQuickAccess ( this IWindowsFolder @this )
142+ public static HRESULT TryPinFolderToQuickAccess ( this IWindowsFolder @this )
141143 {
142144 HRESULT hr = default ;
143145
@@ -168,7 +170,7 @@ public unsafe static HRESULT TryPinFolderToQuickAccess(this IWindowsFolder @this
168170 return HRESULT . S_OK ;
169171 }
170172
171- public unsafe static HRESULT TryUnpinFolderFromQuickAccess ( this IWindowsFolder @this )
173+ public static HRESULT TryUnpinFolderFromQuickAccess ( this IWindowsFolder @this )
172174 {
173175 HRESULT hr = default ;
174176
@@ -198,5 +200,105 @@ public unsafe static HRESULT TryUnpinFolderFromQuickAccess(this IWindowsFolder @
198200
199201 return HRESULT . S_OK ;
200202 }
203+
204+ public static IEnumerable < ContextMenuItem > GetShellNewItems ( this IWindowsFolder @this )
205+ {
206+ HRESULT hr = default ;
207+
208+ IContextMenu * pNewMenu = default ;
209+ using ComPtr < IShellExtInit > pShellExtInit = default ;
210+ using ComPtr < IContextMenu2 > pContextMenu2 = default ;
211+
212+ hr = PInvoke . CoCreateInstance ( CLSID . CLSID_NewMenu , null , CLSCTX . CLSCTX_INPROC_SERVER , IID . IID_IContextMenu , ( void * * ) & pNewMenu ) ;
213+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
214+ return [ ] ;
215+
216+ hr = pNewMenu ->QueryInterface ( IID . IID_IContextMenu2 , ( void * * ) pContextMenu2 . GetAddressOf ( ) ) ;
217+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
218+ return [ ] ;
219+
220+ hr = pNewMenu ->QueryInterface ( IID . IID_IShellExtInit , ( void * * ) pShellExtInit . GetAddressOf ( ) ) ;
221+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
222+ return [ ] ;
223+
224+ @this . ShellNewMenu = pNewMenu ;
225+
226+ ITEMIDLIST * pFolderPidl = default ;
227+ hr = PInvoke . SHGetIDListFromObject ( ( IUnknown * ) @this . ThisPtr , & pFolderPidl ) ;
228+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
229+ return [ ] ;
230+
231+ hr = pShellExtInit . Get ( ) ->Initialize ( pFolderPidl , null , default ) ;
232+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
233+ return [ ] ;
234+
235+ // Inserts "New (&W)"
236+ HMENU hMenu = PInvoke . CreatePopupMenu ( ) ;
237+ hr = pNewMenu ->QueryContextMenu ( hMenu , 0 , 1 , 256 , 0 ) ;
238+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
239+ return [ ] ;
240+
241+ // Invokes CNewMenu::_InitMenuPopup(), which populates the hSubMenu
242+ HMENU hSubMenu = PInvoke . GetSubMenu ( hMenu , 0 ) ;
243+ hr = pContextMenu2 . Get ( ) ->HandleMenuMsg ( PInvoke . WM_INITMENUPOPUP , ( WPARAM ) ( nuint ) hSubMenu . Value , 0 ) ;
244+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
245+ return [ ] ;
246+
247+ uint dwCount = unchecked ( ( uint ) PInvoke . GetMenuItemCount ( hSubMenu ) ) ;
248+ if ( dwCount is unchecked ( ( uint ) - 1 ) )
249+ return [ ] ;
250+
251+ // Enumerates and populates the list
252+ List < ContextMenuItem > shellNewItems = [ ] ;
253+ for ( uint dwIndex = 0 ; dwIndex < dwCount ; dwIndex ++ )
254+ {
255+ MENUITEMINFOW mii = default ;
256+ mii . cbSize = ( uint ) sizeof ( MENUITEMINFOW ) ;
257+ mii . fMask = MENU_ITEM_MASK . MIIM_STRING | MENU_ITEM_MASK . MIIM_ID | MENU_ITEM_MASK . MIIM_STATE ;
258+ mii . dwTypeData = ( char * ) NativeMemory . Alloc ( 256U ) ;
259+ mii . cch = 256 ;
260+
261+ if ( PInvoke . GetMenuItemInfo ( hSubMenu , dwIndex , true , & mii ) )
262+ {
263+ shellNewItems . Add ( new ( )
264+ {
265+ Id = mii . wID ,
266+ Name = mii . dwTypeData . ToString ( ) ,
267+ Type = ( ContextMenuType ) mii . fState ,
268+ } ) ;
269+ }
270+
271+ NativeMemory . Free ( mii . dwTypeData ) ;
272+ }
273+
274+ return shellNewItems ;
275+ }
276+
277+ public static bool InvokeShellNewItem ( this IWindowsFolder @this , ContextMenuItem item )
278+ {
279+ HRESULT hr = default ;
280+
281+ if ( @this . ShellNewMenu is null )
282+ {
283+ IContextMenu * pNewMenu = default ;
284+
285+ hr = PInvoke . CoCreateInstance ( CLSID . CLSID_NewMenu , null , CLSCTX . CLSCTX_INPROC_SERVER , IID . IID_IContextMenu , ( void * * ) & pNewMenu ) ;
286+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
287+ return false ;
288+
289+ @this . ShellNewMenu = pNewMenu ;
290+ }
291+
292+ CMINVOKECOMMANDINFO cmici = default ;
293+ cmici . cbSize = ( uint ) sizeof ( CMINVOKECOMMANDINFO ) ;
294+ cmici . lpVerb = ( PCSTR ) ( byte * ) item . Id ;
295+ cmici . nShow = ( int ) SHOW_WINDOW_CMD . SW_SHOWNORMAL ;
296+
297+ hr = @this . ShellNewMenu ->InvokeCommand ( & cmici ) ;
298+ if ( hr . ThrowIfFailedOnDebug ( ) . Failed )
299+ return false ;
300+
301+ return false ;
302+ }
201303 }
202304}
0 commit comments