11// Copyright (c) Files Community
22// Licensed under the MIT License.
33
4+ using System . Runtime . CompilerServices ;
5+ using System . Text ;
46using Windows . Win32 ;
57using Windows . Win32 . Foundation ;
68using Windows . Win32 . System . SystemServices ;
79using Windows . Win32 . UI . Shell ;
10+ using Windows . Win32 . UI . Shell . PropertiesSystem ;
11+ using Windows . Win32 . UI . WindowsAndMessaging ;
812
913namespace Files . App . Storage
1014{
@@ -13,14 +17,27 @@ public static partial class WindowsStorableHelpers
1317 public unsafe static HRESULT GetPropertyValue < TValue > ( this IWindowsStorable storable , string propKey , out TValue value )
1418 {
1519 using ComPtr < IShellItem2 > pShellItem2 = default ;
16- var shellItem2Iid = typeof ( IShellItem2 ) . GUID ;
17- HRESULT hr = storable . ThisPtr . Get ( ) ->QueryInterface ( & shellItem2Iid , ( void * * ) pShellItem2 . GetAddressOf ( ) ) ;
18- hr = PInvoke . PSGetPropertyKeyFromName ( propKey , out var originalPathPropertyKey ) ;
19- hr = pShellItem2 . Get ( ) ->GetString ( originalPathPropertyKey , out var szOriginalPath ) ;
20+ HRESULT hr = storable . ThisPtr . Get ( ) ->QueryInterface ( IID . IID_IShellItem2 , ( void * * ) pShellItem2 . GetAddressOf ( ) ) ;
21+
22+ PROPERTYKEY propertyKey = default ;
23+ fixed ( char * pszPropertyKey = propKey )
24+ hr = PInvoke . PSGetPropertyKeyFromName ( pszPropertyKey , & propertyKey ) ;
2025
2126 if ( typeof ( TValue ) == typeof ( string ) )
2227 {
23- value = ( TValue ) ( object ) szOriginalPath . ToString ( ) ;
28+ ComHeapPtr < PWSTR > szPropertyValue = default ;
29+ hr = pShellItem2 . Get ( ) ->GetString ( & propertyKey , szPropertyValue . Get ( ) ) ;
30+ value = ( TValue ) ( object ) szPropertyValue . Get ( ) ->ToString ( ) ;
31+
32+ return hr ;
33+ }
34+ if ( typeof ( TValue ) == typeof ( bool ) )
35+ {
36+ bool propertyValue = false ;
37+ hr = pShellItem2 . Get ( ) ->GetBool ( propertyKey , out var fPropertyValue ) ;
38+ propertyValue = fPropertyValue ;
39+ value = Unsafe . As < bool , TValue > ( ref propertyValue ) ;
40+
2441 return hr ;
2542 }
2643 else
@@ -51,5 +68,61 @@ public unsafe static string GetDisplayName(this IWindowsStorable storable, SIGDN
5168 ? new string ( ( char * ) pszName . Get ( ) ) // this is safe as it gets memcpy'd internally
5269 : string . Empty ;
5370 }
71+
72+ public unsafe static HRESULT TryInvokeContextMenuVerb ( this IWindowsStorable storable , string verbName )
73+ {
74+ Debug . Assert ( Thread . CurrentThread . GetApartmentState ( ) is ApartmentState . STA ) ;
75+
76+ using ComPtr < IContextMenu > pContextMenu = default ;
77+ HRESULT hr = storable . ThisPtr . Get ( ) ->BindToHandler ( null , BHID . BHID_SFUIObject , IID . IID_IContextMenu , ( void * * ) pContextMenu . GetAddressOf ( ) ) ;
78+ HMENU hMenu = PInvoke . CreatePopupMenu ( ) ;
79+ hr = pContextMenu . Get ( ) ->QueryContextMenu ( hMenu , 0 , 1 , 0x7FFF , PInvoke . CMF_OPTIMIZEFORINVOKE ) ;
80+
81+ CMINVOKECOMMANDINFO cmici = default ;
82+ cmici . cbSize = ( uint ) sizeof ( CMINVOKECOMMANDINFO ) ;
83+ cmici . nShow = ( int ) SHOW_WINDOW_CMD . SW_HIDE ;
84+
85+ fixed ( byte * pszVerbName = Encoding . ASCII . GetBytes ( verbName ) )
86+ {
87+ cmici . lpVerb = new ( pszVerbName ) ;
88+ hr = pContextMenu . Get ( ) ->InvokeCommand ( cmici ) ;
89+
90+ if ( ! PInvoke . DestroyMenu ( hMenu ) )
91+ return HRESULT . E_FAIL ;
92+
93+ return hr ;
94+ }
95+ }
96+
97+ public unsafe static HRESULT TryInvokeContextMenuVerbs ( this IWindowsStorable storable , string [ ] verbNames , bool earlyReturnOnSuccess )
98+ {
99+ Debug . Assert ( Thread . CurrentThread . GetApartmentState ( ) is ApartmentState . STA ) ;
100+
101+ using ComPtr < IContextMenu > pContextMenu = default ;
102+ HRESULT hr = storable . ThisPtr . Get ( ) ->BindToHandler ( null , BHID . BHID_SFUIObject , IID . IID_IContextMenu , ( void * * ) pContextMenu . GetAddressOf ( ) ) ;
103+ HMENU hMenu = PInvoke . CreatePopupMenu ( ) ;
104+ hr = pContextMenu . Get ( ) ->QueryContextMenu ( hMenu , 0 , 1 , 0x7FFF , PInvoke . CMF_OPTIMIZEFORINVOKE ) ;
105+
106+ CMINVOKECOMMANDINFO cmici = default ;
107+ cmici . cbSize = ( uint ) sizeof ( CMINVOKECOMMANDINFO ) ;
108+ cmici . nShow = ( int ) SHOW_WINDOW_CMD . SW_HIDE ;
109+
110+ foreach ( var verbName in verbNames )
111+ {
112+ fixed ( byte * pszVerbName = Encoding . ASCII . GetBytes ( verbName ) )
113+ {
114+ cmici . lpVerb = new ( pszVerbName ) ;
115+ hr = pContextMenu . Get ( ) ->InvokeCommand ( cmici ) ;
116+
117+ if ( ! PInvoke . DestroyMenu ( hMenu ) )
118+ return HRESULT . E_FAIL ;
119+
120+ if ( hr . Succeeded && earlyReturnOnSuccess )
121+ return hr ;
122+ }
123+ }
124+
125+ return hr ;
126+ }
54127 }
55128}
0 commit comments