2020-02-26 17:18:06 -05:00
using Microsoft.Win32 ;
using System ;
2018-08-29 14:59:20 -04:00
using Vanara.PInvoke ;
namespace Vanara.Windows.Shell
{
2020-02-26 17:18:06 -05:00
/// <summary>A value that determines if a user can select a single item, multiple items, or a selection from an item.</summary>
[Flags]
public enum VerbMultiSelectModel
{
/// <summary>
/// Inferred from the type of verb implementation you have chosen. For COM-based methods (such as DropTarget and ExecuteCommand)
/// Player is assumed, and for the other methods Document is assumed.
/// </summary>
Unset = 0 ,
2018-08-29 14:59:20 -04:00
2020-02-26 17:18:06 -05:00
/// <summary>Support any number of items.</summary>
Player = 1 ,
2018-08-29 14:59:20 -04:00
2020-02-26 17:18:06 -05:00
/// <summary>Support only a single selection.</summary>
Single = 2 ,
/// <summary>
/// Create a top level window for each item. Doing so limits the number of items activated and helps avoid running out of system
/// resources if the user opens too many windows.
/// </summary>
Document = 4
}
/// <summary>Determines the placement of a verb in a menu.</summary>
public enum VerbPosition
{
/// <summary>The menu position is undefined.</summary>
Undefined ,
/// <summary>The menu should be displayed at the top.</summary>
Top ,
/// <summary>The menu should be displayed at the bottom.</summary>
Bottom
}
/// <summary>Determines menu location.</summary>
public enum VerbSelectionModel
{
/// <summary>Specifies an item verb.</summary>
Item ,
/// <summary>Specifies a verb on the background shortcut menu.</summary>
BackgroundShortcutMenu
}
2018-08-29 14:59:20 -04:00
/// <summary>Encapsulates a shortcut menu verb in the registry.</summary>
public class CommandVerb : RegBasedSettings , IEquatable < CommandVerb >
{
internal CommandVerb ( RegistryKey key , string name , bool readOnly = true ) : base ( key , readOnly )
{
Name = name ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets an Advanced Query Syntax (AQS) expression that determines whether the verb is displayed or hidden.</summary>
/// <value>The AQS expression that controls visibility.</value>
2018-08-29 14:59:20 -04:00
public string AppliesTo
{
get = > key . GetValue ( "AppliesTo" , null ) ? . ToString ( ) ;
set = > UpdateValue ( "AppliesTo" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets the SFGAO value of the bit values of the mask to test to determine whether the verb should be enabled or disabled.
/// </summary>
/// <value>The attribute mask.</value>
2018-08-29 14:59:20 -04:00
public Shell32 . SFGAO AttributeMask
{
get = > ( Shell32 . SFGAO ) ( int ) key . GetValue ( "AttributeMask" , 0 ) ;
set = > UpdateValue ( "AttributeMask" , ( int ) value , RegistryValueKind . DWord ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets the SFGAO value of the bits that are tested to determine whether the verb should be enabled or disabled.</summary>
/// <value>The attribute value.</value>
2018-08-29 14:59:20 -04:00
public Shell32 . SFGAO AttributeValue
{
get = > ( Shell32 . SFGAO ) ( int ) key . GetValue ( "AttributeValue" , 0 ) ;
set = > UpdateValue ( "AttributeValue" , ( int ) value , RegistryValueKind . DWord ) ;
}
/// <summary>Gets or sets the command string used to launch the command in a console window or batch (.bat) file.</summary>
/// <value>The command string.</value>
/// <remarks>
/// If any element of the command string contains or might contain spaces, it must be enclosed in quotation marks. Otherwise, if the
2020-02-26 17:18:06 -05:00
/// element contains a space, it will not parse correctly. For instance, "My Program.exe" starts the application properly. If you
/// use My Program.exe without quotation marks, then the system attempts to launch My with Program.exe as its first command line
2018-08-29 14:59:20 -04:00
/// argument. You should always use quotation marks with arguments such as "%1" that are expanded to strings by the Shell, because
/// you cannot be certain that the string will not contain a space.
/// </remarks>
public string Command
{
get = > key . GetSubKeyDefaultValue ( "command" ) ? . ToString ( ) ;
set = > UpdateKeyValue ( "command" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets the optional CLSID of an object that implments <c>IExplorerCommandState</c>.</summary>
/// <value>The command state handler.</value>
2018-08-29 14:59:20 -04:00
public Guid ? CommandStateHandler
{
get = > key . GetGuidValue ( "CommandStateHandler" ) ;
2019-01-27 17:42:41 -05:00
set = > UpdateValue ( "CommandStateHandler" , value ) ;
2018-08-29 14:59:20 -04:00
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets an Advanced Query Syntax (AQS) expression that controls which verb is the default.</summary>
/// <value>The AQS expression that controls which verb is the default.</value>
2018-08-29 14:59:20 -04:00
public string DefaultAppliesTo
{
get = > key . GetValue ( "DefaultAppliesTo" , null ) ? . ToString ( ) ;
set = > UpdateValue ( "DefaultAppliesTo" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets the optional CLSID value of the inproc extension that handles an excution within Windows Explorer or IE. For
/// Windows Explorer, this value should be also assigned to the <see cref="ExplorerCommandHandler"/> value and exclusively
/// associated with a <see cref="Command"/> value of "%SYSTEMROOT%\Explorer.exe". For IE, this value should be exclusively
/// associated with a <see cref="Command"/> value of <c>"C:\Program Files\Internet Explorer\iexplore.exe" %1</c>.
/// </summary>
/// <value>The explorer command handler CLSID.</value>
2018-08-29 14:59:20 -04:00
public Guid ? DelegateExecute
{
2019-01-31 13:39:03 -05:00
get
{
2020-02-26 17:18:06 -05:00
using var sk = key . OpenSubKey ( "command" ) ;
return sk ? . GetGuidValue ( "DelegateExecute" ) ;
2019-01-31 13:39:03 -05:00
}
set
{
2020-02-26 17:18:06 -05:00
EnsureWritable ( ) ;
2019-01-31 13:39:03 -05:00
if ( ! value . HasValue )
{
try { key . DeleteSubKeyTree ( "command" ) ; } catch { }
}
else
{
2020-02-26 17:18:06 -05:00
using var sk = key . CreateSubKey ( "command" ) ;
sk ? . SetValue ( "DelegateExecute" , value . Value . ToRegString ( ) ) ;
2019-01-31 13:39:03 -05:00
}
}
2018-08-29 14:59:20 -04:00
}
/// <summary>
/// Gets or sets an optional display name associated with them, which is displayed on the shortcut menu instead of the verb string
/// itself. For example, the display string for <c>openas</c> is Open With. Like normal menu strings, including an ampersand
/// character in the display string allows keyboard selection of the command.
/// </summary>
/// <value>The display name for the verb.</value>
public string DisplayName
{
get = > key . GetValue ( "" , null ) ? . ToString ( ) ;
set = > UpdateValue ( "" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets the optional CLSID of the object that implements <c>IDropTarget</c>.</summary>
/// <value>The drop target handler's CLSID.</value>
2018-08-29 14:59:20 -04:00
public Guid ? DropTarget
{
get
{
2020-02-26 17:18:06 -05:00
using var sk = key . OpenSubKey ( "DropTarget" ) ;
return sk ? . GetGuidValue ( "CLSID" ) ;
2018-08-29 14:59:20 -04:00
}
set
{
2020-02-26 17:18:06 -05:00
EnsureWritable ( ) ;
2018-08-29 14:59:20 -04:00
if ( ! value . HasValue )
{
try { key . DeleteSubKeyTree ( "DropTarget" ) ; } catch { }
}
else
{
2020-02-26 17:18:06 -05:00
using var sk = key . CreateSubKey ( "DropTarget" ) ;
sk ? . SetValue ( "CLSID" , value . Value . ToRegString ( ) ) ;
2018-08-29 14:59:20 -04:00
}
}
}
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets the optional CLSID value of the inproc explorer extension that handles this verb's execution. This value should be
/// also assigned to the <see cref="DelegateExecute"/> value and is generally associated with a <see cref="Command"/> value of "%SYSTEMROOT%\Explorer.exe".
/// </summary>
/// <value>The explorer command handler CLSID.</value>
2018-08-29 14:59:20 -04:00
public Guid ? ExplorerCommandHandler
{
get = > key . GetGuidValue ( "ExplorerCommandHandler" ) ;
2019-01-27 17:42:41 -05:00
set = > UpdateValue ( "ExplorerCommandHandler" , value ) ;
2018-08-29 14:59:20 -04:00
}
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets a value that marks the command as extended and will be displayed only when the user right-clicks an object while
/// also pressing the SHIFT key.
/// </summary>
/// <value>
/// <see langword="true"/> if command is only displayed only when the user right-clicks an object while also pressing the SHIFT key;
/// otherwise, <see langword="false"/>.
/// </value>
2018-08-29 14:59:20 -04:00
public bool Extended
{
get = > key . HasValue ( "Extended" ) ;
set = > ToggleValue ( "Extended" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets an Advanced Query Syntax (AQS) expression that controls whether a User Account Control (UAC) shield is displayed.
/// </summary>
/// <value>The AQS expression that controls whether a User Account Control (UAC) shield is displayed.</value>
2018-08-29 14:59:20 -04:00
public string HasLUAShield
{
get = > key . GetValue ( "HasLUAShield" , null ) ? . ToString ( ) ;
set = > UpdateValue ( "HasLUAShield" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets a value that determines on which menu the command is displayed.</summary>
/// <value>The implied selection model.</value>
2018-08-29 14:59:20 -04:00
public VerbSelectionModel ImpliedSelectionModel
{
get = > ( VerbSelectionModel ) ( int ) key . GetValue ( "ImpliedSelectionModel" , 0 ) ;
set = > UpdateValue ( "ImpliedSelectionModel" , ( int ) value , RegistryValueKind . DWord ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets a value that tells the system that the verb is not an actual verb, but exists solely for the purpose of backward compatibility.
/// </summary>
/// <value><see langword="true"/> if disabled; otherwise, <see langword="false"/>.</value>
2018-08-29 14:59:20 -04:00
public bool LegacyDisable
{
get = > key . HasValue ( "LegacyDisable" ) ;
set = > ToggleValue ( "LegacyDisable" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets a localizable value that is displayed as a menu's text.</summary>
/// <value>The menu's text.</value>
2018-08-29 14:59:20 -04:00
public IndirectString MUIVerb
{
get = > IndirectString . TryParse ( key . GetValue ( "MUIVerb" ) ? . ToString ( ) , out var loc ) ? loc : null ;
set = > UpdateValue ( "MUIVerb" , value ? . ToString ( ) ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets a value that determines if a user can select a single item, multiple items, or a selection from an item.
/// </summary>
/// <value>The multi select model for the menu.</value>
2018-08-29 14:59:20 -04:00
public VerbMultiSelectModel MultiSelectModel
{
2020-02-26 17:18:06 -05:00
get
{
var value = key . GetValue ( "MultiSelectModel" , VerbMultiSelectModel . Unset . ToString ( ) ) . ToString ( ) . Replace ( '|' , ',' ) . Replace ( " " , "" ) ;
return ( VerbMultiSelectModel ) Enum . Parse ( typeof ( VerbMultiSelectModel ) , value , true ) ;
}
set = > UpdateValue ( "MultiSelectModel" , value . ToString ( ) . Replace ( ", " , " | " ) , RegistryValueKind . String , VerbMultiSelectModel . Unset . ToString ( ) ) ;
2018-08-29 14:59:20 -04:00
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets the text string that is used by the Shell to identify the associated command.</summary>
2018-08-29 14:59:20 -04:00
/// <value>The verb name.</value>
2020-02-26 17:18:06 -05:00
public string Name { get ; }
2018-08-29 14:59:20 -04:00
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets a value that tells the system that this verb can never be displayed as the default verb for this file type.
/// </summary>
/// <value><see langword="true"/> if menu can never be set as default; otherwise, <see langword="false"/>.</value>
2018-08-29 14:59:20 -04:00
public bool NeverDefault
{
get = > key . HasValue ( "NeverDefault" ) ;
set = > ToggleValue ( "NeverDefault" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets a value that tells the system that this verb can only be displayed when in an Explorer window.</summary>
/// <value><see langword="true"/> if menu is only to be displayed when in an Explorer window; otherwise, <see langword="false"/>.</value>
2018-08-29 14:59:20 -04:00
public bool OnlyInBrowserWindow
{
get = > key . HasValue ( "OnlyInBrowserWindow" ) ;
set = > ToggleValue ( "OnlyInBrowserWindow" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>
/// Gets or sets a value that is used to place a verb at the top or bottom of the menu. If there are multiple verbs that specify
/// this attribute then the last one to do so gets priority
/// </summary>
/// <value>The verb menu placement.</value>
2018-08-29 14:59:20 -04:00
public VerbPosition Position
{
2020-02-26 17:18:06 -05:00
get = > ( VerbPosition ) Enum . Parse ( typeof ( VerbPosition ) , key . GetValue ( "Position" , VerbPosition . Undefined . ToString ( ) ) . ToString ( ) ) ;
set = > UpdateValue ( "Position" , value . ToString ( ) , RegistryValueKind . String , VerbPosition . Undefined . ToString ( ) ) ;
2018-08-29 14:59:20 -04:00
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets a value that tells the system that this verb is available for programmatic access only and not displayed.</summary>
/// <value><see langword="true"/> if verb is available for programmatic access only and not displayed; otherwise, <see langword="false"/>.</value>
2018-08-29 14:59:20 -04:00
public bool ProgrammaticAccessOnly
{
get = > key . HasValue ( "ProgrammaticAccessOnly" ) ;
set = > ToggleValue ( "ProgrammaticAccessOnly" , value ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets a value that tells the system to place a separator after this menu item.</summary>
/// <value><see langword="true"/> if a separator should be displayed after this menu item; otherwise, <see langword="false"/>.</value>
2018-08-29 14:59:20 -04:00
public bool SeparatorAfter
{
get = > 1 = = key . GetValue ( "SeparatorAfter" , 0 ) as int? ;
set = > UpdateValue ( "SeparatorAfter" , value ? 1 : 0 , RegistryValueKind . DWord ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets a value that tells the system to place a separator before this menu item.</summary>
/// <value><see langword="true"/> if a separator should be displayed before this menu item; otherwise, <see langword="false"/>.</value>
2018-08-29 14:59:20 -04:00
public bool SeparatorBefore
{
get = > 1 = = key . GetValue ( "SeparatorBefore" , 0 ) as int? ;
set = > UpdateValue ( "SeparatorBefore" , value ? 1 : 0 , RegistryValueKind . DWord ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets a value that controls if verb visibility can be suppressed through policy settings.</summary>
/// <value>A <see cref="Shell32.RESTRICTIONS"/> value.</value>
2018-08-29 14:59:20 -04:00
public Shell32 . RESTRICTIONS SuppressionPolicy
{
get = > ( Shell32 . RESTRICTIONS ) ( int ) key . GetValue ( "SuppressionPolicy" , 0 ) ;
set = > UpdateValue ( "SuppressionPolicy" , ( int ) value , RegistryValueKind . DWord ) ;
}
2020-02-26 17:18:06 -05:00
/// <summary>Gets or sets an optional CLSID for a handler that controls if verb visibility can be suppressed through policy settings.</summary>
/// <value>The handler's CLSID value.</value>
public Guid ? SuppressionPolicyEx
{
get = > key . GetGuidValue ( "SuppressionPolicyEx" ) ;
set = > UpdateValue ( "SuppressionPolicyEx" , value ) ;
}
/// <summary>Determines if another <see cref="CommandVerb"/> is equal to this instance.</summary>
/// <param name="other">The other <see cref="CommandVerb"/>.</param>
/// <returns><see langword="true"/> if the items are equal; otherwise <see langword="false"/>.</returns>
2018-08-29 14:59:20 -04:00
public bool Equals ( CommandVerb other ) = > Equals ( ( RegBasedSettings ) other ) ;
}
}