Skip to content

Support context menu modifications for third-party file managers (XYplorer, Total Commander, Double Commander and possibly more) - #797

Open
geezmolycos wants to merge 7 commits into
moudey:mainfrom
geezmolycos:shellext-selection-retriever
Open

Support context menu modifications for third-party file managers (XYplorer, Total Commander, Double Commander and possibly more)#797
geezmolycos wants to merge 7 commits into
moudey:mainfrom
geezmolycos:shellext-selection-retriever

Conversation

@geezmolycos

@geezmolycos geezmolycos commented Feb 15, 2026

Copy link
Copy Markdown

I implemented an context menu shell extension in Nilesoft Shell to retrieve the selected file list, so that it works in most file managers where Shell Extensions are allowed to run in context menu. In my test, in XYplorer, Total Commander, Double Commander, Everything context menu modifications are working correctly, in Directory Opus and OneCommander, they are not working because they don't use native context menu.

In the beginning I discovered that Nilesoft Shell doesn't work properly with third-party file managers. It only changes the menu appearence, item addition/modification are not working, like so:
Clipboard-20260216-02
On the Internet I found out lots of discussions on this problem:
https://www.xyplorer.com/xyfc/viewtopic.php?p=226677#p226677
https://www.xyplorer.com/xyfc/viewtopic.php?t=26686
https://web.archive.org/web/20250715220407/https://www.xyplorer.com/xyfc/viewtopic.php?t=27737
https://resource.dopus.com/t/how-can-i-make-the-directory-opus-use-my-windows-context-menu/53508/4
https://www.reddit.com/r/OneCommander/comments/1hf7q6l/request_support_for_nilesoft_shell_integration/

Because I feel like Nilesoft Shell fits well with modern Windows 11 UI, and replacement solutions are not as easy to use, I decided to somehow implement third-party file manager support for Nilesoft Shell. I reseached into the code of Nilesoft Shell and come up with my solution.

The details of this implementation is that:

  • I added a dummy context menu Shell Extension that retrieves selected files required by Selection::QuerySelected.
  • It gets called whenever a shell context menu is generated, and puts the file list in a global variable g_ShellContext.
  • In ContextMenu::Initialize it checks for the dummy context menu item it generated to judge whether a menu is indeed a context menu.
  • If it is a context menu and the original method to retrieve selection list is failed, QuerySelected reads selected file list stored in g_ShellContext.

Instead of Windows File Explorer hook that only works in Explorer, this method can retrieve the selected file list wherever Shell Extension runs (in third-party file managers). The original implementation requires a ShellBrowser to be available in Selection::QueryShellWindow, and used in Selection::QuerySelected, but third-party managers implement file browser by themselves, they don't have a ShellBrowser exposed. My implementation doesn't completely change how it works, it only tries to use the Shell Extension retrieved file selection list when the original method fails.

Below are some screenshots showing it work in different file managers. In shell.nss the excluded condition should be changed. like:

XYplorer
exclude.where = !process.is_explorer && (process.name != "XYplorer") && (process.name != "notepad") && (process.name != "dopus") && (process.name != "dopusrt") && (process.name != "doublecmd") && (process.name != "OneCommander")

Clipboard-20260216-01 Clipboard-20260216

Total Commander 64-bit
Screenshot_20260216-015308

Double Commander
Screenshot_20260216-015342_doublecmd

Directory Opus (not working)

Screenshot_20260216-014944_dopus

Everything (working)
Screenshot_20260216-025936_thorium

If it's not working, it can be added in "excluded" condition, to remove the "ShellExtSelectionRetriever Placeholder" item in context menu.

@THreeSH98

Copy link
Copy Markdown

You did a good job. I don’t understand anything about coding, but I would like Nilesoft Shell to work everywhere it’s possible. I use Everything and Directory Opus, and it’s a pity that this can’t be done in DOpus. If possible, could you please explain in simpler terms which lines of code need to be added to the .nss files to enable support in Everything? Thank you for your contribution!

@N1ck01

N1ck01 commented Mar 15, 2026

Copy link
Copy Markdown

I was going to create a new thread asking about this but it sounds like the third-party file manager that I use, xplorer², can also be included here as well, because only changes to theme.nss are working and no modify.nss/remove.nss changes are working.
The xplorer² file manager appears to just use the standard Windows context menu, even though it adds some additional dedicated items to the top of the menu that are only shown within xplorer² but not within Windows File Explorer, but those additional items can be removed within the advanced options of xplorer² but even then no modify.nss/remove.nss changes work.
So I'm really hoping that whatever these proposed changes are will also be able to support the xplorer² file manager or if it can also be checked for compatibility with this implementation.
Thank you very much for trying to support third-party file managers.

@RubicBG
RubicBG requested a review from moudey May 14, 2026 19:52
@webtechlabs

Copy link
Copy Markdown

Very nice contribution. Hopefully @moudey will add your PR in the next build. Happy to see Shell work with any other app that uses the OS built-in context menu. Thank you for your fix.

@RubicBG

RubicBG commented May 20, 2026

Copy link
Copy Markdown
Collaborator

NS source code here - in this GitHub Repo, is several builds behind. Some function are missing

app.delete
app.exists
app.get
app.remove
app.set
package.logo
path.dir.boxl
path.file.save_box
str.fmt
user.groups
sys.lang.id
sys.lang.name
sys.lang.country
sys.lang
appx.apps
appx.entryPoint
appx.executable
appx.logo
package.apps
package.entryPoint
package.executable
ump.apps
ump.entryPoint
ump.executable
ump.logo
... and more

I restored most of them, fixed a few others, and also added 3rd party app support (not the method described here, but without this idea I wouldn't have succeeded - many thanks for sharing the solution). I want to fix a few more things before sharing the finished dll file - when I'm ready I'll share it here too.

@RubicBG

RubicBG commented May 27, 2026

Copy link
Copy Markdown
Collaborator

https://github.com/RubicBG/ReShell-Releases/blob/main/shell.dll

A few things have changed from the original:

  • sel.type returns different numbers (new numbering: file=1, dir=2, namespace=3...)
  • sys.is_primary_monitor has been renamed to sys.screen.primary

All changes, fixes and new functions will be documented soon at:
https://rubicbg.github.io/ReShell-Releases/

TCNOco added a commit to TCNOco/Shell that referenced this pull request Aug 16, 2026
The DLL has always been registered as a context-menu handler under *,
Directory, Drive, Folder, Directory\Background, DesktopBackground,
LibraryFolder and LibraryFolder\Background, but DllGetClassObject
returned E_NOTIMPL on every path: the registration existed only to make
hosts load us, and the NtUserTrackPopupMenuEx hook did all the work.

That costs every host that is not Explorer. Selections::QuerySelected
reaches the selection through IShellBrowser, which it gets by sending
WM_GETISHELLBROWSER to the window. A third-party file manager implements
its own view and answers nothing, so QuerySelected returns false at its
has_IShellBrowser gate and the menu is built with no selection - theming
works, anything selection-aware does not. XYplorer, Total Commander,
Double Commander, Everything and the rest were already calling
IShellExtInit::Initialize and handing us the selection outright, and we
were declining it.

This is capability detection, not application detection: any host
honouring ContextMenuHandlers is served, with no per-application list in
the DLL or in anyone's config. Upstream PR moudey#797 approached the same goal
by inserting a marker item and finding it again by title; identity here
is the HMENU the host passes to QueryContextMenu, which is the same
handle it later passes to TrackPopupMenu, so the hook recognises its own
menu with a pointer compare. No placeholder item, nothing user-visible,
nothing to mistranslate.

The object inserts zero items, so Explorer's menu is unchanged by
construction and the hook keeps doing everything there.

DllCanUnloadNow had to be fixed first, and this is the part worth
reviewing. It answered from _loader.explorer alone and returned S_OK -
"nothing of mine is alive, unload me" - in every host that was not
explorer.exe. That was true only while we handed out no objects. With a
live IContextMenu outstanding it would let COM unload shell.dll out from
under the host still holding it, so it now consults an object count.
There was no reference counting anywhere in this DLL before: the only
IUnknown implementation, ProcessReference, is never constructed.

The count is what the new tests cover, since a mistake in it either
unloads the DLL under a caller or pins it forever in every process that
ever raised a menu, and neither shows up in a build. Verified to have
teeth: dropping the decrement from ~ShellExtHandler produces four
failures across two tests and fails the build.

This is the plumbing only. The captured selection is not yet routed into
Selections, so behaviour is unchanged everywhere for now - that is the
next commit, and it is the part that needs a real host to verify.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TCNOco added a commit to TCNOco/Shell that referenced this pull request Aug 16, 2026
The context-menu handler works, and the selection reaches Selections,
but nothing was ever drawn outside Explorer: the shipped config's
exclude.where was !process.is_explorer, and ContextMenu::Initialize
bails at is_excluded() before it builds anything. Everything, XYplorer
and every other host fell straight through to their own menu no matter
what the handler did.

This is the same condition that made upstream PR moudey#797 inert, and it was
written down in this fork's own review of that PR before the handler was
built. Building the plumbing and leaving the rule that says "not here"
is a miss.

The rule is now Explorer, or any host that invoked us as a registered
context-menu handler. window.is_contextmenuhandler is true exactly when
the host went through our IContextMenu, so it is the capability rather
than an application name - no per-application list, which was the point
of doing this ourselves instead of taking moudey#797. A process that loaded
the DLL some other way and never went through the handler is still
excluded, exactly as before.

Note for anyone updating: install-local.ps1 preserves an existing
shell.nss, so an install that already has the old rule keeps it. Use
-ResetConfig, or edit the installed file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants