This test seems to work correctly on my Windows 10 OS (22H2) , Windows App SDK 1.6.241114003,
click on a Button, with a "E:\Test\Temp" folder :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace WinUI3_IContextMenu2
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
public enum HRESULT : int
{
S_OK = 0,
S_FALSE = 1,
E_NOINTERFACE = unchecked((int)0x80004002),
E_NOTIMPL = unchecked((int)0x80004001),
E_FAIL = unchecked((int)0x80004005),
E_UNEXPECTED = unchecked((int)0x8000FFFF),
E_OUTOFMEMORY = unchecked((int)0x8007000E)
}
[DllImport("Shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr ILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath);
[DllImport("Shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern HRESULT SHBindToParent(IntPtr pidl, ref Guid riid, ref IShellFolder ppv, ref IntPtr ppidlLast);
[ComImport]
[Guid("000214E6-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IShellFolder
{
HRESULT ParseDisplayName(IntPtr hwnd,
// IBindCtx pbc,
IntPtr pbc,
[MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, [In, Out] ref uint pchEaten, out IntPtr ppidl, [In, Out] ref SFGAO pdwAttributes);
HRESULT EnumObjects(IntPtr hwnd, SHCONTF grfFlags, out IEnumIDList ppenumIDList);
[PreserveSig()]
HRESULT BindToObject(IntPtr pidl,
//IBindCtx pbc,
IntPtr pbc,
[In] ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppv);
HRESULT BindToStorage(IntPtr pidl, IntPtr pbc, [In] ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppv);
HRESULT CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);
//HRESULT CreateViewObject(IntPtr hwndOwner, [In] ref Guid riid, out IntPtr ppv);
HRESULT CreateViewObject(IntPtr hwndOwner, [In] ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppv);
HRESULT GetAttributesOf(uint cidl, IntPtr apidl, [In, Out] ref SFGAO rgfInOut);
HRESULT GetUIObjectOf(IntPtr hwndOwner, uint cidl, ref IntPtr apidl, [In] ref Guid riid, [In, Out] ref uint rgfReserved, out IntPtr ppv);
HRESULT GetDisplayNameOf(IntPtr pidl, SHGDNF uFlags, out STRRET pName);
HRESULT SetNameOf(IntPtr hwnd, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string pszName, SHGDNF uFlags, out IntPtr ppidlOut);
}
[ComImport]
[Guid("000214F2-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumIDList
{
[PreserveSig()]
HRESULT Next(uint celt, out IntPtr rgelt, out int pceltFetched);
[PreserveSig()]
HRESULT Skip(uint celt);
void Reset();
[return: MarshalAs(UnmanagedType.Interface)]
IEnumIDList Clone();
}
[Flags]
public enum SHCONTF : ushort
{
SHCONTF_CHECKING_FOR_CHILDREN = 0x0010,
SHCONTF_FOLDERS = 0x0020,
SHCONTF_NONFOLDERS = 0x0040,
SHCONTF_INCLUDEHIDDEN = 0x0080,
SHCONTF_INIT_ON_FIRST_NEXT = 0x0100,
SHCONTF_NETPRINTERSRCH = 0x0200,
SHCONTF_SHAREABLE = 0x0400,
SHCONTF_STORAGE = 0x0800,
SHCONTF_NAVIGATION_ENUM = 0x1000,
SHCONTF_FASTITEMS = 0x2000,
SHCONTF_FLATLIST = 0x4000,
SHCONTF_ENABLE_ASYNC = 0x8000
}
[Flags]
public enum SFGAO : uint
{
CANCOPY = 0x00000001,
CANMOVE = 0x00000002,
CANLINK = 0x00000004,
STORAGE = 0x00000008,
CANRENAME = 0x00000010,
CANDELETE = 0x00000020,
HASPROPSHEET = 0x00000040,
DROPTARGET = 0x00000100,
CAPABILITYMASK = 0x00000177,
ENCRYPTED = 0x00002000,
ISSLOW = 0x00004000,
GHOSTED = 0x00008000,
LINK = 0x00010000,
SHARE = 0x00020000,
READONLY = 0x00040000,
HIDDEN = 0x00080000,
DISPLAYATTRMASK = 0x000FC000,
STREAM = 0x00400000,
STORAGEANCESTOR = 0x00800000,
VALIDATE = 0x01000000,
REMOVABLE = 0x02000000,
COMPRESSED = 0x04000000,
BROWSABLE = 0x08000000,
FILESYSANCESTOR = 0x10000000,
FOLDER = 0x20000000,
FILESYSTEM = 0x40000000,
HASSUBFOLDER = 0x80000000,
CONTENTSMASK = 0x80000000,
STORAGECAPMASK = 0x70C50008,
PKEYSFGAOMASK = 0x81044000
}
public enum SHGDNF
{
SHGDN_NORMAL = 0,
SHGDN_INFOLDER = 0x1,
SHGDN_FOREDITING = 0x1000,
SHGDN_FORADDRESSBAR = 0x4000,
SHGDN_FORPARSING = 0x8000
}
[StructLayout(LayoutKind.Explicit, Size = 264)]
public struct STRRET
{
[FieldOffset(0)]
public uint uType;
[FieldOffset(4)]
public IntPtr pOleStr;
[FieldOffset(4)]
public uint uOffset;
[FieldOffset(4)]
public IntPtr cStr;
}
[ComImport]
[Guid("000214e4-0000-0000-c000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IContextMenu
{
HRESULT QueryContextMenu(IntPtr hmenu, uint indexMenu, uint idCmdFirst, uint idCmdLast, uint uFlags);
[PreserveSig()]
HRESULT InvokeCommand(ref CMINVOKECOMMANDINFO pici);
[PreserveSig()]
HRESULT GetCommandString(uint idCmd, uint uType, IntPtr pReserved, StringBuilder pszName, uint cchMax);
}
[ComImport]
[Guid("000214f4-0000-0000-c000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IContextMenu2 : IContextMenu
{
new HRESULT QueryContextMenu(IntPtr hmenu, uint indexMenu, uint idCmdFirst, uint idCmdLast, uint uFlags);
[PreserveSig()]
new HRESULT InvokeCommand(ref CMINVOKECOMMANDINFO pici);
[PreserveSig()]
new HRESULT GetCommandString(uint idCmd, uint uType, IntPtr pReserved, StringBuilder pszName, uint cchMax);
[PreserveSig()]
HRESULT HandleMenuMsg(uint uMsg, IntPtr wParam, IntPtr lParam);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CMINVOKECOMMANDINFO
{
public int cbSize;
public int fMask;
public IntPtr hwnd;
public IntPtr lpVerb;
public IntPtr lpParameters;
public IntPtr lpDirectory;
public int nShow;
public int dwHotKey;
public IntPtr hIcon;
}
public const int CMF_NORMAL = 0x00000000;
public const int CMF_DEFAULTONLY = 0x00000001;
public const int CMF_VERBSONLY = 0x00000002;
public const int CMF_EXPLORE = 0x00000004;
public const int CMF_NOVERBS = 0x00000008;
public const int CMF_CANRENAME = 0x00000010;
public const int CMF_NODEFAULT = 0x00000020;
public const int CMF_INCLUDESTATIC = 0x00000040;
public const int CMF_ITEMMENU = 0x00000080;
public const int CMF_EXTENDEDVERBS = 0x00000100;
public const int CMF_DISABLEDVERBS = 0x00000200;
public const int CMF_ASYNCVERBSTATE = 0x00000400;
public const int CMF_OPTIMIZEFORINVOKE = 0x00000800;
public const int CMF_SYNCCASCADEMENU = 0x00001000;
public const int CMF_DONOTPICKDEFAULT = 0x00002000;
public const int CMF_RESERVED = unchecked((int)0xffff0000);
public const int SW_SHOWNORMAL = 1;
public const int GCS_VERBA = 0x00000000; // canonical verb
public const int GCS_HELPTEXTA = 0x00000001; // help text (for status bar)
public const int GCS_VALIDATEA = 0x00000002; // validate command exists
public const int GCS_VERBW = 0x00000004; // canonical verb (unicode)
public const int GCS_HELPTEXTW = 0x00000005; // help text (unicode version)
public const int GCS_VALIDATEW = 0x00000006; // validate command exists (unicode)
public const int GCS_VERBICONW = 0x00000014; // icon string (unicode)
public const int GCS_UNICODE = 0x00000004; // for bit testing - Unicode string
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreatePopupMenu();
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint TrackPopupMenu(IntPtr hMenu, uint uFlags, int x, int y, int nReserved, IntPtr hWnd, IntPtr prcRect);
public const int TPM_LEFTBUTTON = 0x0000;
public const int TPM_RIGHTBUTTON = 0x0002;
public const int TPM_LEFTALIGN = 0x0000;
public const int TPM_CENTERALIGN = 0x0004;
public const int TPM_RIGHTALIGN = 0x0008;
public const int TPM_TOPALIGN = 0x0000;
public const int TPM_VCENTERALIGN = 0x0010;
public const int TPM_BOTTOMALIGN = 0x0020;
public const int TPM_HORIZONTAL = 0x0000; /* Horz alignment matters more */
public const int TPM_VERTICAL = 0x0040; /* Vert alignment matters more */
public const int TPM_NONOTIFY = 0x0080; /* Don't send any notification msgs */
public const int TPM_RETURNCMD = 0x0100;
public const int TPM_RECURSE = 0x0001;
public const int TPM_HORPOSANIMATION = 0x0400;
public const int TPM_HORNEGANIMATION = 0x0800;
public const int TPM_VERPOSANIMATION = 0x1000;
public const int TPM_VERNEGANIMATION = 0x2000;
public const int TPM_NOANIMATION = 0x4000;
public const int TPM_LAYOUTRTL = 0x8000;
public const int TPM_WORKAREA = 0x10000;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool GetCursorPos(ref POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
public POINT(int x, int y)
{
this.x = x;
this.y = y;
}
}
public delegate int SUBCLASSPROC(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam, IntPtr uIdSubclass, uint dwRefData);
[DllImport("Comctl32.dll", SetLastError = true)]
public static extern bool SetWindowSubclass(IntPtr hWnd, SUBCLASSPROC pfnSubclass, uint uIdSubclass, uint dwRefData);
[DllImport("Comctl32.dll", SetLastError = true)]
public static extern int DefSubclassProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
private SUBCLASSPROC SubClassDelegate;
public const int WM_INITMENUPOPUP = 0x0117;
[DllImport("Ole32.dll", SetLastError = true)]
public static extern HRESULT OleInitialize(IntPtr pvReserved);
private IntPtr hWndMain = IntPtr.Zero;
IContextMenu2? m_pContextMenu2 = null;
public MainWindow()
{
this.InitializeComponent();
hWndMain = WinRT.Interop.WindowNative.GetWindowHandle(this);
// For some submenus
SubClassDelegate = new SUBCLASSPROC(WindowSubClass);
bool bRet = SetWindowSubclass(hWndMain, SubClassDelegate, 0, 0);
// For copy/cut
OleInitialize(IntPtr.Zero);
}
private int WindowSubClass(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam, IntPtr uIdSubclass, uint dwRefData)
{
switch (uMsg)
{
case WM_INITMENUPOPUP:
{
if (m_pContextMenu2 != null)
m_pContextMenu2.HandleMenuMsg(uMsg, wParam, lParam);
return 0;
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
//myButton.Content = "Clicked";
IntPtr pItemIDL = ILCreateFromPath("E:\\Test\\Temp");
if (pItemIDL != IntPtr.Zero)
{
IntPtr pcm = IntPtr.Zero;
Guid IID_IContextMenu = new Guid("000214E4-0000-0000-C000-000000000046");
HRESULT hr = SHGetUIObjectFromFullPIDL(pItemIDL, IntPtr.Zero, ref IID_IContextMenu, ref pcm);
if (hr == HRESULT.S_OK)
{
IContextMenu? pContextMenu = Marshal.GetObjectForIUnknown(pcm) as IContextMenu;
IntPtr hMenu = CreatePopupMenu();
hr = pContextMenu.QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
if (hr == HRESULT.S_OK)
{
m_pContextMenu2 = (IContextMenu2)pContextMenu;
POINT pt = new POINT();
GetCursorPos(ref pt);
int nX = pt.x, nY = pt.y;
uint nCmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD, nX, nY, 0, hWndMain, IntPtr.Zero);
if (nCmd != 0)
{
CMINVOKECOMMANDINFO cmi = new CMINVOKECOMMANDINFO();
cmi.cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFO));
cmi.fMask = 0;
cmi.hwnd = hWndMain;
cmi.lpVerb = (IntPtr)(nCmd - 1);
cmi.lpParameters = IntPtr.Zero;
cmi.lpDirectory = IntPtr.Zero;
cmi.nShow = SW_SHOWNORMAL;
cmi.dwHotKey = 0;
cmi.hIcon = IntPtr.Zero; ;
hr = pContextMenu.InvokeCommand(ref cmi);
}
}
}
}
}
// From MSDN : https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shbrowseforfolderw
private HRESULT SHGetUIObjectFromFullPIDL(IntPtr pidl, IntPtr hwnd, ref Guid riid, ref IntPtr ppv)
{
IntPtr pidlChild = IntPtr.Zero;
IShellFolder? psf = null;
ppv = IntPtr.Zero;
Guid IID_IShellFolder = new Guid("000214E6-0000-0000-C000-000000000046");
HRESULT hr = SHBindToParent(pidl, ref IID_IShellFolder, ref psf, ref pidlChild);
if (hr == HRESULT.S_OK)
{
uint rgfReserved = 0;
hr = psf.GetUIObjectOf(hwnd, 1, ref pidlChild, riid, ref rgfReserved, out ppv);
Marshal.ReleaseComObject(psf);
}
return hr;
}
}
}