Windows Shell Integration Today — Context Menus, File Associations, and What Changed in Windows 11

· Updated: · · Windows, Shell Extensions, Context Menu, File Association, COM, Windows 11, File Explorer, MSIX, Windows Development

Revision history (first version, published Aug 20, 2026)
First published
Cite this article(DOI (registered archive): 10.5281/zenodo.22170871)

The DOIs below refer to previously archived versions and may not match the current text. Use this page’s URL to reference the current text.

Go Komura (2026). Windows Shell Integration Today — Context Menus, File Associations, and What Changed in Windows 11. KomuraSoft LLC. https://comcomponent.com/en/blog/windows-shell-integration-context-menu-file-association/

DOI (registered archive)
10.5281/zenodo.22170871
DOI (last registered version)
10.5281/zenodo.22170872

“We moved to Windows 11 and can no longer find our app’s context-menu item.” On inspection, the item has not disappeared: open “Show more options” at the end of the menu and it appears. What is happening in this consultation is not a defect but a Windows 11 design change to the menu. In the field it leads to inquiries such as “it takes one more click” and “I cannot find the item”.

However, “opening a file with this app” and “putting a custom command on the new context menu” are two different tasks. If you start writing a shell extension DLL without separating them, you make a requirement that only needs an association registration more complicated than it has to be.

This article is an introduction for IT staff at small and midsize companies and for Windows developers who look after business apps. It connects the foundation of file associations, the difference between the new and old menus, the choice of implementation method, registration and removal in the installer, and troubleshooting.

1. The Bottom Line First

The foundation of associations and verbs is unchanged; on Windows 11, how the menu is presented split into new and old. First decide what you want to achieve, then choose only the mechanisms that goal requires.

If All You Need Is “Open”, Start from an Association and a Static Verb

The foundation of file association is the three-layer registry structure “extension key → ProgID → verb”. The extension key points at a ProgID, and shell\<verb>\command under the ProgID holds the command line to launch. If all you want is “open with this app”, this registration and a static verb are still enough, and no shell extension DLL is needed. Microsoft, too, advises starting from the simplest static verb that meets the requirements.12

Register under HKLM for all users or under HKCU for a single user. HKCR is a merged view that overlays the Classes of both, so name HKLM or HKCU explicitly as the write destination and treat HKCR as a place to check.3

Note that registering as a candidate and being chosen as the default app are separate things. The user chooses the default app that opens on double-click, and the OS protects that choice. The app’s job ends at registering itself correctly as a candidate, not at having the installer seize the default.4

Putting a Custom Command on the New Menu Requires IExplorerCommand and Package Identity

On Windows 11, classic IContextMenu extensions are relegated to the old menu that opens with “Show more options” (Shift+F10). Putting a custom command on the new menu requires an IExplorerCommand implementation and package identity.56

The official route is to register a native DLL that implements IExplorerCommand in an MSIX manifest (desktop4:FileExplorerContextMenus). If an existing app cannot be fully converted to MSIX, a sparse package (MSIX with external location) can grant identity alone.67

Think Through DLL Safety and Cleanup at Uninstall

A classic shell extension is a COM DLL loaded into the process of Explorer and other hosts. A crash or delay in the extension spreads to the whole host. A 64-bit host needs a 64-bit DLL, and implementing an in-process extension in managed code is unsupported.89

After registering, changing, or deleting an association, notify with SHChangeNotify(SHCNE_ASSOCCHANGED). At uninstall, the official guidance is to delete your own ProgID and related keys but leave the extension key’s default value. Shell integration is not just showing a menu; it includes applying the change and cleaning up afterward.110

Where to Read, by Goal

What you want to know, or the symptom you see Where to read
Which implementation method to choose The decision table in Chapter 6. It separates association-only from adding a custom command to the new menu
Support double-click and “Open with” Associations in Chapter 2 and static verbs in Chapter 3
Registered, but the app does not become the default UserChoice in Section 2.4. It separates candidate registration from the user’s choice
The menu moved one level deeper on Windows 11 The new and old menus in Chapter 5. The fix is in Section 5.2, and Section 5.3 if you cannot convert to MSIX
What the installer should register and remove Chapter 7. Especially per-user registration in Section 7.3 and cleanup in Section 7.4
The menu is missing, appears twice, or Explorer is slow Isolation in Chapter 8. DLL constraints are in Chapter 4

To learn the mechanism, read from Chapter 2 in order; to decide a policy for an existing app, you can start at Chapter 6.

In the diagram a solid line marks a relation that always holds and a dashed line marks a conditional one (the conditions are given per relation on the detail page). The full list of relations (16 in total, with evidence and certainty) and the definitions of the main concepts are collected on the knowledge map detail page (in Japanese). Data: JSON-LD / Turtle

2. How File Associations Work — The Three-Layer Structure Extension Key → ProgID → Verb

This chapter goes in the order file-side registration → write destination → app-side registration → the user’s choice. The foundation has not changed in more than twenty years: an extension points at a ProgID, a verb holds a command line, and more involved extensions run as in-process COM DLLs.

2.1. Reading the Three-Layer Structure from One Example

First, the basic shape of an association in one example. The extension key points at a ProgID, and a verb inside the ProgID holds the command to launch.1 The precedence that applies when the user has chosen a default app is explained in Section 2.4.

HKEY_CLASSES_ROOT
   .kmrpt                                  ← (1) Extension key
      (Default) = KomuraSoft.Report.1      ←     A pointer that only names the ProgID
      OpenWithProgids
         KomuraSoft.Report.1               ←     Candidate under "Open with"
   KomuraSoft.Report.1                     ← (2) ProgID (the substance of the association)
      (Default) = Komura Report Document
      DefaultIcon
         (Default) = "C:\Program Files\KomuraSoft\Report.exe",0
      shell                                ← (3) List of verbs
         open
            command
               (Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
  • (1) The extension key (.kmrpt) only names a ProgID in its default value. Writing a command directly here is a mistake.
  • (2) The ProgID (KomuraSoft.Report.1) is the substance of the association; it holds the display name, the icon, and the list of verbs.
  • (3) A verb is an action such as “open” or “print”, and the default value of shell\open\command is the command line that is actually launched.

This separation is what lets you point several extensions (.kmrpt and .kmrpt-file, for example) at the same ProgID, or swap in a new ProgID when the app is upgraded.

The three-layer structure of file associationThe extension key is a pointer whose default value only names a ProgID, the ProgID is the substance that holds the display name, icon, and list of verbs, and the default value of command under a verb is the command line that is actually launcheddefault value names the ProgIDExtension key .kmrptProgID KomuraSoft.Report.1verb (open and others under shell)Default value of commandReport.exe is launchedAlso holds the display name and DefaultIcon

Figure 1: The extension key is a pointer, the ProgID is the substance, and the verb’s command is the command line that is actually launched.

2.2. HKCR Is a “Merged View” — Where You Write Changes the Meaning

The example above is shown under HKEY_CLASSES_ROOT (HKCR), but HKCR is not a physical storage location; it is a merged view that overlays HKLM\Software\Classes and HKCU\Software\Classes. If the same key exists in both, the HKCU side wins.3

HKCR is a merged viewHKCR overlays the Classes of HKLM and HKCU, the HKCU side takes precedence when the same key exists in both, registrations are written to HKLM or HKCU explicitly, and HKCR is treated as read-onlyHKLM\Software\Classes (all users)HKCR (merged view)HKCU\Software\Classes (per user)If the same key exists, HKCU winsTreat it as read-only (for checking)

Figure 2: HKCR is how the Classes of HKLM and HKCU look when overlaid; always name one or the other as the write destination.

Write destination Meaning Rights required
HKLM\Software\Classes Registration shared by all users Administrator
HKCU\Software\Classes Registration for that user only None
Writing directly to HKCR Routed depending on where the existing key lives Depends

In practice, the safe rule is to always name either HKLM or HKCU explicitly when writing a registration, and treat HKCR as read-only (for checking).

Do Not Confuse Association Data with the Bitness of COM Registration

Under WOW64 registry redirection, association data and COM registration are treated differently. Association data directly under HKLM\Software\Classes, such as extension keys and ProgIDs, has been shared between the 32-bit and 64-bit registry views since Windows 7, so it does not slip into Wow6432Node even when a 32-bit installer writes it.

On the other hand, some COM-registration subkeys such as Classes\CLSID are redirected, and the 32-bit versus 64-bit write split matters when you register the shell extensions (in-process COM) described later. Details are in “Registry 32-bit/64-bit Redirection and Virtualization Pitfalls”.

2.3. Registration on the App Side — App Paths, Applications, RegisteredApplications

Paired with the file side (extension and ProgID), there are also three kinds of registration on the app side.11

  • App Paths (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths): a registration that lets ShellExecuteEx launch the app by executable name alone. Microsoft recommends it because it avoids polluting the PATH environment variable.
  • Applications (HKCR\Applications\<app.exe>): defines the default way the app opens an arbitrary file handed to it through “Open with”, and the app’s display name (FriendlyAppName).
  • RegisteredApplications + Capabilities: declares the extensions and MIME types the app can handle, so the app appears as a candidate on the Windows default apps settings page.

Most consultations of the form “our app does not appear in the default apps list” are cases where the ProgID was registered but this Capabilities registration was left out.

The three kinds of app-side registrationApp-side registration comes in three kinds, App Paths, Applications, and RegisteredApplications, which respectively provide launching by executable name alone, the default way of opening through Open with, and a listing on the default apps settings pageApp-side registrationApp PathsApplicationsRegisteredApplicationsLaunch by file name aloneDefault for Open withListed on the default apps pageRequires a Capabilities declaration

Figure 3: There are three kinds of app-side registration, and appearing as a candidate on the default apps page requires a Capabilities registration.

2.4. The Default App Belongs to the User — UserChoice Protection

Writing a ProgID into the extension key’s default value does not by itself make your app the default. The result of the user’s explicit choice through “Open with” and similar is kept in HKCU\...\Explorer\FileExts\<extension>\UserChoice, and association resolution gives it precedence.

Do Not Design Around Rewriting UserChoice Directly

Windows does not support programmatic changes to the default app. Default app settings are designed to be made by the user through the system Settings UI; the UserChoice data is obfuscated, and a filter driver (UCPD.sys) blocks writes from apps. In managed environments, Group Policy and MDM policy are the official means.4

That tools such as SetUserFTA, which “mimic the hash and rewrite the value”, have been used in the past is the flip side of this protection.

The Installer Prepares the App to Be Chosen

What you build into your app’s installer is these three things.

  1. Register the ProgID and verbs correctly.
  2. Add the app to OpenWithProgIds.
  3. If needed, steer the user to the default apps settings page.

Rather than seizing the default, you set things up so that the user can choose.

Default app resolution and UserChoice protectionThe result of the user's explicit choice is kept in UserChoice and takes precedence in association resolution, and UCPD.sys blocks rewrites from apps, so what an installer can do ends at registering as a candidate and steering to the settings pagetakes precedenceblocked by UCPD.sysUserChoice (the user's choice)Association resolutionExtension key default valueRewrite from an appThe installer's jobRegister the ProgID and verbsAdd to OpenWithProgIdsSteer to the settings page

Figure 4: Association resolution gives precedence to the user’s choice (UserChoice), and the OS protects it against rewrites from apps.

3. Verbs Other Than “Open” — print, edit, runas, and Custom Verbs

3.1. Standard Verbs and Custom Verbs

Verbs are not limited to open. The standard verbs whose meaning the OS knows include edit, print, play, and preview in addition to open, and standard verbs automatically get a display name that matches the OS locale. The default verb used on double-click is decided in this order: the default value of the shell key → the first verb in the registry → open → openwith.12

How the default verb is decidedThe default verb used on double-click is the first one found in the order of the shell key's default value, the first verb in the registry, open, and openwithif absentif absentif absentDefault value of the shell keyFirst verb in the registryopenopenwith

Figure 5: The default verb on double-click is the first one found in this order.

To add an action of your own, register a custom verb.

KomuraSoft.Report.1
   shell
      open
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
      print
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" /print "%1"
      verify                          ← custom verb
         (Default) = Verify Report (&V)   ← display name on the menu
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" /verify "%1"

3.2. Elevation, Shift-Only Display, and Legacy DDE

Verbs also support the following.

  • Registering a verb named runas defines an elevated launch equivalent to “Run as administrator”, and it is also used when a ShellExecute-family API is called with runas as the verb.
  • Placing an empty value named Extended on the verb key makes it an extended verb that is shown only when you hold Shift while right-clicking. It is handy for hiding a rarely used, risky operation.12
  • Associations of older apps sometimes still contain a configuration that sends a document into an already running process through DDE (the ddeexec key), but launching a verb through DDE is already deprecated legacy. There is no reason to write it anew.12

3.3. Quote the EXE Path and the Selected File’s Path

A frequent source of trouble is quoting on the command line. If any element of the command string can contain a space, it must be wrapped in quotes. That applies to an EXE path such as C:\Program Files\..., of course, and %1 (the path of the selected file) should always be written as "%1", because you cannot guarantee that a user’s file path contains no spaces. An unquoted My Program.exe is interpreted as “launch My with the argument Program.exe”.13

Quoting mistakes on the command lineAn unquoted command is split at the spaces and misread as launching My with the argument Program.exe, so an EXE path that can contain spaces and %1, which stands for the selected file's path, are always wrapped in quotessplit at spacescommand without quotesMisread as launching a different EXEcommand with quotesLaunches as intendedWrap the EXE path in quotesAlways wrap %1 in quotes too

Figure 6: An unquoted command is wrongly split at spaces, so always wrap the EXE path and %1 in quotes.

3.4. Before Writing a DLL, Check Whether a Static Verb Is Enough

Everything so far is a registry-only mechanism (a static verb): it works without writing a single DLL, and it carries no risk of destabilizing Explorer. Microsoft itself says repeatedly that before writing a shell extension, you should consider whether the simplest static verb that meets the requirements will do.2

4. Classic Shell Extensions — DLLs That Run Inside Explorer

The point to take from this chapter is that a classic extension DLL runs inside the process of Explorer and other hosts. Once you understand where it runs, the constraints on stability, bitness, and implementation language all connect.

4.1. Kinds of Shell Extension

For requirements a static verb cannot meet, such as “change the menu dynamically based on the selection” or “replace the icon or the property sheet”, you use a shell extension handler. The representative kinds are as follows.8

Handler Main interfaces What it can do
Context menu handler IContextMenu + IShellExtInit Add and control menu items dynamically
Icon handler / icon overlay IExtractIcon / IShellIconOverlayIdentifier Per-file icons and overlays
Property sheet handler IShellPropSheetExt Add a tab to the property sheet
Thumbnail / infotip IThumbnailProvider / IQueryInfo Thumbnails and hover descriptions
Drag-and-drop / copy hook handler IDropTarget / ICopyHook Intervene on drop or on copy and move

All of these are implemented as COM classes, with their CLSID registered in the registry. For COM itself, see “What Are COM, ActiveX, and OCX?”.

4.2. What Being an In-Process COM Server Means

The essence of a classic shell extension is that it is an in-process COM server (DLL) loaded into the process of Explorer (or of any app that opened a common file dialog). Every caveat follows from this.8

  • If the extension crashes, Explorer crashes with it. If it hangs, right-clicking freezes for several seconds. And the damage is not limited to Explorer; it reaches every app that displayed a file-open dialog.
  • Menu construction happens on the UI thread, so slow work such as network access or file I/O must not be done while the menu is being displayed.
  • As a rule, register the threading model as Apartment.
How an in-process extension takes its host downA shell extension DLL is loaded not only into Explorer but also into the process of any app that opens a file dialog, so a crash or hang in the extension spreads to the whole host processloaded in-processloaded in-processShell extension DLLExplorerAny app that opens a dialogCrash or hang spreadsNo slow work at display time

Figure 7: The extension DLL runs inside the host process, so a crash or hang spreads to the whole host.

When we investigate consultations such as “Explorer freezes when I open a particular folder” or “right-clicking takes five seconds”, it is not unusual for the cause to be a third-party shell extension rather than the customer’s own app. Chapter 8 covers how to isolate it.

4.3. Matching Bitness — A 64-bit Environment Requires a 64-bit DLL

An in-process DLL must match the bitness of the process that loads it. Explorer on 64-bit Windows is a 64-bit process, so a shell extension DLL built only for 32-bit is never loaded at all and never appears on the menu. Because there is no error either, this is a classic cause of “I registered it but it does not appear”.

The App Itself Does Not Have to Become 64-bit

Combining a 32-bit app with a 64-bit shell extension DLL is a legitimate configuration, but be aware that COM registration is split by bitness (Wow6432Node). Note also that what a verb’s command launches is an EXE in a separate process, so it is not subject to this constraint (a 32-bit EXE is fine as it is).

Bitness matching for a shell extension DLLThe only shell extension DLL a 64-bit Explorer can load is a 64-bit one, a 32-bit-only DLL produces no error and never appears on the menu, and an EXE launched from a verb's command is a separate process and is not subject to the constraintcan loadcannot loadseparate process64-bit Explorer64-bit shell extension DLL32-bit-only DLLMissing from the menu with no errorEXE launched from a verbFine as 32-bit

Figure 8: The only DLL loaded into 64-bit Explorer is a 64-bit DLL; an EXE launched from a verb is not subject to this constraint.

4.4. Why You Must Not Write It in Managed Code

We often get the question “can’t we write the shell extension in C#?”, but Microsoft explicitly does not recommend writing in-process shell extensions in managed code (.NET) and declares them unsupported.9

The reason lies in the nature of an extension being loaded into arbitrary processes. There are three main factors that destabilize the host app.

  • CLR version conflicts. These are a problem especially below .NET Framework 4.
  • Reentrancy. The CLR can reenter the message loop while waiting on a lock.
  • Object lifetime. The non-deterministic lifetime imposed by garbage collection conflicts with COM’s reference-counting contract.

Some of these have been mitigated in .NET Framework 4 and later and in modern .NET, but the official position has not changed.

The practical guideline is simple. Write in-process extensions in native C++. If you want to use managed code, make it a normal EXE launched from a verb’s command, or an out-of-process extension that runs in a separate process (a preview handler, for example).9

Deciding whether managed code is acceptableAn in-process extension that runs inside the Explorer process is written in native C++ as a rule, and if you want managed code, make it a normal EXE launched from a verb's command or an out-of-process extension that runs in a separate processyesnoRuns in-process?Write it in native C++Managed code is fineCLR conflicts and reentrancy destabilize the hostNormal EXE launched from a verbSeparate-process extension such as preview

Figure 9: In-process extensions are native C++ as a rule; managed code is limited to configurations that run in a separate process.

5. The Windows 11 New Context Menu — A Menu Split in Two

This chapter goes in the order why items moved to the old menu → registering on the new menu → how to keep an existing installer. The difference between an association that only does “open with this app” and adding a custom command is covered in Section 5.4.

5.1. What Happened

Windows 11 redesigned the File Explorer context menu. Cut, copy, and the like became a row of icons at the top; “Open” and “Open with” are grouped together at the top; and commands added by apps are grouped below the shell’s standard commands. When one app adds several commands, they are collected into a flyout (submenu) labeled with the app’s name.5

And here is the crucial point. Classic IContextMenu-based shell extensions were not removed; they were relegated to the old menu, which opens with “Show more options” (Shift+F10) and loads the Windows 10 menu as it was.5 The “hidden menu” in the opening consultation is this split.

The context menu split in two on Windows 11A right-click first opens the new menu, which shows only commands registered with IExplorerCommand and package identity, while classic IContextMenu extensions are relegated to the old menu that opens with Show more optionsShow more options Shift+F10Right-click a fileNew menu (Windows 11)IExplorerCommand + identity commandsOld menu (the Windows 10 menu)Classic IContextMenu extensionsSeveral commands are collected into a flyout

Figure 10: The only commands on the new menu are IExplorerCommand + identity commands; classic extensions are relegated to the old menu.

5.2. The Official Route onto the New Menu — IExplorerCommand + Manifest Registration

The official route for putting a custom command on the new menu is a native DLL that implements IExplorerCommand, registered in an MSIX manifest.6

The Manifest Needs Two Declarations

In the example below, the first half declares the COM server (the CLSID and the implementing DLL) and the second half declares the context menu extension (the target and the command). The same CLSID ties the two together.

<!-- Package manifest (excerpt) -->
<com:Extension Category="windows.comServer">
  <com:ComServer>
    <com:SurrogateServer DisplayName="Komura commands">
      <com:Class Id="01234567-89AB-CDEF-0123-456789ABCDEF"
                 Path="KomuraCommand.dll" ThreadingModel="STA" />
    </com:SurrogateServer>
  </com:ComServer>
</com:Extension>
<desktop4:Extension Category="windows.fileExplorerContextMenus">
  <desktop4:FileExplorerContextMenus>
    <desktop5:ItemType Type=".kmrpt">
      <desktop5:Verb Id="VerifyReport"
                     Clsid="01234567-89AB-CDEF-0123-456789ABCDEF" />
    </desktop5:ItemType>
  </desktop4:FileExplorerContextMenus>
</desktop4:Extension>

The Type of ItemType can be a specific extension, or * (all files), Directory (folders), or Directory\Background (the folder background). The DLL must match Explorer’s architecture (64-bit or ARM64).6

IExplorerCommand itself is an interface that dates from the Windows 7 era; you implement the title (GetTitle), the icon (GetIcon), the enabled, disabled, or hidden state (GetState), and execution (Invoke). The methods are called from the UI thread, so access to network resources is prohibited, and the menu-building methods must return quickly. Do heavy work after Invoke.146

Manifest structure for a new-menu registrationThe COM server declaration in the MSIX manifest maps the CLSID to the DLL, and the context menu extension declaration ties the target and the implementation together through ItemType and Verb, so the custom command appears on the new menumaps CLSID to DLLspecified by ItemType and VerbMSIX manifestCOM server declarationMenu extension declarationIExplorerCommand implementation DLLCommand shown on the new menuTarget is an extension, all files, etc.

Figure 11: The two declarations in the manifest tie the implementation DLL to its target, and the command appears on the new menu.

5.3. The Option for Non-Packaged Apps — Gaining Identity Alone with a Sparse Package

When “our app can only be distributed as an MSI, and MSIX is out of the question”, the way out is a sparse package (MSIX with external location). You build and sign a small MSIX that holds only a manifest and no app files, and register it at the end of the existing installer. The app thereby acquires package identity, and the manifest registration above (that is, appearing on the new menu) becomes possible.

It is available on Windows 10 version 2004 and later, and the package must be signed with a certificate trusted on the target machine.7 The order of registration and removal, and the caveat that the registration is per user, are covered in Section 7.3.

How a sparse package grants identityAfter the existing installer places the app files, registering a manifest-only sparse package with an external location gives the app package identity and makes manifest registration on the new menu possibleExisting installerPlace the app filesSparse packageManifest only, no app filesRegister with an external locationAcquire package identityNew-menu registration becomes possibleRequires a trusted signature

Figure 12: Register a sparse package that contains no app files, with an external location, and the app acquires package identity.

Its greatest advantage is that you do not have to replace the installer, which makes it the realistic answer for an app with existing MSI/EXE installer assets. For a comparison with a full move to MSIX, see also “Choosing a Windows App Distribution Method”.

5.4. How Association Verbs Appear on the New Menu

This is easy to misunderstand: the associations from Chapters 2 and 3 (ProgID and verbs) are still alive on the new menu. The default verb on double-click, “Open”, and the “Open with” candidates are resolved from the association and shown at the top of the new menu. In other words, if all you want is “make files openable with this app”, Windows 11 requires no additional work.

On the other hand, an association is not a general-purpose menu extension, so if you want an arbitrary custom command on the first level of the new menu, you need IExplorerCommand plus identity. That is the division of roles.6

Division of roles between associations and the new menuA ProgID-and-verb association is still used on the new menu to resolve the default verb on double-click, Open, and Open with, and is shown at the top, while putting an arbitrary custom command on the first level of the new menu requires IExplorerCommand and identityAssociation (ProgID and verbs)Resolve the default verb and OpenShown at the top of the new menuNo extra work on Windows 11Arbitrary custom commandIExplorerCommand + identityShown on the first level of the new menu

Figure 13: Associations still handle “Open”-type resolution on the new menu; only custom commands require IExplorerCommand plus identity.

6. A Practical Decision Table — Which of the Three Options to Take

First check whether (a) is enough; if you need a custom command on the new menu, choose (b). Keeping an existing classic extension for the time being is (c).

What you want to achieve Recommended means How it appears on Windows 11 Work and cost required
(a) Launch your app on double-click or “Open” Association + static verb (registry registration only) Integrated into “Open” and “Open with” on the new menu Registry registration by the installer only. No DLL, no additional signing requirement
(b) Put a custom command for the selected file or folder on the new menu IExplorerCommand implementation + MSIX manifest registration. A non-packaged app gains identity with a sparse package First level of the new menu (several commands are collected into a flyout labeled with the app name) Native C++ DLL + package identity + code signing
(c) Keep using an existing classic IContextMenu extension Keep it as is for now (do not choose it for new development) Only on the old menu under “Show more options” (Shift+F10) Maintain the 64-bit build and COM registration. Plan a future move to (b)

Decision 1: Choose the Simplest Method That Meets the Requirement

The first rule is not to bring in (b) or (c) for a requirement that (a) satisfies. The moment you write a shell extension, you take on responsibility for Explorer’s stability.

Decision 2: Separate Keeping the Old Menu from Improving Usability

(c) is merely “not broken”; as a user experience it stays permanently one step lower. The more often a command is used in daily work, the greater the return on investing in a move to (b).

How to choose among the three optionsIf all you want is launching on double-click or Open, an association and a static verb suffice, to put a custom command on the new menu use IExplorerCommand and MSIX manifest registration, if you cannot convert to MSIX grant identity with a sparse package, and keep an existing classic IContextMenu extension on the old menu for nowyesnoyesyesnonoOnly need Open?Association + static verbCustom command on the new menu?Can you convert to MSIX?IExplorerCommand+MSIXIdentity via sparse packageKeep the classic one for nowShown only on the old menuNo DLL, low risk

Figure 14: Depending on the requirement, choose among a static verb, IExplorerCommand plus identity, and keeping the classic extension.

7. Deployment and Registration in Practice — Installer, Sparse Package, Cleanup

Once the implementation method is decided, build the write destination, change notification, package registration and removal, and cleanup at uninstall into the installer.

7.1. HKLM or HKCU

Match the write destination to the form of the installer. For all users (placed under Program Files, administrator rights), use HKLM\Software\Classes; for a per-user install (no elevation), use HKCU\Software\Classes. Mixing them produces inquiries of the form “it opens for user A but not for user B”.

For a shell extension that involves CLSID registration, Reg-Free COM, which removes the need for registry registration altogether, is a valid option for COM used inside your own app, but it cannot be applied to a shell extension that Explorer loads, so the conventional registration is required (see “What Is Reg-Free COM?”).

7.2. Notify After a Change — SHChangeNotify

After registering, changing, or deleting an association, notify the SHCNE_ASSOCCHANGED event with SHChangeNotify. If you skip this, Explorer may not recognize the change until a restart.110

// Call once after changing associations, for example from an installer custom action
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);

7.3. Registering and Removing a Sparse Package

Registering and removing a sparse package is the installer’s job. Register after placing the files, and remove before deleting the files.7

# At install time: after placing the files, register the install folder as the external location
Add-AppxPackage -Path "C:\Program Files\KomuraSoft\KomuraReport.identity.msix" `
                -ExternalLocation "C:\Program Files\KomuraSoft"

# At uninstall time: remove the package registration before deleting the files
Remove-AppxPackage <package full name>

Separate Registration for the Running User from Deployment to All Users

Add-AppxPackage registers the package for the user who runs it. If it runs as LocalSystem from a custom action of a per-machine MSI, the user who performed the installation is not granted identity. So configure it to run under user impersonation.

However, even with impersonation, the registration applies only to the user who ran that installation. When one PC is shared by several users, the other users and any users created later have no package identity, and the command does not appear on their new menu.

If every user must be able to use it, provide per-user registration, for example by having the app check its own package registration at first launch and register if it is missing. Include removal from each registered user in the uninstall plan as well.

If the Menu Does Not Reflect the Registration

For a manifest registration to take effect, an Explorer restart (or a sign-out) may be required.6

Order of registering and removing a sparse packageAt install time register the sparse package after placing the files, at uninstall time remove the registration before deleting the files, and note that the registration applies only to the user who ran itInstallPlace the filesRegister the sparse packageUninstallRemove the package registrationDelete the filesRegistration applies only to the running user

Figure 15: Register after placing the files, remove before deleting them, and note that registration is per running user.

7.4. Cleanup at Uninstall — What to Delete and What to Leave

The official guide gives clear direction on cleanup at uninstall.1

  • Delete: your own ProgID keys in full, the Capabilities/RegisteredApplications registration, the shell extension’s CLSID registration, and the sparse package (Remove-AppxPackage).
  • Leave: the default value of the extension key (.kmrpt). The official recommendation is not to delete it even if it still points at your ProgID. It is hard to determine whether another app has taken the default since the installation, and Windows simply ignores a default-value ProgID that is not registered, so leaving it does no harm.
  • Call SHChangeNotify(SHCNE_ASSOCCHANGED) at the end of the cleanup as well.

Most “we uninstalled it, but leftovers still show on the menu” problems are gaps in this cleanup design.

Cleanup design at uninstallAt uninstall, delete your own ProgID keys, CLSID registration, and sparse package, leave the extension key's default value because an unregistered ProgID is ignored, and notify the change with SHChangeNotify at the end of the cleanupUninstallDeleteLeaveProgID and CLSID registrationSparse packageExtension key default valueAn unregistered ProgID is ignoredNotify with SHChangeNotify at the end

Figure 16: Delete your own registrations, leave the extension key’s default value, and notify the change at the end of the cleanup.

8. Troubleshooting — Missing, Duplicate, Slow

Investigate problems in three groups: “missing”, “appears twice or will not go away”, and “slow or crashing”. Finally, we explain how to verify registration and cleanup in a clean environment.

8.1. It Does Not Appear on the Menu

First, check which menu you are looking at, new or old. Then isolate in the following order.

  1. Which menu you are looking at: a classic-style registration appears only on the old menu under Shift+F10. Check both first.
  2. Bitness: a 32-bit-only shell extension DLL is not loaded into 64-bit Explorer (Section 4.3).
  3. Write destination: a mix-up between HKLM and HKCU, or Wow6432Node. Check the actual key with reg query.
  4. Package registration: for the new menu, check with Get-AppxPackage whether the package is registered, whether the signing certificate is trusted, and the -ExternalLocation path, then restart Explorer.6
  5. Missing notification: if SHChangeNotify was forgotten, you can tell by whether an Explorer restart makes the change take effect.
Isolation order when an item does not appear on the menuStart by checking which menu you are looking at, then isolate in order the DLL bitness, the registry write destination, the package registration and signature, and a missing SHChangeNotify notificationCheck which menu, new or oldCheck the DLL bitnessCheck the HKLM or HKCU write destinationCheck package registration and signatureDetect a missing notification by restarting

Figure 17: When an item is “missing”, isolate in this order: which menu you are looking at, bitness, write destination, package registration, missing notification.

8.2. It Appears Twice, or Will Not Go Away

First, narrow it down by which menu has the duplicate.

  • Duplicate only on the old menu: suspect a cleanup gap at uninstall (Section 7.4) or leftovers of an old version’s ProgID.
  • Appears on both new and old: suspect a classic registry registration coexisting with an MSIX manifest registration.

Both are typical starting points for isolating the cause; confirm by checking what is actually registered.

Isolating a duplicate entryA duplicate only on the old menu points to leftovers such as a cleanup gap or an old ProgID, while an entry on both new and old menus points to a classic registry registration coexisting with a manifest registrationold menu onlyboth new and oldWhich menu has the duplicate?LeftoversCoexistenceCleanup gap or an old ProgID left behindClassic registry registration coexisting with the new registration

Figure 18: A duplicate only on the old menu points to leftovers; an entry on both new and old points to coexistence.

8.3. Explorer Is Slow or Crashes

When right-clicking is slow or a particular folder crashes Explorer, start by taking stock of the installed shell extensions.

  1. List the extensions. Use a tool such as NirSoft’s ShellExView to check the non-Microsoft extensions.
  2. Disable temporarily to narrow down. Binary-search the suspicious ones and identify the DLL responsible. For a crash, the “Faulting module” in Event Viewer is another clue.
  3. If it is your own extension, examine the menu-building path. Suspect synchronous I/O or network access (Sections 4.2 and 5.2).
Identifying the responsible DLL when Explorer is slow or crashesList non-Microsoft shell extensions with ShellExView, temporarily disable suspicious ones while binary-searching to identify the responsible DLL, and for a crash also use the faulting module in Event Viewer as a clueTake stock of shell extensionsList the non-Microsoft onesDisable temporarily and binary-searchIdentify the responsible DLLIn case of a crashCheck the faulting module

Figure 19: Binary-search by temporarily disabling non-Microsoft extensions, and for a crash also use Event Viewer.

8.4. Windows Sandbox Is Handy for Verification

The basic verification of shell integration is to confirm “install in a clean environment → operate → uninstall → zero leftovers”. Windows Sandbox (Pro, Enterprise, or Education) is handy here: every launch brings up a pristine, disposable Windows in a few seconds, so you can run the installer’s registration and cleanup tests as many times as you like. Closing it discards everything, which also makes it suitable for investigating registry leftovers.15

9. Summary

When a move to Windows 11 brings the complaint that “the menu got hidden”, first check what you want to achieve against the decision table in Chapter 6. Think in these three stages.

1. Decide Whether an Association Is Enough

If all you need is “open with this app”, an association and a static verb are still enough. The foundation is extension key → ProgID → verb, and HKCR is a view for checking that overlays the Classes of HKLM and HKCU. Name the write destination explicitly, and always wrap %1 in quotes.

The user, however, is the one who chooses the default app. The installer registers the app correctly as a candidate rather than seizing the default.

2. Decide Which Menu Your Custom Command Goes On

Classic IContextMenu extensions run on the old menu that opens with “Show more options”. Putting a custom command on the new menu requires IExplorerCommand and registration in an MSIX manifest. For an app that cannot be fully converted to MSIX, gaining package identity with a sparse package is the realistic answer.

A classic extension DLL runs inside the process of Explorer and other hosts. A crash or delay spreads to the whole host, and a 64-bit host needs a 64-bit DLL. Writing an in-process extension in managed code is unsupported; native C++ is the rule.

3. Verify Registration, Change Notification, and Removal as One Set

After registering, changing, or deleting an association, notify with SHChangeNotify. At uninstall, delete your own ProgID and related keys, but leave the extension key’s default value. A sparse package is registered per user, so in a multi-user environment include registration and removal for each user in the plan.

Use Windows Sandbox and repeat install in a clean environment → operate → uninstall → check for leftovers.

Choose the simplest means first, and move on to adding a custom command to the new menu only when required. Following this order lets you size the work and determine the scope of registrations and implementation you have to maintain.

KomuraSoft LLC handles the design and implementation of file associations, context menus, and shell extensions for business apps; support for the Windows 11 new context menu (migrating to IExplorerCommand, introducing a sparse package); reviewing the registration and cleanup of existing installers; and investigating the cause when Explorer is slow or crashes. You are welcome to start from deciding what to do about “the menu that got hidden under Show more options”.

References

  1. Microsoft Learn, File Types. On the structure in which an extension key points at a ProgID, OpenWithProgIds, when to register under HKLM versus HKCU\Software\Classes, calling SHChangeNotify(SHCNE_ASSOCCHANGED) after an association change, and deleting the ProgID at uninstall while leaving the extension key’s default value. ↩ ↩2 ↩3 ↩4 ↩5

  2. Microsoft Learn, Choosing a Static or Dynamic Shortcut Menu Method. On choosing the simplest static-verb method that meets the requirements, IContextMenu being the most powerful but also the most complex and classified on the not-recommended side, and IExplorerCommand/IExplorerCommandState being the recommended method. ↩ ↩2

  3. Microsoft Learn, HKEY_CLASSES_ROOT Key. On HKEY_CLASSES_ROOT being a merged view of HKLM\Software\Classes and HKCU\Software\Classes, the user-side definition taking precedence over the machine-side one, and the routing rules for writes. ↩ ↩2

  4. Microsoft Learn, Windows app defaults platform. On default app changes being designed to happen only through the system Settings UI, user settings data being obfuscated and write-protected by a filter driver (UCPD.sys), registry-based changes being unsupported, and using Group Policy or MDM policy in managed environments. ↩ ↩2

  5. Windows Developer Blog, Extending the Context Menu and Share Dialog in Windows 11. On the design of the Windows 11 new context menu, extension through IExplorerCommand plus app identity, placing “Open” and “Open with” at the top, collecting several commands into a flyout labeled with the app name, and classic IContextMenu extensions being loaded as the Windows 10 menu under “Show more options” (Shift+F10). ↩ ↩2 ↩3

  6. Microsoft Learn, Add a File Explorer context menu command to a packaged desktop app. On registration on the Windows 11 new context menu being done with an IExplorerCommand implementation plus windows.comServer and desktop4:FileExplorerContextMenus manifest declarations, ItemType accepting *, Directory, and Directory\Background, matching the DLL architecture, keeping menu-building methods fast, supporting non-packaged apps with a sparse package, an Explorer restart sometimes being required for the registration to take effect, and file associations not being a general-purpose menu extension. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8

  7. Microsoft Learn, Grant package identity by packaging with external location. On gaining package identity by registering a package with external location (sparse package) without changing the existing installer, availability on Windows 10 version 2004 and later, and Windows features that require identity (context menu registration, notifications, and so on) becoming usable. ↩ ↩2 ↩3

  8. Microsoft Learn, Working with Shell Extensions. On the kinds of shell extension handler, an extension being an in-process COM DLL loaded into Explorer (and into processes that host the shell) so that a crash or hang spreads to all of Explorer, registration with ThreadingModel=Apartment, and considering simpler alternatives before a shell extension. ↩ ↩2 ↩3

  9. Microsoft Learn, Guidance for Implementing In-Process Extensions. On Microsoft not recommending and not supporting in-process shell extensions implemented in managed code, the reasons including CLR version conflicts, reentrancy, and non-deterministic object lifetime, and managed code being acceptable for out-of-process extensions (preview handlers, or launching from shell\verb\command). ↩ ↩2 ↩3

  10. Microsoft Learn, SHChangeNotify function. On how to raise the SHCNE_ASSOCCHANGED event that notifies the system of a file association change, and how to use it so that the shell recognizes the change. ↩ ↩2

  11. Microsoft Learn, Application Registration. On registering an executable through the App Paths subkey being the recommended approach, the role of the Applications subkey, verb registration through SystemFileAssociations, and the precedence of the ProgID and related information when the default app changes. ↩

  12. Microsoft Learn, Creating Shortcut Menu Handlers. On how to register static verbs, the order in which the default verb is decided (default value → first verb → Open → Open With), the display names of standard verbs being supplied by the OS, extended verbs through Extended, associations with DDE commands being deprecated, and WOW64 redirection caveats in 64-bit environments. ↩ ↩2 ↩3

  13. Microsoft Learn, Verbs and File Associations. On verbs being the actions also used by ShellExecuteEx, elements of a command string that can contain spaces needing to be wrapped in quotes and “%1” always being written quoted, and registering the default procedure under HKCR\Applications. ↩

  14. Microsoft Learn, IExplorerCommand interface. On the method set of GetTitle, GetIcon, GetState, Invoke, EnumSubCommands, and others, the methods being called on the UI thread and therefore not being allowed to communicate with network resources, and availability from Windows Vista onward. ↩

  15. Microsoft Learn, Windows Sandbox. On being able to start a disposable, isolated Windows environment in a few seconds, all changes being discarded when it is closed, its suitability for software testing and installer verification, and availability on Pro, Enterprise, and Education. ↩

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

These topic pages place the article in a broader service and decision context.

This article connects naturally to the following service pages.

Frequently Asked Questions

Common questions about the topic of this article.

Why does our app's context-menu item on Windows 11 appear only under "Show more options"?
Because on Windows 11 the File Explorer context menu split into two layers, new and old. The only commands that can appear on the new menu are ones that implement the IExplorerCommand interface and are registered in an MSIX package manifest (that is, they have package identity). Classic IContextMenu-based shell extensions were moved to the old menu that opens with "Show more options" (Shift+F10). The extension itself is not broken, so it keeps working for now, but if you want it on the new menu you need to migrate to IExplorerCommand and gain identity either by packaging as MSIX or with a sparse package.
Can our installer set our app as the default app for a file (the app that opens it on double-click)?
No. Choosing the default app is designed to be the user's action, and Windows does not support changing the default app from anywhere other than the system Settings UI. The UserChoice information that holds each user's choice is obfuscated, and a filter driver (UCPD.sys) also protects it against writes from apps. What an installer can do is register a ProgID and verbs, add itself to OpenWithProgIds so it appears as a candidate under "Open with", and steer the user to the default apps settings page. The correct implementation is not to seize the default but to be ready to be chosen.
May I write a shell extension in managed code such as C#?
Microsoft states plainly that writing an in-process shell extension (a context menu handler, an icon handler, and the like) in managed code is not recommended and is unsupported. The extension is loaded into the process of Explorer or of any app that opens a common file dialog, so CLR version conflicts, reentrancy, and non-deterministic object lifetime destabilize the host app. Native C++ is the rule for the implementation. On the other hand, a normal EXE launched from a verb's command, or an out-of-process extension such as a preview handler that runs in a separate process, is fine in managed code.
What is a sparse package (MSIX with external location)?
A small MSIX package that contains no app files, only a manifest (identity information). For an app installed normally by an existing installer (MSI, Inno Setup, and so on), you register the package with Add-AppxPackage -ExternalLocation pointing at the install folder; the app then acquires package identity and can use features that require identity, such as registration on the Windows 11 new context menu and toast notifications. It is available on Windows 10 version 2004 and later, and the package needs a code signature trusted on the target machine. It is the realistic option when you want new-menu support without moving the whole distribution method to MSIX.
What should I do when a context-menu item appears twice, or will not go away?
First isolate the cause by checking which menu it appears on: the new menu or the old menu (Show more options). A double entry is typically either a classic registry registration coexisting with an MSIX manifest registration, or a ProgID or extension CLSID registration that was not cleaned up at uninstall. After changing associations, also suspect a missing SHChangeNotify(SHCNE_ASSOCCHANGED) notification; right after package registration, suspect a missing Explorer restart. If that still does not resolve it, temporarily disable non-Microsoft extensions in ShellExView and binary-search to identify the DLL responsible.

Author Profile

Profile page for the article author.

Go Komura

Representative of KomuraSoft LLC

Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.

Back to the Blog