using System; using Vanara.PInvoke; using static Vanara.PInvoke.Ole32; using static Vanara.PInvoke.Shell32; namespace Vanara.Windows.Shell { /// /// Wraps the functionality of IExecuteCommand. To implement, derive from this class and override the method. All Shell items /// passed to the command are available through the property. /// /// /// This class provides an easy means of creating a COM based out-of-process DropTarget. /// /// Create a .NET application project. /// Delete the Program.cs file that includes the Main method. /// Create a new class and derive it from with the attributes in the example below. /// Ensure the project is built as "X86". /// /// // Full implementation of a shell context menu handler using IExecuteCommand. ///[ComVisible(true), Guid("<Your GUID here>"), ClassInterface(ClassInterfaceType.None)] ///public class MyExecCmd : ShellExecuteCommand ///{ /// // *** Replace with your ProgID, verb name and display name *** /// const string progID = "txtfile"; /// const string verbName = "ExecuteCommandVerb"; /// const string verbDisplayName = "ExecuteCommand Verb Sample"; /// /// // Overridden method performs all the functionality of your verb handler. The properties from ShellExecuteCommand should all be set /// // and can be used for your implementation. Once you have completed all work required of this command, you must call the /// // QuitMessageLoop method to finish processing all messages and then exit. If you fail to call QuitMessageLoop, this process will run /// // indefinitely and future context menu requests will not be handled. /// public override void OnExecute() /// { /// var szMsg = $"Found {SelectedItems.Count} item(s) called with '{CommandName}' verb: " + string.Join(", ", SelectedItems.Select(i => i.Name)); /// if (!UIDisplayBlocked) /// MessageBox.Show(szMsg, verbDisplayName); /// // Don't fail to call QuitMessageLoop once you're done processing the Shell call. /// QuitMessageLoop(); /// } /// /// // This is the main thread of the application. When called from the Shell, it will append the -embedding argument to indicate that a /// // message loop needs to start running and the COM server registered. All that is accomplished by calling the 'Run' method on your /// // new class. You may choose to register or unregister based on alternate command-line arguments. It is highly recommended that you /// // supply a timeout to the Run method to prevent a failure from leaving this process running indefinitely. The timeout is automatically /// // canceled once the OnExecute method is called so as to prevent long-running code from being terminated. You must specify the /// // [STAThread] attribute on this method for the handler to function. /// [STAThread] /// private static void Main(string[] args) /// { /// if (args.Length > 0 && args[0] == "-embedding") /// new MyExecCmd().Run(TimeSpan.FromSeconds(30)); /// } /// /// // This method registers this out-of-proc server to handle the DelegateExecute. This can be omitted if done by an installer. /// private static void Register() /// { /// ShellRegistrar.RegisterLocalServer<MyExecCmd>(verbDisplayName, systemWide: false); /// using (var progid = new ProgId(progID, false)) /// using (var verb = progid.Verbs.Add(verbName, verbDisplayName)) /// verb.DelegateExecute = Marshal.GenerateGuidForType(typeof(MyExecCmd)); /// } /// /// // This method unregisters this out-of-proc server from handling the DelegateExecute. This can be omitted if done by an uninstaller. /// private static void Unregister() /// { /// using (var progid = new ProgId(progID, false)) /// progid.Verbs.Remove(verbName); /// ShellRegistrar.UnregisterLocalServer<MyExecCmd>(false); /// } ///} /// /// public abstract class ShellExecuteCommand : ShellCommand, IExecuteCommand, IObjectWithSelection { /// Initializes a new instance of the class. protected ShellExecuteCommand() : base() { } /// Initializes a new instance of the class. /// The context within which the COM object is to be run. /// Indicates how connections are made to the class object. protected ShellExecuteCommand(CLSCTX classContext, REGCLS classUse) : base(classContext, classUse) { } /// Gets a value based on the current state of the keys CTRL and SHIFT. /// The value based on the current state of the keys CTRL and SHIFT. public MouseButtonState KeyState { get; private set; } /// Gets a new working directory. This value is if the current working directory is to be used. /// Returns a value. public string NewWorkingDirectory { get; private set; } /// Gets the parameter values for the verb. /// Returns a value. public string Parameters { get; private set; } /// /// Gets the screen coordinates at which the user right-clicked to invoke the shortcut menu from which a command was chosen. /// Applications can use this information to present any UI. This is particularly useful in a multi-monitor situation. The default /// position is the center of the default monitor. /// /// Returns a value. public POINT Position { get; private set; } /// Gets or sets the selected shell items. /// The selected shell items. public ShellItemArray SelectedItems { get; private set; } /// Gets a value indicating whether any UI associated with the selected Shell item should be displayed. /// if display of any associated UI is blocked; otherwise, . public bool UIDisplayBlocked { get; private set; } /// Gets the specified window's visual state. /// Returns a value. public ShowWindowCommand WindowState { get; private set; } /// Called in response to IExecuteCommand.Execute(). public abstract void OnExecute(); /// HRESULT IExecuteCommand.Execute() { QueueNonBlockingCallback(o => OnExecute()); CancelTimeout(); return HRESULT.S_OK; } /// HRESULT IObjectWithSelection.GetSelection(in Guid riid, out object ppv) { if (SelectedItems is null) { ppv = null; return HRESULT.E_NOINTERFACE; } ppv = ShellUtil.QueryInterface(SelectedItems, riid); return HRESULT.S_OK; } /// HRESULT IExecuteCommand.SetDirectory(string pszDirectory) { NewWorkingDirectory = pszDirectory; return HRESULT.S_OK; } /// HRESULT IExecuteCommand.SetKeyState(MouseButtonState grfKeyState) { KeyState = grfKeyState; return HRESULT.S_OK; } /// HRESULT IExecuteCommand.SetNoShowUI(bool fNoShowUI) { UIDisplayBlocked = fNoShowUI; return HRESULT.S_OK; } /// HRESULT IExecuteCommand.SetParameters(string pszParameters) { Parameters = pszParameters; return HRESULT.S_OK; } /// HRESULT IExecuteCommand.SetPosition(POINT pt) { Position = pt; return HRESULT.S_OK; } /// HRESULT IObjectWithSelection.SetSelection(IShellItemArray psia) { SelectedItems = new ShellItemArray(psia); return HRESULT.S_OK; } /// HRESULT IExecuteCommand.SetShowWindow(ShowWindowCommand nShow) { WindowState = nShow; return HRESULT.S_OK; } } }