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;
}
}
}