Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 41 additions & 37 deletions src/CommandEnumerator.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,41 @@
using Juknum.Windows.ContextMenu.Interfaces;
using System.Runtime.InteropServices;
using Vanara.PInvoke;

namespace Juknum.Windows.ContextMenu;

[ComVisible(false)]
internal class CommandEnumerator(IExplorerCommand[] commands) : IEnumExplorerCommand {
private int index = 0;
private readonly IExplorerCommand[] commands = commands;

#region IEnumExplorerCommand Members
public HRESULT Next(uint celt, IExplorerCommand[] pUICommand, IntPtr pceltFetched) {
uint fetched = 0;

while (fetched < celt && index < commands.Length) {
pUICommand[fetched++] = commands[index++];
}

return fetched == celt ? HRESULT.S_OK : HRESULT.S_FALSE;
}

public HRESULT Skip(uint celt) {
index += (int)celt;

if (index > commands.Length) {
index = commands.Length;
return HRESULT.S_FALSE;
}

return HRESULT.S_OK;
}

public void Reset() => index = 0;
public IEnumExplorerCommand Clone() => new CommandEnumerator(commands) { index = this.index };
#endregion
}
using System.Runtime.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.Shell32;

namespace Juknum.Windows.ContextMenu;

[ComVisible(false)]
internal class CommandEnumerator(IExplorerCommand[] commands) : IEnumExplorerCommand {
private int index = 0;
private readonly IExplorerCommand[] commands = commands;

#region IEnumExplorerCommand Members
public HRESULT Next(uint celt, IExplorerCommand[] pUICommand, IntPtr pceltFetched) {
uint fetched = 0;

while (fetched < celt && index < commands.Length) {
pUICommand[fetched++] = commands[index++];
}

if (pceltFetched != IntPtr.Zero) {
Marshal.WriteInt32(pceltFetched, (int)fetched);
}

return fetched == celt ? HRESULT.S_OK : HRESULT.S_FALSE;
}

public HRESULT Skip(uint celt) {
index += (int)celt;

if (index > commands.Length) {
index = commands.Length;
return HRESULT.S_FALSE;
}

return HRESULT.S_OK;
}

public void Reset() => index = 0;
public IEnumExplorerCommand Clone() => new CommandEnumerator(commands) { index = this.index };
#endregion
}
186 changes: 93 additions & 93 deletions src/ExplorerCommand.cs
Original file line number Diff line number Diff line change
@@ -1,94 +1,94 @@
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Vanara.PInvoke;
using static Vanara.PInvoke.Shell32;
namespace Juknum.Windows.ContextMenu;
[ComVisible(false)]
public abstract class ExplorerCommand : Interfaces.IExplorerCommand {
/// <summary>
/// Unique identifier (GUID) of the command.
/// </summary>
public abstract Guid Guid { get; }
/// <summary>
/// Title of the command, which is displayed in the context menu.
/// </summary>
public abstract string Title { get; }
/// <summary>
/// Optional icon for the command, which is displayed in the context menu.
/// </summary>
public virtual string? Icon { get; } = null;
/// <summary>
/// Optional tooltip for the command, which is displayed when hovering over the command in the context menu.
/// Note: This property is not used by Windows Explorer.
/// </summary>
public virtual string? ToolTip { get; } = null;
/// <summary>
/// Determines whether the command is enabled for the given shell item array.
/// </summary>
/// <param name="psiItemArray">The shell item array for which to determine command enablement.</param>
/// <param name="canBeSlow">Indicates whether the operation can be slow.</param>
/// <returns><c>true</c> if the command is enabled; otherwise, <c>false</c>.</returns>
public virtual bool IsEnabled(IShellItemArray psiItemArray, bool canBeSlow) => true;
/// <summary>
/// The method that is called when the command is executed.
/// This method should be overridden to provide custom behavior for the command.
/// </summary>
/// <param name="psiItemArray">The shell item array for which to execute the command.</param>
/// <param name="pbc">The bind context for the command.</param>
/// <returns><c>true</c> if the command was executed successfully; otherwise, <c>false</c>.</returns>
public abstract bool Execute(IShellItemArray psiItemArray, IBindCtx? pbc);
#region IExplorerCommand Members
public HRESULT GetTitle(IShellItemArray psiItemArray, out string? ppszName) {
ppszName = Title;
return HRESULT.S_OK;
}
public HRESULT GetIcon(IShellItemArray psiItemArray, out string? ppszIcon) {
ppszIcon = Icon;
return Icon is null ? HRESULT.E_NOTIMPL : HRESULT.S_OK;
}
public HRESULT GetToolTip(IShellItemArray psiItemArray, out string? ppszInfotip) {
ppszInfotip = ToolTip;
return ToolTip is null ? HRESULT.E_NOTIMPL : HRESULT.S_OK;
}
public HRESULT GetCanonicalName(out Guid pguidCommandName) {
pguidCommandName = Guid;
return HRESULT.S_OK;
}
public HRESULT GetState(IShellItemArray psiItemArray, bool fOkToBeSlow, out EXPCMDSTATE pCmdState) {
pCmdState = IsEnabled(psiItemArray, fOkToBeSlow) ? EXPCMDSTATE.ECS_ENABLED : EXPCMDSTATE.ECS_DISABLED;
return HRESULT.S_OK;
}
public virtual HRESULT Invoke(IShellItemArray psiItemArray, IBindCtx? pbc) {
try {
return Execute(psiItemArray, pbc) ? HRESULT.S_OK : HRESULT.S_FALSE;
}
catch {
return HRESULT.E_FAIL;
}
}
public virtual HRESULT GetFlags(out EXPCMDFLAGS pFlags) {
pFlags = EXPCMDFLAGS.ECF_DEFAULT;
return HRESULT.S_OK;
}
public virtual HRESULT EnumSubCommands(out Interfaces.IEnumExplorerCommand? ppEnum) {
ppEnum = null;
return HRESULT.E_NOTIMPL;
}
#endregion
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Vanara.PInvoke;
using static Vanara.PInvoke.Shell32;

namespace Juknum.Windows.ContextMenu;

[ComVisible(false)]
public abstract class ExplorerCommand : IExplorerCommand {

/// <summary>
/// Unique identifier (GUID) of the command.
/// </summary>
public abstract Guid Guid { get; }

/// <summary>
/// Title of the command, which is displayed in the context menu.
/// </summary>
public abstract string Title { get; }

/// <summary>
/// Optional icon for the command, which is displayed in the context menu.
/// </summary>
public virtual string? Icon { get; } = null;

/// <summary>
/// Optional tooltip for the command, which is displayed when hovering over the command in the context menu.
/// Note: This property is not used by Windows Explorer.
/// </summary>
public virtual string? ToolTip { get; } = null;

/// <summary>
/// Determines whether the command is enabled for the given shell item array.
/// </summary>
/// <param name="psiItemArray">The shell item array for which to determine command enablement.</param>
/// <param name="canBeSlow">Indicates whether the operation can be slow.</param>
/// <returns><c>true</c> if the command is enabled; otherwise, <c>false</c>.</returns>
public virtual bool IsEnabled(IShellItemArray psiItemArray, bool canBeSlow) => true;

/// <summary>
/// The method that is called when the command is executed.
/// This method should be overridden to provide custom behavior for the command.
/// </summary>
/// <param name="psiItemArray">The shell item array for which to execute the command.</param>
/// <param name="pbc">The bind context for the command.</param>
/// <returns><c>true</c> if the command was executed successfully; otherwise, <c>false</c>.</returns>
public abstract bool Execute(IShellItemArray psiItemArray, IBindCtx? pbc);

#region IExplorerCommand Members
public HRESULT GetTitle(IShellItemArray psiItemArray, out string? ppszName) {
ppszName = Title;
return HRESULT.S_OK;
}

public HRESULT GetIcon(IShellItemArray psiItemArray, out string? ppszIcon) {
ppszIcon = Icon;
return Icon is null ? HRESULT.E_NOTIMPL : HRESULT.S_OK;
}

public HRESULT GetToolTip(IShellItemArray psiItemArray, out string? ppszInfotip) {
ppszInfotip = ToolTip;
return ToolTip is null ? HRESULT.E_NOTIMPL : HRESULT.S_OK;
}

public HRESULT GetCanonicalName(out Guid pguidCommandName) {
pguidCommandName = Guid;
return HRESULT.S_OK;
}

public HRESULT GetState(IShellItemArray psiItemArray, bool fOkToBeSlow, out EXPCMDSTATE pCmdState) {
pCmdState = IsEnabled(psiItemArray, fOkToBeSlow) ? EXPCMDSTATE.ECS_ENABLED : EXPCMDSTATE.ECS_DISABLED;
return HRESULT.S_OK;
}

public virtual HRESULT Invoke(IShellItemArray psiItemArray, IBindCtx? pbc) {
try {
return Execute(psiItemArray, pbc) ? HRESULT.S_OK : HRESULT.S_FALSE;
}
catch {
return HRESULT.E_FAIL;
}
}

public virtual HRESULT GetFlags(out EXPCMDFLAGS pFlags) {
pFlags = EXPCMDFLAGS.ECF_DEFAULT;
return HRESULT.S_OK;
}

public virtual HRESULT EnumSubCommands(out IEnumExplorerCommand? ppEnum) {
ppEnum = null;
return HRESULT.E_NOTIMPL;
}
#endregion
}
96 changes: 48 additions & 48 deletions src/ExplorerCommandMenu.cs
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Vanara.PInvoke;
using static Vanara.PInvoke.Shell32;
namespace Juknum.Windows.ContextMenu;
[ComVisible(false)]
public abstract class ExplorerCommandMenu : ExplorerCommand {
/// <summary>
/// Array of <see cref="ExplorerCommand"/> objects that represent the commands in the menu.
/// </summary>
public abstract ExplorerCommand[] Commands { get; }
#region ExplorerCommand Members
/// <summary>
/// Does not execute any command when the menu is clicked.
/// Override this method to provide custom behavior for the menu.
/// </summary>
/// <param name="psiItemArray">The shell item array for which to execute the command.</param>
/// <param name="pbc">The bind context for the command.</param>
/// <returns><c>true</c> if the command was executed successfully; otherwise, <c>false</c>.</returns>
public override bool Execute(IShellItemArray psiItemArray, IBindCtx? pbc) => true;
#endregion
#region IExplorerCommand Members
public override HRESULT Invoke(IShellItemArray psiItemArray, IBindCtx? pbc) {
// Do nothing when the menu is clicked. The menu will be displayed automatically by Windows Explorer.
return HRESULT.E_NOTIMPL;
}
public override HRESULT GetFlags(out EXPCMDFLAGS pFlags) {
pFlags = EXPCMDFLAGS.ECF_HASSUBCOMMANDS;
return HRESULT.S_OK;
}
public override HRESULT EnumSubCommands(out Interfaces.IEnumExplorerCommand? ppEnum) {
try {
ppEnum = new CommandEnumerator(Commands);
return HRESULT.S_OK;
}
catch {
ppEnum = null;
return HRESULT.S_FALSE;
}
}
#endregion
}
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Vanara.PInvoke;
using static Vanara.PInvoke.Shell32;

namespace Juknum.Windows.ContextMenu;

[ComVisible(false)]
public abstract class ExplorerCommandMenu : ExplorerCommand {
/// <summary>
/// Array of <see cref="ExplorerCommand"/> objects that represent the commands in the menu.
/// </summary>
public abstract ExplorerCommand[] Commands { get; }

#region ExplorerCommand Members
/// <summary>
/// Does not execute any command when the menu is clicked.
/// Override this method to provide custom behavior for the menu.
/// </summary>
/// <param name="psiItemArray">The shell item array for which to execute the command.</param>
/// <param name="pbc">The bind context for the command.</param>
/// <returns><c>true</c> if the command was executed successfully; otherwise, <c>false</c>.</returns>
public override bool Execute(IShellItemArray psiItemArray, IBindCtx? pbc) => true;
#endregion

#region IExplorerCommand Members
public override HRESULT Invoke(IShellItemArray psiItemArray, IBindCtx? pbc) {
// Do nothing when the menu is clicked. The menu will be displayed automatically by Windows Explorer.
return HRESULT.E_NOTIMPL;
}

public override HRESULT GetFlags(out EXPCMDFLAGS pFlags) {
pFlags = EXPCMDFLAGS.ECF_HASSUBCOMMANDS;
return HRESULT.S_OK;
}

public override HRESULT EnumSubCommands(out IEnumExplorerCommand? ppEnum) {
try {
ppEnum = new CommandEnumerator(Commands);
return HRESULT.S_OK;
}
catch {
ppEnum = null;
return HRESULT.S_FALSE;
}
}
#endregion
}
29 changes: 0 additions & 29 deletions src/Interfaces/IEnumExplorerCommand.cs

This file was deleted.

Loading