// // Copyright (c) PlaceholderCompany. All rights reserved. // namespace SystemTrayMenu.Utilities { using System; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; using SystemTrayMenu.Helper; /// /// "Stand-alone" shell context menu /// /// It isn't really debugged but is mostly working. /// Create an instance and call ShowContextMenu with a list of FileInfo for the files. /// Limitation is that it only handles files in the same directory but it can be fixed /// by changing the way files are translated into PIDLs. /// /// Based on FileBrowser in C# from CodeProject /// http://www.codeproject.com/useritems/FileBrowser.asp /// /// Hooking class taken from MSDN Magazine Cutting Edge column /// http://msdn.microsoft.com/msdnmag/issues/02/10/CuttingEdge/ /// /// Andreas Johansson /// afjohansson@hotmail.com /// http://afjohansson.spaces.live.com /// . /// /// /// ShellContextMenu scm = new ShellContextMenu(); /// FileInfo[] files = new FileInfo[1]; /// files[0] = new FileInfo(@"c:\windows\notepad.exe"); /// scm.ShowContextMenu(this.Handle, files, Cursor.Position);. /// public class ShellContextMenu : NativeWindow { private const int MaxPath = 260; private const uint CmdFirst = 1; private const uint CmdLast = 30000; private const int ResultOK = 0; private static readonly int CbMenuItemInfo = Marshal.SizeOf(typeof(MENUITEMINFO)); private static readonly int CbInvokeCommand = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)); private static Guid iidIShellFolder = new Guid("{000214E6-0000-0000-C000-000000000046}"); private static Guid iidIContextMenu = new Guid("{000214e4-0000-0000-c000-000000000046}"); private static Guid iidIContextMenu2 = new Guid("{000214f4-0000-0000-c000-000000000046}"); private static Guid iidIContextMenu3 = new Guid("{bcfce0a0-ec17-11d0-8d10-00a0c90f2719}"); private IContextMenu oContextMenu; private IContextMenu2 oContextMenu2; private IContextMenu3 oContextMenu3; private IShellFolder oDesktopFolder; private IShellFolder oParentFolder; private IntPtr[] arrPIDLs; private string strParentFolder; /// /// Initializes a new instance of the class. /// public ShellContextMenu() { CreateHandle(new CreateParams()); } /// /// Finalizes an instance of the class. /// ~ShellContextMenu() { ReleaseAll(); } // Defines the values used with the IShellFolder::GetDisplayNameOf and IShellFolder::SetNameOf // methods to specify the type of file or folder names used by those methods [Flags] private enum SHGNO { NORMAL = 0x0000, INFOLDER = 0x0001, FOREDITING = 0x1000, FORADDRESSBAR = 0x4000, FORPARSING = 0x8000, } // The attributes that the caller is requesting, when calling IShellFolder::GetAttributesOf [Flags] private enum SFGAO : uint { BROWSABLE = 0x8000000, CANCOPY = 1, CANDELETE = 0x20, CANLINK = 4, CANMONIKER = 0x400000, // HASSTORAGE = 0x400000, //STREAM = 0x400000, CANMOVE = 2, CANRENAME = 0x10, CAPABILITYMASK = 0x177, COMPRESSED = 0x4000000, CONTENTSMASK = 0x80000000, // HASSUBFOLDER = 0x80000000, DISPLAYATTRMASK = 0xfc000, DROPTARGET = 0x100, ENCRYPTED = 0x2000, FILESYSANCESTOR = 0x10000000, FILESYSTEM = 0x40000000, FOLDER = 0x20000000, GHOSTED = 0x8000, HASPROPSHEET = 0x40, HIDDEN = 0x80000, ISSLOW = 0x4000, LINK = 0x10000, NEWCONTENT = 0x200000, NONENUMERATED = 0x100000, READONLY = 0x40000, REMOVABLE = 0x2000000, SHARE = 0x20000, STORAGE = 8, STORAGEANCESTOR = 0x800000, STORAGECAPMASK = 0x70c50008, VALIDATE = 0x1000000, } // Determines the type of items included in an enumeration. // These values are used with the IShellFolder::EnumObjects method [Flags] private enum SHCONTF { FOLDERS = 0x0020, NONFOLDERS = 0x0040, INCLUDEHIDDEN = 0x0080, INIT_ON_FIRST_NEXT = 0x0100, NETPRINTERSRCH = 0x0200, SHAREABLE = 0x0400, STORAGE = 0x0800, } // Specifies how the shortcut menu can be changed when calling IContextMenu::QueryContextMenu [Flags] private enum CMF : uint { NORMAL = 0x00000000, DEFAULTONLY = 0x00000001, VERBSONLY = 0x00000002, EXPLORE = 0x00000004, NOVERBS = 0x00000008, CANRENAME = 0x00000010, NODEFAULT = 0x00000020, INCLUDESTATIC = 0x00000040, EXTENDEDVERBS = 0x00000100, RESERVED = 0xffff0000, } // Flags specifying the information to return when calling IContextMenu::GetCommandString [Flags] private enum GCS : uint { VERBA = 0, HELPTEXTA = 1, VALIDATEA = 2, VERBW = 4, HELPTEXTW = 5, VALIDATEW = 6, } // The cmd for a custom added menu item private enum CMD_CUSTOM { ExpandCollapse = (int)CmdLast + 1, } // Flags used with the CMINVOKECOMMANDINFOEX structure [Flags] private enum CMIC : uint { HOTKEY = 0x00000020, ICON = 0x00000010, FLAG_NO_UI = 0x00000400, UNICODE = 0x00004000, NO_CONSOLE = 0x00008000, ASYNCOK = 0x00100000, NOZONECHECKS = 0x00800000, SHIFT_DOWN = 0x10000000, CONTROL_DOWN = 0x40000000, FLAG_LOG_USAGE = 0x04000000, PTINVOKE = 0x20000000, } // Specifies how the window is to be shown [Flags] private enum SW { HIDE = 0, SHOWNORMAL = 1, // NORMAL = 1, SHOWMINIMIZED = 2, SHOWMAXIMIZED = 3, // MAXIMIZE = 3, SHOWNOACTIVATE = 4, SHOW = 5, MINIMIZE = 6, SHOWMINNOACTIVE = 7, SHOWNA = 8, RESTORE = 9, SHOWDEFAULT = 10, } // Window message flags [Flags] private enum WM : uint { ACTIVATE = 0x6, ACTIVATEAPP = 0x1C, AFXFIRST = 0x360, AFXLAST = 0x37F, APP = 0x8000, ASKCBFORMATNAME = 0x30C, CANCELJOURNAL = 0x4B, CANCELMODE = 0x1F, CAPTURECHANGED = 0x215, CHANGECBCHAIN = 0x30D, CHAR = 0x102, CHARTOITEM = 0x2F, CHILDACTIVATE = 0x22, CLEAR = 0x303, CLOSE = 0x10, COMMAND = 0x111, COMPACTING = 0x41, COMPAREITEM = 0x39, CONTEXTMENU = 0x7B, COPY = 0x301, COPYDATA = 0x4A, CREATE = 0x1, CTLCOLORBTN = 0x135, CTLCOLORDLG = 0x136, CTLCOLOREDIT = 0x133, CTLCOLORLISTBOX = 0x134, CTLCOLORMSGBOX = 0x132, CTLCOLORSCROLLBAR = 0x137, CTLCOLORSTATIC = 0x138, CUT = 0x300, DEADCHAR = 0x103, DELETEITEM = 0x2D, DESTROY = 0x2, DESTROYCLIPBOARD = 0x307, DEVICECHANGE = 0x219, DEVMODECHANGE = 0x1B, DISPLAYCHANGE = 0x7E, DRAWCLIPBOARD = 0x308, DRAWITEM = 0x2B, DROPFILES = 0x233, ENABLE = 0xA, ENDSESSION = 0x16, ENTERIDLE = 0x121, ENTERMENULOOP = 0x211, ENTERSIZEMOVE = 0x231, ERASEBKGND = 0x14, EXITMENULOOP = 0x212, EXITSIZEMOVE = 0x232, FONTCHANGE = 0x1D, GETDLGCODE = 0x87, GETFONT = 0x31, GETHOTKEY = 0x33, GETICON = 0x7F, GETMINMAXINFO = 0x24, GETOBJECT = 0x3D, GETSYSMENU = 0x313, GETTEXT = 0xD, GETTEXTLENGTH = 0xE, HANDHELDFIRST = 0x358, HANDHELDLAST = 0x35F, HELP = 0x53, HOTKEY = 0x312, HSCROLL = 0x114, HSCROLLCLIPBOARD = 0x30E, ICONERASEBKGND = 0x27, IME_CHAR = 0x286, IME_COMPOSITION = 0x10F, // IME_KEYLAST = 0x10F, IME_COMPOSITIONFULL = 0x284, IME_CONTROL = 0x283, IME_ENDCOMPOSITION = 0x10E, IME_KEYDOWN = 0x290, IME_KEYUP = 0x291, IME_NOTIFY = 0x282, IME_REQUEST = 0x288, IME_SELECT = 0x285, IME_SETCONTEXT = 0x281, IME_STARTCOMPOSITION = 0x10D, INITDIALOG = 0x110, INITMENU = 0x116, INITMENUPOPUP = 0x117, INPUTLANGCHANGE = 0x51, INPUTLANGCHANGEREQUEST = 0x50, KEYDOWN = 0x100, // KEYFIRST = 0x100, KEYLAST = 0x108, KEYUP = 0x101, KILLFOCUS = 0x8, LBUTTONDBLCLK = 0x203, LBUTTONDOWN = 0x201, LBUTTONUP = 0x202, LVM_GETEDITCONTROL = 0x1018, LVM_SETIMAGELIST = 0x1003, MBUTTONDBLCLK = 0x209, MBUTTONDOWN = 0x207, MBUTTONUP = 0x208, MDIACTIVATE = 0x222, MDICASCADE = 0x227, MDICREATE = 0x220, MDIDESTROY = 0x221, MDIGETACTIVE = 0x229, MDIICONARRANGE = 0x228, MDIMAXIMIZE = 0x225, MDINEXT = 0x224, MDIREFRESHMENU = 0x234, MDIRESTORE = 0x223, MDISETMENU = 0x230, MDITILE = 0x226, MEASUREITEM = 0x2C, MENUCHAR = 0x120, MENUCOMMAND = 0x126, MENUDRAG = 0x123, MENUGETOBJECT = 0x124, MENURBUTTONUP = 0x122, MENUSELECT = 0x11F, MOUSEACTIVATE = 0x21, MOUSEFIRST = 0x200, // MOUSEMOVE = 0x200, MOUSEHOVER = 0x2A1, MOUSELAST = 0x20A, // MOUSEWHEEL = 0x20A, MOUSELEAVE = 0x2A3, MOVE = 0x3, MOVING = 0x216, NCACTIVATE = 0x86, NCCALCSIZE = 0x83, NCCREATE = 0x81, NCDESTROY = 0x82, NCHITTEST = 0x84, NCLBUTTONDBLCLK = 0xA3, NCLBUTTONDOWN = 0xA1, NCLBUTTONUP = 0xA2, NCMBUTTONDBLCLK = 0xA9, NCMBUTTONDOWN = 0xA7, NCMBUTTONUP = 0xA8, NCMOUSEHOVER = 0x2A0, NCMOUSELEAVE = 0x2A2, NCMOUSEMOVE = 0xA0, NCPAINT = 0x85, NCRBUTTONDBLCLK = 0xA6, NCRBUTTONDOWN = 0xA4, NCRBUTTONUP = 0xA5, NEXTDLGCTL = 0x28, NEXTMENU = 0x213, NOTIFY = 0x4E, NOTIFYFORMAT = 0x55, NULL = 0x0, PAINT = 0xF, PAINTCLIPBOARD = 0x309, PAINTICON = 0x26, PALETTECHANGED = 0x311, PALETTEISCHANGING = 0x310, PARENTNOTIFY = 0x210, PASTE = 0x302, PENWINFIRST = 0x380, PENWINLAST = 0x38F, POWER = 0x48, PRINT = 0x317, PRINTCLIENT = 0x318, QUERYDRAGICON = 0x37, QUERYENDSESSION = 0x11, QUERYNEWPALETTE = 0x30F, QUERYOPEN = 0x13, QUEUESYNC = 0x23, QUIT = 0x12, RBUTTONDBLCLK = 0x206, RBUTTONDOWN = 0x204, RBUTTONUP = 0x205, RENDERALLFORMATS = 0x306, RENDERFORMAT = 0x305, SETCURSOR = 0x20, SETFOCUS = 0x7, SETFONT = 0x30, SETHOTKEY = 0x32, SETICON = 0x80, SETMARGINS = 0xD3, SETREDRAW = 0xB, SETTEXT = 0xC, SETTINGCHANGE = 0x1A, // WININICHANGE = 0x1A, SHOWWINDOW = 0x18, SIZE = 0x5, SIZECLIPBOARD = 0x30B, SIZING = 0x214, SPOOLERSTATUS = 0x2A, STYLECHANGED = 0x7D, STYLECHANGING = 0x7C, SYNCPAINT = 0x88, SYSCHAR = 0x106, SYSCOLORCHANGE = 0x15, SYSCOMMAND = 0x112, SYSDEADCHAR = 0x107, SYSKEYDOWN = 0x104, SYSKEYUP = 0x105, TCARD = 0x52, TIMECHANGE = 0x1E, TIMER = 0x113, TVM_GETEDITCONTROL = 0x110F, TVM_SETIMAGELIST = 0x1109, UNDO = 0x304, UNINITMENUPOPUP = 0x125, USER = 0x400, USERCHANGED = 0x54, VKEYTOITEM = 0x2E, VSCROLL = 0x115, VSCROLLCLIPBOARD = 0x30A, WINDOWPOSCHANGED = 0x47, WINDOWPOSCHANGING = 0x46, SH_NOTIFY = 0x0401, } // Specifies the content of the new menu item [Flags] private enum MFT : uint { GRAYED = 0x00000003, // DISABLED = 0x00000003, CHECKED = 0x00000008, SEPARATOR = 0x00000800, RADIOCHECK = 0x00000200, BITMAP = 0x00000004, OWNERDRAW = 0x00000100, MENUBARBREAK = 0x00000020, MENUBREAK = 0x00000040, RIGHTORDER = 0x00002000, BYCOMMAND = 0x00000000, BYPOSITION = 0x00000400, POPUP = 0x00000010, } // Specifies the state of the new menu item [Flags] private enum MFS : uint { GRAYED = 0x00000003, // DISABLED = 0x00000003, CHECKED = 0x00000008, HILITE = 0x00000080, ENABLED = 0x00000000, // UNCHECKED = 0x00000000, // UNHILITE = 0x00000000, DEFAULT = 0x00001000, } // Specifies the content of the new menu item [Flags] private enum MIIM : uint { BITMAP = 0x80, CHECKMARKS = 0x08, DATA = 0x20, FTYPE = 0x100, ID = 0x02, STATE = 0x01, STRING = 0x40, SUBMENU = 0x04, TYPE = 0x10, } // Indicates the type of storage medium being used in a data transfer [Flags] private enum TYMED { ENHMF = 0x40, FILE = 2, GDI = 0x10, HGLOBAL = 1, ISTORAGE = 8, ISTREAM = 4, MFPICT = 0x20, NULL = 0, } [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("000214E6-0000-0000-C000-000000000046")] private interface IShellFolder { // Translates a file object's or folder's display name into an item identifier list. // Return value: error code, if any [PreserveSig] int ParseDisplayName( IntPtr hwnd, IntPtr pbc, [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, ref uint pchEaten, out IntPtr ppidl, ref SFGAO pdwAttributes); // Allows a client to determine the contents of a folder by creating an item // identifier enumeration object and returning its IEnumIDList interface. // Return value: error code, if any [PreserveSig] int EnumObjects( IntPtr hwnd, SHCONTF grfFlags, out IntPtr enumIDList); // Retrieves an IShellFolder object for a subfolder. // Return value: error code, if any [PreserveSig] int BindToObject( IntPtr pidl, IntPtr pbc, ref Guid riid, out IntPtr ppv); // Requests a pointer to an object's storage interface. // Return value: error code, if any [PreserveSig] int BindToStorage( IntPtr pidl, IntPtr pbc, ref Guid riid, out IntPtr ppv); // Determines the relative order of two file objects or folders, given their // item identifier lists. Return value: If this method is successful, the // CODE field of the HRESULT contains one of the following values (the code // can be retrived using the helper function GetHResultCode): Negative A // negative return value indicates that the first item should precede // the second (pidl1 < pidl2). // Positive A positive return value indicates that the first item should // follow the second (pidl1 > pidl2). Zero A return value of zero // indicates that the two items are the same (pidl1 = pidl2). [PreserveSig] int CompareIDs( IntPtr lParam, IntPtr pidl1, IntPtr pidl2); // Requests an object that can be used to obtain information from or interact // with a folder object. // Return value: error code, if any [PreserveSig] int CreateViewObject( IntPtr hwndOwner, Guid riid, out IntPtr ppv); // Retrieves the attributes of one or more file objects or subfolders. // Return value: error code, if any [PreserveSig] int GetAttributesOf( uint cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, ref SFGAO rgfInOut); // Retrieves an OLE interface that can be used to carry out actions on the // specified file objects or folders. // Return value: error code, if any [PreserveSig] int GetUIObjectOf( IntPtr hwndOwner, uint cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, ref Guid riid, IntPtr rgfReserved, out IntPtr ppv); // Retrieves the display name for the specified file object or subfolder. // Return value: error code, if any [PreserveSig] int GetDisplayNameOf( IntPtr pidl, SHGNO uFlags, IntPtr lpName); // Sets the display name of a file object or subfolder, changing the item // identifier in the process. // Return value: error code, if any [PreserveSig] int SetNameOf( IntPtr hwnd, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string pszName, SHGNO uFlags, out IntPtr ppidlOut); } [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [GuidAttribute("000214e4-0000-0000-c000-000000000046")] private interface IContextMenu { // Adds commands to a shortcut menu [PreserveSig] int QueryContextMenu( IntPtr hmenu, uint iMenu, uint idCmdFirst, uint idCmdLast, CMF uFlags); // Carries out the command associated with a shortcut menu item [PreserveSig] int InvokeCommand( ref CMINVOKECOMMANDINFOEX info); // Retrieves information about a shortcut menu command, // including the help string and the language-independent, // or canonical, name for the command [PreserveSig] int GetCommandString( uint idcmd, GCS uflags, uint reserved, [MarshalAs(UnmanagedType.LPArray)] byte[] commandstring, int cch); } [ComImport] [Guid("000214f4-0000-0000-c000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IContextMenu2 { // Adds commands to a shortcut menu [PreserveSig] int QueryContextMenu( IntPtr hmenu, uint iMenu, uint idCmdFirst, uint idCmdLast, CMF uFlags); // Carries out the command associated with a shortcut menu item [PreserveSig] int InvokeCommand( ref CMINVOKECOMMANDINFOEX info); // Retrieves information about a shortcut menu command, // including the help string and the language-independent, // or canonical, name for the command [PreserveSig] int GetCommandString( uint idcmd, GCS uflags, uint reserved, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder commandstring, int cch); // Allows client objects of the IContextMenu interface to // handle messages associated with owner-drawn menu items [PreserveSig] int HandleMenuMsg( uint uMsg, IntPtr wParam, IntPtr lParam); } [ComImport] [Guid("bcfce0a0-ec17-11d0-8d10-00a0c90f2719")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IContextMenu3 { // Adds commands to a shortcut menu [PreserveSig] int QueryContextMenu( IntPtr hmenu, uint iMenu, uint idCmdFirst, uint idCmdLast, CMF uFlags); // Carries out the command associated with a shortcut menu item [PreserveSig] int InvokeCommand( ref CMINVOKECOMMANDINFOEX info); // Retrieves information about a shortcut menu command, // including the help string and the language-independent, // or canonical, name for the command [PreserveSig] int GetCommandString( uint idcmd, GCS uflags, uint reserved, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder commandstring, int cch); // Allows client objects of the IContextMenu interface to // handle messages associated with owner-drawn menu items [PreserveSig] int HandleMenuMsg( uint uMsg, IntPtr wParam, IntPtr lParam); // Allows client objects of the IContextMenu3 interface to // handle messages associated with owner-drawn menu items [PreserveSig] int HandleMenuMsg2( uint uMsg, IntPtr wParam, IntPtr lParam, IntPtr plResult); } /// /// Shows the context menu. /// /// FileInfos (should all be in same directory). /// Where to show the menu. public void ShowContextMenu(FileInfo[] files, Point pointScreen) { // Release all resources first. ReleaseAll(); arrPIDLs = GetPIDLs(files); ShowContextMenu(pointScreen); } /// /// Shows the context menu. /// /// DirectoryInfos (should all be in same directory). /// Where to show the menu. public void ShowContextMenu(DirectoryInfo[] dirs, Point pointScreen) { // Release all resources first. ReleaseAll(); arrPIDLs = GetPIDLs(dirs); ShowContextMenu(pointScreen); } /// /// Shows the context menu. /// /// Where to show the menu. public void ShowContextMenu(Point pointScreen) { IntPtr pMenu = IntPtr.Zero, iContextMenuPtr = IntPtr.Zero, iContextMenuPtr2 = IntPtr.Zero, iContextMenuPtr3 = IntPtr.Zero; try { if (arrPIDLs == null) { ReleaseAll(); return; } if (!GetContextMenuInterfaces(oParentFolder, arrPIDLs, out iContextMenuPtr)) { ReleaseAll(); return; } pMenu = DllImports.NativeMethods.User32CreatePopupMenu(); int nResult = oContextMenu.QueryContextMenu( pMenu, 0, CmdFirst, CmdLast, CMF.EXPLORE | CMF.NORMAL | ((Control.ModifierKeys & Keys.Shift) != 0 ? CMF.EXTENDEDVERBS : 0)); Marshal.QueryInterface(iContextMenuPtr, ref iidIContextMenu2, out iContextMenuPtr2); Marshal.QueryInterface(iContextMenuPtr, ref iidIContextMenu3, out iContextMenuPtr3); oContextMenu2 = (IContextMenu2)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr2, typeof(IContextMenu2)); oContextMenu3 = (IContextMenu3)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr3, typeof(IContextMenu3)); uint nSelected = DllImports.NativeMethods.User32TrackPopupMenuEx( pMenu, DllImports.NativeMethods.TPM.RETURNCMD, pointScreen.X, pointScreen.Y, Handle, IntPtr.Zero); DllImports.NativeMethods.User32DestroyMenu(pMenu); pMenu = IntPtr.Zero; if (nSelected != 0) { InvokeCommand(oContextMenu, nSelected, strParentFolder, pointScreen); } } catch { throw; } finally { if (pMenu != IntPtr.Zero) { DllImports.NativeMethods.User32DestroyMenu(pMenu); } if (iContextMenuPtr != IntPtr.Zero) { Marshal.Release(iContextMenuPtr); } if (iContextMenuPtr2 != IntPtr.Zero) { Marshal.Release(iContextMenuPtr2); } if (iContextMenuPtr3 != IntPtr.Zero) { Marshal.Release(iContextMenuPtr3); } ReleaseAll(); } } /// /// Free the PIDLs. /// /// Array of PIDLs (IntPtr). protected static void FreePIDLs(IntPtr[] arrPIDLs) { if (arrPIDLs != null) { for (int n = 0; n < arrPIDLs.Length; n++) { if (arrPIDLs[n] != IntPtr.Zero) { Marshal.FreeCoTaskMem(arrPIDLs[n]); arrPIDLs[n] = IntPtr.Zero; } } } } /// /// This method receives WindowMessages. It will make the "Open With" and "Send To" work /// by calling HandleMenuMsg and HandleMenuMsg2. It will also call the OnContextMenuMouseHover /// method of Browser when hovering over a ContextMenu item. /// /// the Message of the Browser's WndProc. protected override void WndProc(ref Message m) { if (oContextMenu != null && m.Msg == (int)WM.MENUSELECT && ((int)ShellHelper.HiWord(m.WParam) & (int)MFT.SEPARATOR) == 0 && ((int)ShellHelper.HiWord(m.WParam) & (int)MFT.POPUP) == 0) { } if (oContextMenu2 != null && (m.Msg == (int)WM.INITMENUPOPUP || m.Msg == (int)WM.MEASUREITEM || m.Msg == (int)WM.DRAWITEM)) { if (oContextMenu2.HandleMenuMsg( (uint)m.Msg, m.WParam, m.LParam) == ResultOK) { return; } } if (oContextMenu3 != null && m.Msg == (int)WM.MENUCHAR) { if (oContextMenu3.HandleMenuMsg2( (uint)m.Msg, m.WParam, m.LParam, IntPtr.Zero) == ResultOK) { return; } } base.WndProc(ref m); } /// /// Get the PIDLs. /// /// Array of FileInfo. /// Array of PIDLs. protected IntPtr[] GetPIDLs(FileInfo[] arrFI) { if (arrFI == null || arrFI.Length == 0) { return null; } IShellFolder oParentFolder = GetParentFolder(arrFI[0].DirectoryName); if (oParentFolder == null) { return null; } IntPtr[] arrPIDLs = new IntPtr[arrFI.Length]; int n = 0; foreach (FileInfo fi in arrFI) { // Get the file relative to folder uint pchEaten = 0; SFGAO pdwAttributes = 0; IntPtr pPIDL = IntPtr.Zero; int nResult = oParentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, fi.Name, ref pchEaten, out pPIDL, ref pdwAttributes); if (nResult != ResultOK) { FreePIDLs(arrPIDLs); return null; } arrPIDLs[n] = pPIDL; n++; } return arrPIDLs; } /// /// Get the PIDLs. /// /// Array of DirectoryInfo. /// Array of PIDLs. protected IntPtr[] GetPIDLs(DirectoryInfo[] arrFI) { if (arrFI == null || arrFI.Length == 0 || arrFI[0].Parent == null) { return null; } IShellFolder oParentFolder = GetParentFolder(arrFI[0].Parent.FullName); if (oParentFolder == null) { return null; } IntPtr[] arrPIDLs = new IntPtr[arrFI.Length]; int n = 0; foreach (DirectoryInfo fi in arrFI) { // Get the file relative to folder uint pchEaten = 0; SFGAO pdwAttributes = 0; IntPtr pPIDL = IntPtr.Zero; int nResult = oParentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, fi.Name, ref pchEaten, out pPIDL, ref pdwAttributes); if (nResult != ResultOK) { FreePIDLs(arrPIDLs); return null; } arrPIDLs[n] = pPIDL; n++; } return arrPIDLs; } private static void InvokeCommand(IContextMenu contextMenu, uint nCmd, string strFolder, Point pointInvoke) { CMINVOKECOMMANDINFOEX invoke = new CMINVOKECOMMANDINFOEX { CbSize = CbInvokeCommand, LpVerb = (IntPtr)(nCmd - CmdFirst), LpDirectory = strFolder, LpVerbW = (IntPtr)(nCmd - CmdFirst), LpDirectoryW = strFolder, FMask = CMIC.UNICODE | CMIC.PTINVOKE | ((Control.ModifierKeys & Keys.Control) != 0 ? CMIC.CONTROL_DOWN : 0) | ((Control.ModifierKeys & Keys.Shift) != 0 ? CMIC.SHIFT_DOWN : 0), PtInvoke = new POINT(pointInvoke.X, pointInvoke.Y), NShow = SW.SHOWNORMAL, }; _ = contextMenu.InvokeCommand(ref invoke); } /// Gets the interfaces to the context menu. /// Parent folder. /// PIDLs. /// true if it got the interfaces, otherwise false. private bool GetContextMenuInterfaces(IShellFolder oParentFolder, IntPtr[] arrPIDLs, out IntPtr ctxMenuPtr) { int nResult = oParentFolder.GetUIObjectOf( IntPtr.Zero, (uint)arrPIDLs.Length, arrPIDLs, ref iidIContextMenu, IntPtr.Zero, out ctxMenuPtr); if (nResult == ResultOK) { oContextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(ctxMenuPtr, typeof(IContextMenu)); /*IntPtr pUnknownContextMenu2 = IntPtr.Zero; if (S_OK == Marshal.QueryInterface(pUnknownContextMenu, ref IID_IContextMenu2, out pUnknownContextMenu2)) { _oContextMenu2 = (IContextMenu2)Marshal.GetTypedObjectForIUnknown(pUnknownContextMenu2, typeof(IContextMenu2)); } IntPtr pUnknownContextMenu3 = IntPtr.Zero; if (S_OK == Marshal.QueryInterface(pUnknownContextMenu, ref IID_IContextMenu3, out pUnknownContextMenu3)) { _oContextMenu3 = (IContextMenu3)Marshal.GetTypedObjectForIUnknown(pUnknownContextMenu3, typeof(IContextMenu3)); }*/ return true; } else { ctxMenuPtr = IntPtr.Zero; oContextMenu = null; return false; } } /// /// Release all allocated interfaces, PIDLs. /// private void ReleaseAll() { if (oContextMenu != null) { Marshal.ReleaseComObject(oContextMenu); oContextMenu = null; } if (oContextMenu2 != null) { Marshal.ReleaseComObject(oContextMenu2); oContextMenu2 = null; } if (oContextMenu3 != null) { Marshal.ReleaseComObject(oContextMenu3); oContextMenu3 = null; } if (oDesktopFolder != null) { Marshal.ReleaseComObject(oDesktopFolder); oDesktopFolder = null; } if (oParentFolder != null) { Marshal.ReleaseComObject(oParentFolder); oParentFolder = null; } if (arrPIDLs != null) { FreePIDLs(arrPIDLs); arrPIDLs = null; } } /// /// Gets the desktop folder. /// /// IShellFolder for desktop folder. private IShellFolder GetDesktopFolder() { if (oDesktopFolder == null) { // Get desktop IShellFolder int nResult = DllImports.NativeMethods.Shell32SHGetDesktopFolder(out IntPtr pUnkownDesktopFolder); if (nResult != ResultOK) { #pragma warning disable CA1303 // Do not pass literals as localized parameters throw new ShellContextMenuException("Failed to get the desktop shell folder"); #pragma warning restore CA1303 //=> Exceptions not translated in logfile => OK } oDesktopFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnkownDesktopFolder, typeof(IShellFolder)); } return oDesktopFolder; } /// /// Gets the parent folder. /// /// Folder path. /// IShellFolder for the folder (relative from the desktop). private IShellFolder GetParentFolder(string folderName) { if (oParentFolder == null) { IShellFolder oDesktopFolder = GetDesktopFolder(); if (oDesktopFolder == null) { return null; } // Get the PIDL for the folder file is in uint pchEaten = 0; SFGAO pdwAttributes = 0; int nResult = oDesktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, folderName, ref pchEaten, out IntPtr pPIDL, ref pdwAttributes); if (nResult != ResultOK) { return null; } IntPtr pStrRet = Marshal.AllocCoTaskMem((MaxPath * 2) + 4); Marshal.WriteInt32(pStrRet, 0, 0); _ = this.oDesktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet); StringBuilder strFolder = new StringBuilder(MaxPath); _ = DllImports.NativeMethods.ShlwapiStrRetToBuf(pStrRet, pPIDL, strFolder, MaxPath); Marshal.FreeCoTaskMem(pStrRet); strParentFolder = strFolder.ToString(); // Get the IShellFolder for folder nResult = oDesktopFolder.BindToObject(pPIDL, IntPtr.Zero, ref iidIShellFolder, out IntPtr pUnknownParentFolder); // Free the PIDL first Marshal.FreeCoTaskMem(pPIDL); if (nResult != ResultOK) { return null; } oParentFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownParentFolder, typeof(IShellFolder)); } return oParentFolder; } [StructLayout(LayoutKind.Sequential)] private struct CWPSTRUCT { public IntPtr Lparam; public IntPtr Wparam; public int Message; public IntPtr Hwnd; } // Contains extended information about a shortcut menu command [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct CMINVOKECOMMANDINFOEX { public int CbSize; public CMIC FMask; public IntPtr Hwnd; public IntPtr LpVerb; [MarshalAs(UnmanagedType.LPStr)] public string LpParameters; [MarshalAs(UnmanagedType.LPStr)] public string LpDirectory; public SW NShow; public int DwHotKey; public IntPtr HIcon; [MarshalAs(UnmanagedType.LPStr)] public string LpTitle; public IntPtr LpVerbW; [MarshalAs(UnmanagedType.LPWStr)] public string LpParametersW; [MarshalAs(UnmanagedType.LPWStr)] public string LpDirectoryW; [MarshalAs(UnmanagedType.LPWStr)] public string LpTitleW; public POINT PtInvoke; } // Contains information about a menu item [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct MENUITEMINFO { private readonly int cbSize; private readonly MIIM fMask; private readonly MFT fType; private readonly MFS fState; private readonly uint wID; private readonly IntPtr hSubMenu; private readonly IntPtr hbmpChecked; private readonly IntPtr hbmpUnchecked; private readonly IntPtr dwItemData; [MarshalAs(UnmanagedType.LPTStr)] private readonly string dwTypeData; private readonly int cch; private readonly IntPtr hbmpItem; public MENUITEMINFO(string text) { cbSize = CbMenuItemInfo; dwTypeData = text; cch = text.Length; fMask = 0; fType = 0; fState = 0; wID = 0; hSubMenu = IntPtr.Zero; hbmpChecked = IntPtr.Zero; hbmpUnchecked = IntPtr.Zero; dwItemData = IntPtr.Zero; hbmpItem = IntPtr.Zero; } } // A generalized global memory handle used for data transfer operations by the // IAdviseSink, IDataObject, and IOleCache interfaces [StructLayout(LayoutKind.Sequential)] private struct STGMEDIUM { public TYMED Tymed; public IntPtr HBitmap; public IntPtr HMetaFilePict; public IntPtr HEnhMetaFile; public IntPtr HGlobal; public IntPtr LpszFileName; public IntPtr Pstm; public IntPtr Pstg; public IntPtr PUnkForRelease; } // Defines the x- and y-coordinates of a point [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct POINT { public int X; public int Y; public POINT(int x, int y) { X = x; Y = y; } } } }