Click here to Skip to main content
15,886,199 members
Articles / Programming Languages / C#

C# File Browser

Rate me:
Please Sign up or sign in to vote.
4.93/5 (173 votes)
21 Aug 200628 min read 2.4M   81.5K   587  
A file browser written in C#, very much like Windows Explorer.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Collections;
using System.Drawing;
using ShellDll;
using System.Threading;
using System.ComponentModel;
using System.IO;

namespace FileBrowser
{
    /// <summary>
    /// This class takes care of showing ContextMenu's for a BrowserTreeView
    /// </summary>
    internal class BrowserTVContextMenuWrapper : NativeWindow
    {
        #region Fields

        // The browser for which to provide the context menu's
        private Browser br;
        
        // If this bool is true the next time the context menu has to be shown will be cancelled
        private bool suspendContextMenu;

        // The interfaces for the needed context menu's
        private IContextMenu iContextMenu;
        private IContextMenu2 iContextMenu2;
        private IContextMenu3 iContextMenu3;

        private bool contextMenuVisible;

        // The cmd for a custom added menu item
        private enum CMD_CUSTOM
        {
            ExpandCollapse = (int)ShellAPI.CMD_LAST + 1
        }

        #endregion

        /// <summary>
        /// Registers the neccesairy events
        /// </summary>
        /// <param name="br">The browser for which to support the ContextMenu</param>
        public BrowserTVContextMenuWrapper(Browser br)
        {
            this.br = br;

            br.FolderView.MouseUp += new System.Windows.Forms.MouseEventHandler(FolderView_MouseUp);
            br.FolderView.AfterLabelEdit += new NodeLabelEditEventHandler(FolderView_AfterLabelEdit);
            br.FolderView.BeforeLabelEdit += new NodeLabelEditEventHandler(FolderView_BeforeLabelEdit);
            br.FolderView.KeyDown += new KeyEventHandler(FolderView_KeyDown);

            this.CreateHandle(new CreateParams());
        }

        #region Public

        public bool SuspendContextMenu
        {
            get { return suspendContextMenu; }
            set { suspendContextMenu = value; }
        }

        #endregion

        #region Override

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="m">the Message of the Browser's WndProc</param>
        /// <returns>true if the message has been handled, false otherwise</returns>
        protected override void WndProc(ref Message m)
        {
            #region IContextMenu

            if (iContextMenu != null &&
                m.Msg == (int)ShellAPI.WM.MENUSELECT &&
                ((int)ShellHelper.HiWord(m.WParam) & (int)ShellAPI.MFT.SEPARATOR) == 0 &&
                ((int)ShellHelper.HiWord(m.WParam) & (int)ShellAPI.MFT.POPUP) == 0)
            {
                string info = string.Empty;

                if (ShellHelper.LoWord(m.WParam) == (int)CMD_CUSTOM.ExpandCollapse)
                    info = "Expands or collapses the current selected item";
                else
                {
                    info = ContextMenuHelper.GetCommandString(
                        iContextMenu,
                        ShellHelper.LoWord(m.WParam) - ShellAPI.CMD_FIRST, 
                        false);
                }

                br.OnContextMenuMouseHover(new ContextMenuMouseHoverEventArgs(info.ToString()));
            }

            #endregion

            #region IContextMenu2

            if (iContextMenu2 != null &&
                (m.Msg == (int)ShellAPI.WM.INITMENUPOPUP ||
                 m.Msg == (int)ShellAPI.WM.MEASUREITEM ||
                 m.Msg == (int)ShellAPI.WM.DRAWITEM))
            {
                if (iContextMenu2.HandleMenuMsg(
                    (uint)m.Msg, m.WParam, m.LParam) == ShellAPI.S_OK)
                    return;
            }

            #endregion

            #region IContextMenu3

            if (iContextMenu3 != null &&
                m.Msg == (int)ShellAPI.WM.MENUCHAR)
            {
                if (iContextMenu3.HandleMenuMsg2(
                    (uint)m.Msg, m.WParam, m.LParam, IntPtr.Zero) == ShellAPI.S_OK)
                    return;
            }

            #endregion            

            base.WndProc(ref m);
        }

        #endregion

        #region Events

        void FolderView_KeyDown(object sender, KeyEventArgs e)
        {
            ContextMenuHelper.ProcessKeyCommands(br, sender, e);
        }

        void FolderView_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            ShellItem item = e.Node.Tag as ShellItem;

            if (!item.CanRename)
            {
                e.CancelEdit = true;
                System.Media.SystemSounds.Beep.Play();
            }
            if (item.IsDisk)
            {
                IntPtr editHandle = ShellAPI.SendMessage(br.FolderView.Handle, ShellAPI.WM.TVM_GETEDITCONTROL, 0, IntPtr.Zero);
                ShellAPI.SendMessage(editHandle, ShellAPI.WM.SETTEXT, 0, 
                    Marshal.StringToHGlobalAuto(item.Text.Substring(0, item.Text.LastIndexOf(' '))) );
            }
        }

        void FolderView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            ShellItem item = e.Node.Tag as ShellItem;

            IntPtr newPidl = IntPtr.Zero;
            if (e.Label != null && !(item.IsDisk && item.Text.Substring(0, item.Text.LastIndexOf(' ')) == e.Label) &&
                item.ParentItem.ShellFolder.SetNameOf(
                    br.Handle,
                    item.PIDLRel.Ptr,
                    e.Label,
                    ShellAPI.SHGNO.NORMAL,
                    out newPidl) == ShellAPI.S_OK)
            {
                item.Update(newPidl, ShellItemUpdateType.Renamed);
            }
            else
            {
                e.CancelEdit = true;
            }

            br.FolderView.LabelEdit = false;
        }

        /// <summary>
        /// When the mouse goes up on an node and suspendContextMenu is true, this method will show the
        /// ContextMenu for that node and after the user selects an item, it will execute that command.
        /// </summary>
        void FolderView_MouseUp(object sender, MouseEventArgs e)
        {
            if (suspendContextMenu || contextMenuVisible)
            {
                suspendContextMenu = false;
                return;
            }

            TreeViewHitTestInfo hitTest = br.FolderView.HitTest(e.Location);

            contextMenuVisible = true;
            if (e.Button == MouseButtons.Right &&
                (hitTest.Location == TreeViewHitTestLocations.Image ||
                hitTest.Location == TreeViewHitTestLocations.Label ||
                hitTest.Location == TreeViewHitTestLocations.StateImage))
            {
                #region Fields
                ShellItem item = (ShellItem)hitTest.Node.Tag;

                IntPtr contextMenu = IntPtr.Zero,
                    iContextMenuPtr = IntPtr.Zero,
                    iContextMenuPtr2 = IntPtr.Zero,
                    iContextMenuPtr3 = IntPtr.Zero;
                IShellFolder parentShellFolder =
                    (item.ParentItem != null) ? item.ParentItem.ShellFolder : item.ShellFolder;

                #endregion

                #region Show / Invoke
                try
                {
                    if (ContextMenuHelper.GetIContextMenu(parentShellFolder, new IntPtr[] { item.PIDLRel.Ptr },
                            out iContextMenuPtr, out iContextMenu))
                    {
                        contextMenu = ShellAPI.CreatePopupMenu();

                        iContextMenu.QueryContextMenu(
                            contextMenu,
                            0,
                            ShellAPI.CMD_FIRST,
                            ShellAPI.CMD_LAST,
                            ShellAPI.CMF.EXPLORE |
                            ShellAPI.CMF.CANRENAME |
                            ((Control.ModifierKeys & Keys.Shift) != 0 ? ShellAPI.CMF.EXTENDEDVERBS : 0));

                        string topInvoke = hitTest.Node.IsExpanded ? "Collapse" : "Expand";
                        ShellAPI.MFT extraFlag = (hitTest.Node.Nodes.Count > 0) ? 0 : ShellAPI.MFT.GRAYED;
                        ShellAPI.InsertMenu(contextMenu, 0,
                            ShellAPI.MFT.BYPOSITION | extraFlag,
                            (int)CMD_CUSTOM.ExpandCollapse, topInvoke);
                        ShellAPI.InsertMenu(contextMenu, 1,
                            ShellAPI.MFT.BYPOSITION | ShellAPI.MFT.SEPARATOR,
                            0, "-");

                        ShellAPI.SetMenuDefaultItem(
                            contextMenu,
                            0,
                            true);

                        Marshal.QueryInterface(iContextMenuPtr, ref ShellAPI.IID_IContextMenu2, out iContextMenuPtr2);
                        Marshal.QueryInterface(iContextMenuPtr, ref ShellAPI.IID_IContextMenu3, out iContextMenuPtr3);

                        try
                        {
                            iContextMenu2 =
                                (IContextMenu2)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr2, typeof(IContextMenu2));

                            iContextMenu3 =
                                (IContextMenu3)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr3, typeof(IContextMenu3));
                        }
                        catch (Exception) { }

                        Point ptInvoke = br.FolderView.PointToScreen(e.Location);
                        uint selected = ShellAPI.TrackPopupMenuEx(
                                            contextMenu,
                                            ShellAPI.TPM.RETURNCMD,
                                            ptInvoke.X,
                                            ptInvoke.Y,
                                            this.Handle,
                                            IntPtr.Zero);

                        br.OnContextMenuMouseHover(new ContextMenuMouseHoverEventArgs(string.Empty));

                        if (selected == (int)CMD_CUSTOM.ExpandCollapse)
                        {
                            if (hitTest.Node.IsExpanded)
                                hitTest.Node.Collapse(true);
                            else
                                hitTest.Node.Expand();
                        }
                        else if (selected >= ShellAPI.CMD_FIRST)
                        {
                            string command = ContextMenuHelper.GetCommandString(
                                iContextMenu, 
                                selected - ShellAPI.CMD_FIRST, 
                                true);

                            if (command == "rename")
                            {
                                br.FolderView.LabelEdit = true;
                                hitTest.Node.BeginEdit();
                            }
                            else
                            {
                                ContextMenuHelper.InvokeCommand(
                                    iContextMenu,
                                    selected - ShellAPI.CMD_FIRST,
                                    (item.ParentItem != null) ?
                                    ShellItem.GetRealPath(item.ParentItem) : ShellItem.GetRealPath(item),
                                    ptInvoke);
                            }
                        }
                    }
                }
                #endregion
                catch (Exception) { }
                #region Finally
                finally
                {
                    if (iContextMenu != null)
                    {
                        Marshal.ReleaseComObject(iContextMenu);
                        iContextMenu = null;
                    }

                    if (iContextMenu2 != null)
                    {
                        Marshal.ReleaseComObject(iContextMenu2);
                        iContextMenu2 = null;
                    }

                    if (iContextMenu3 != null)
                    {
                        Marshal.ReleaseComObject(iContextMenu3);
                        iContextMenu3 = null;
                    }

                    if (contextMenu != null)
                        ShellAPI.DestroyMenu(contextMenu);

                    if (iContextMenuPtr != IntPtr.Zero)
                        Marshal.Release(iContextMenuPtr);

                    if (iContextMenuPtr2 != IntPtr.Zero)
                        Marshal.Release(iContextMenuPtr2);

                    if (iContextMenuPtr3 != IntPtr.Zero)                    
                        Marshal.Release(iContextMenuPtr3);
                }
                #endregion
            }
            contextMenuVisible = false;
        }

        #endregion
    }

    /// <summary>
    /// This class takes care of showing ContextMenu's for a BrowserListView and 
    /// executing items when pressing enter or double clicking
    /// </summary>
    internal class BrowserLVContextMenuWrapper : NativeWindow
    {
        #region Fields

        // The browser for which to provide the context menu's
        private Browser br;

        private BrowserPluginWrapper pluginWrapper;
        private StreamStorageProvider provider;

        private BackgroundWorker viewPluginWorker;
        private System.Windows.Forms.Timer viewPluginTimer;
        private delegate void ViewPluginFileChange(IFileInfoProvider provider, ShellItem item);
        private delegate void ViewPluginFolderChange(IDirInfoProvider provider, ShellItem item);

        private delegate void ToolTipDelegate(ListViewItem listItem);
        private ToolTipDelegate toolTipDelegate;

        // If this bool is true the next time the context menu has to be shown will be cancelled
        private bool suspendContextMenu;

        // The interfaces for the needed context menu's
        private IContextMenu iContextMenu, newContextMenu;
        private IContextMenu2 iContextMenu2, newContextMenu2;
        private IContextMenu3 iContextMenu3, newContextMenu3;

        private IntPtr newSubmenuPtr;

        private bool contextMenuVisible;

        // The cmd for a custom added menu item
        private enum CMD_CUSTOM
        {
            Tiles = (int)ShellAPI.CMD_LAST + 1,
            Icons,
            List,
            Details,
            Properties,
            Paste,
            Paste_ShortCut,
            SpecialView
        }

        #endregion

        /// <summary>
        /// Registers the neccesairy events
        /// </summary>
        /// <param name="br">The browser for which to support the ContextMenu</param>
        public BrowserLVContextMenuWrapper(Browser br, BrowserPluginWrapper pluginWrapper)
        {
            this.br = br;
            this.pluginWrapper = pluginWrapper;

            provider = new StreamStorageProvider(FileAccess.ReadWrite);
            viewPluginWorker = new BackgroundWorker();
            viewPluginWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            viewPluginTimer = new System.Windows.Forms.Timer();
            viewPluginTimer.Tick += new EventHandler(viewPluginTimer_Tick);
            viewPluginTimer.Interval = 300;

            br.FileView.ShowItemToolTips = true;
            toolTipDelegate = new ToolTipDelegate(SetToolTip);

            br.SelectedFolderChanged += new SelectedFolderChangedEventHandler(br_SelectedFolderChanged);

            br.FileView.LabelEdit = true;
            br.FileView.MouseUp += new MouseEventHandler(FileView_MouseUp);
            br.FileView.ItemActivate += new EventHandler(FileView_ItemActivate);
            br.FileView.AfterLabelEdit += new LabelEditEventHandler(FileView_AfterLabelEdit);
            br.FileView.BeforeLabelEdit += new LabelEditEventHandler(FileView_BeforeLabelEdit);
            br.FileView.KeyDown += new KeyEventHandler(FileView_KeyDown);
            br.FileView.ItemSelectionChanged += new ListViewItemSelectionChangedEventHandler(FileView_ItemSelectionChanged);
            br.FileView.ItemMouseHover += new ListViewItemMouseHoverEventHandler(FileView_ItemMouseHover);

            this.CreateHandle(new CreateParams());
        }

        #region Public

        public bool SuspendContextMenu
        {
            get { return suspendContextMenu; }
            set { suspendContextMenu = value; }
        }

        #endregion

        #region Override

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="m">the Message of the Browser's WndProc</param>
        /// <returns>true if the message has been handled, false otherwise</returns>
        protected override void WndProc(ref Message m)
        {
            #region IContextMenu

            if (iContextMenu != null &&
                m.Msg == (int)ShellAPI.WM.MENUSELECT &&
                ((int)ShellHelper.HiWord(m.WParam) & (int)ShellAPI.MFT.SEPARATOR) == 0 &&
                ((int)ShellHelper.HiWord(m.WParam) & (int)ShellAPI.MFT.POPUP) == 0)
            {
                string info = string.Empty;
                info = ContextMenuHelper.GetCommandString(
                    iContextMenu,
                    ShellHelper.LoWord(m.WParam) - ShellAPI.CMD_FIRST, 
                    false);
                br.OnContextMenuMouseHover(new ContextMenuMouseHoverEventArgs(info.ToString()));
            }

            #endregion

            #region IContextMenu2

            if (iContextMenu2 != null &&
                (m.Msg == (int)ShellAPI.WM.INITMENUPOPUP ||
                 m.Msg == (int)ShellAPI.WM.MEASUREITEM ||
                 m.Msg == (int)ShellAPI.WM.DRAWITEM))
            {
                if (iContextMenu2.HandleMenuMsg(
                    (uint)m.Msg, m.WParam, m.LParam) == ShellAPI.S_OK)
                    return;
            }

            if (newContextMenu2 != null &&
                ((m.Msg == (int)ShellAPI.WM.INITMENUPOPUP && m.WParam == newSubmenuPtr) ||
                 m.Msg == (int)ShellAPI.WM.MEASUREITEM ||
                 m.Msg == (int)ShellAPI.WM.DRAWITEM))
            {
                if (newContextMenu2.HandleMenuMsg(
                    (uint)m.Msg, m.WParam, m.LParam) == ShellAPI.S_OK)
                    return;
            }

            #endregion

            #region IContextMenu3

            if (iContextMenu3 != null &&
                m.Msg == (int)ShellAPI.WM.MENUCHAR)
            {
                if (iContextMenu3.HandleMenuMsg2(
                    (uint)m.Msg, m.WParam, m.LParam, IntPtr.Zero) == ShellAPI.S_OK)
                    return;
            }

            if (newContextMenu3 != null &&
                m.Msg == (int)ShellAPI.WM.MENUCHAR)
            {
                if (newContextMenu3.HandleMenuMsg2(
                    (uint)m.Msg, m.WParam, m.LParam, IntPtr.Zero) == ShellAPI.S_OK)
                    return;
            }

            #endregion   

            base.WndProc(ref m);
        }

        #endregion

        #region Events

        void FileView_KeyDown(object sender, KeyEventArgs e)
        {
            ContextMenuHelper.ProcessKeyCommands(br, sender, e);
        }

        void FileView_BeforeLabelEdit(object sender, LabelEditEventArgs e)
        {
            ShellItem item = br.FileView.Items[e.Item].Tag as ShellItem;

            if (!item.CanRename)
            {
                e.CancelEdit = true;
                System.Media.SystemSounds.Beep.Play();
                return;
            }
            else if (item.IsDisk)
            {
                IntPtr editHandle = ShellAPI.SendMessage(br.FileView.Handle, ShellAPI.WM.LVM_GETEDITCONTROL, 0, IntPtr.Zero);
                ShellAPI.SendMessage(editHandle, ShellAPI.WM.SETTEXT, 0,
                    Marshal.StringToHGlobalAuto(item.Text.Substring(0, item.Text.LastIndexOf(' '))));
            }
        }

        void FileView_AfterLabelEdit(object sender, LabelEditEventArgs e)
        {
            ShellItem item = br.FileView.Items[e.Item].Tag as ShellItem;

            if (e.Label != null && br.FileView.SelectedOrder.Count == 1)
            {
                #region Rename One
                IntPtr newPidl = IntPtr.Zero;
                if (!(item.IsDisk && item.Text.Substring(0, item.Text.LastIndexOf(' ')) == e.Label) &&
                    item.ParentItem.ShellFolder.SetNameOf(
                        br.Handle,
                        item.PIDLRel.Ptr,
                        e.Label,
                        ShellAPI.SHGNO.NORMAL,
                        out newPidl) == ShellAPI.S_OK)
                {
                    item.Update(newPidl, ShellItemUpdateType.Renamed);
                }
                else
                    e.CancelEdit = true;
                #endregion
            }
            else if (e.Label != null)
            {
                #region Rename Batch
                string label = (!string.IsNullOrEmpty(e.Label) ? e.Label : br.FileView.Items[e.Item].Text);

                string startString = Path.GetFileNameWithoutExtension(label);
                string endString = Path.GetExtension(label);

                int countLength = br.FileView.SelectedOrder.Count.ToString().Length;

                IntPtr newPidl = IntPtr.Zero;
                ShellItem shellItem;
                ListViewItem listItem;

                StringBuilder renameString = new StringBuilder(startString.Length + endString.Length + countLength);
                renameString.Append(startString);
                renameString.Append('0', countLength);
                renameString.Append(endString);

                string counter;
                for (int i = 1; i <= br.FileView.SelectedOrder.Count; i++)
                {
                    listItem = br.FileView.SelectedOrder[i - 1] as ListViewItem;
                    shellItem = listItem.Tag as ShellItem;

                    if (shellItem.IsDisk)
                        continue;

                    counter = i.ToString();
                    renameString.Insert(startString.Length, "0", countLength - counter.Length);
                    renameString.Insert(startString.Length + countLength - counter.Length, counter);
                    renameString.Remove(startString.Length + countLength, countLength);

                    if (shellItem.ParentItem.ShellFolder.SetNameOf(
                            (i == 1 ? br.Handle : IntPtr.Zero),
                            shellItem.PIDLRel.Ptr,
                            renameString.ToString(),
                            ShellAPI.SHGNO.NORMAL,
                            out newPidl) == ShellAPI.S_OK)
                    {
                        shellItem.Update(newPidl, ShellItemUpdateType.Renamed);
                    }
                    else if (i == 1)
                        break;
                }

                e.CancelEdit = true;
                #endregion
            }
        }

        /// <summary>
        /// When items are activated (by pressing enter or double clicking) this method will invoke
        /// the default item of the items contextmenu.
        /// </summary>
        void FileView_ItemActivate(object sender, EventArgs e)
        {
            TreeNode parent = br.FolderView.SelectedNode;

            if (parent != null && br.FileView.SelectedOrder.Count > 0)
            {
                ListViewItem listViewItem = (ListViewItem)br.FileView.SelectedOrder[0];
                ShellItem shellItem = (ShellItem)listViewItem.Tag;
                int startIndex = 0;

                if ((Control.ModifierKeys & Keys.Alt) == 0)
                {
                    if (shellItem.IsFolder)
                    {
                        if (!parent.IsExpanded)
                            parent.Expand();

                        br.FolderView.SelectedNode = parent.Nodes[listViewItem.Text];
                        startIndex = 1;
                    }

                    if (br.FileView.SelectedOrder.Count - startIndex > 0)
                    {
                        #region Fields

                        IntPtr[] pidls = new IntPtr[br.FileView.SelectedOrder.Count - startIndex];
                        for (int i = startIndex; i < pidls.Length; i++)
                        {
                            pidls[i - startIndex] = ((ShellItem)((ListViewItem)br.FileView.SelectedOrder[i]).Tag).PIDLRel.Ptr;
                        }

                        IntPtr icontextMenuPtr = IntPtr.Zero, context2Ptr = IntPtr.Zero, context3Ptr = IntPtr.Zero;
                        ContextMenu contextMenu = new ContextMenu();
                        ShellItem parentShellItem = (ShellItem)parent.Tag;
                        IShellFolder parentShellFolder = parentShellItem.ShellFolder;

                        #endregion

                        #region Show / Invoke

                        try
                        {
                            if (ContextMenuHelper.GetIContextMenu(parentShellFolder, pidls, out icontextMenuPtr, out iContextMenu))
                            {
                                iContextMenu.QueryContextMenu(
                                    contextMenu.Handle,
                                    0,
                                    ShellAPI.CMD_FIRST,
                                    ShellAPI.CMD_LAST,
                                    ShellAPI.CMF.DEFAULTONLY);

                                int defaultCommand = ShellAPI.GetMenuDefaultItem(contextMenu.Handle, false, 0);
                                if (defaultCommand >= ShellAPI.CMD_FIRST)
                                {
                                    ContextMenuHelper.InvokeCommand(
                                        iContextMenu,
                                        (uint)defaultCommand - ShellAPI.CMD_FIRST,
                                        ShellItem.GetRealPath(parentShellItem),
                                        Control.MousePosition);
                                }
                            }
                        }
                        #endregion
                        catch (Exception) { }
                        #region Finally
                        finally
                        {
                            if (iContextMenu != null)
                            {
                                Marshal.ReleaseComObject(iContextMenu);
                                iContextMenu = null;
                            }

                            if (contextMenu.Handle != null)
                                Marshal.FreeCoTaskMem(contextMenu.Handle);

                            Marshal.Release(icontextMenuPtr);
                        }
                        #endregion
                    }
                }
                else
                {
                    IntPtr[] pidls = new IntPtr[br.FileView.SelectedOrder.Count - startIndex];
                    for (int i = startIndex; i < pidls.Length; i++)
                    {
                        pidls[i - startIndex] = ((ShellItem)((ListViewItem)br.FileView.SelectedOrder[i]).Tag).PIDLRel.Ptr;
                    }

                    ContextMenuHelper.InvokeCommand(shellItem.ParentItem, pidls, "properties", Control.MousePosition);
                }
            }
        }

        /// <summary>
        /// When the mouse goes up on an item and suspendContextMenu is true, this method will show the
        /// ContextMenu for that item and after the user selects an item, it will execute that command.
        /// </summary>
        void FileView_MouseUp(object sender, MouseEventArgs e)
        {
            if (suspendContextMenu || contextMenuVisible)
            {
                suspendContextMenu = false;
                return;
            }

            ListViewHitTestInfo hitTest = br.FileView.HitTest(e.Location);
            Point ptInvoke = br.FileView.PointToScreen(e.Location);

            contextMenuVisible = true;
            if (e.Button == MouseButtons.Right &&
                hitTest.Item != null && 
                (br.FileView.View != View.Details || (hitTest.SubItem != null && hitTest.Item.Name == hitTest.SubItem.Name)) &&
                (hitTest.Location == ListViewHitTestLocations.Image ||
                hitTest.Location == ListViewHitTestLocations.Label ||
                hitTest.Location == ListViewHitTestLocations.StateImage))
            {
                CreateNormalMenu(ptInvoke, hitTest, e);
            }
            else if (e.Button == MouseButtons.Right)
            {
                CreateFolderMenu(ptInvoke, e);
            }
            contextMenuVisible = false;
        }

        private void CreateNormalMenu(Point ptInvoke, ListViewHitTestInfo hitTest, MouseEventArgs e)
        {
            hitTest.Item.Selected = true;

            #region Fields

            ShellItem item = (ShellItem)hitTest.Item.Tag;

            int offset = br.FileView.SelectedOrder.Contains(hitTest.Item) ? 0 : 1;
            IntPtr[] pidls = new IntPtr[br.FileView.SelectedOrder.Count + offset];

            if (offset == 1)
                pidls[0] = item.PIDLRel.Ptr;

            for (int i = offset; i < pidls.Length; i++)
            {
                pidls[i] = ((ShellItem)((ListViewItem)br.FileView.SelectedOrder[i - offset]).Tag).PIDLRel.Ptr;
            }

            
            IntPtr contextMenu = IntPtr.Zero,
                iContextMenuPtr = IntPtr.Zero,
                iContextMenuPtr2 = IntPtr.Zero,
                iContextMenuPtr3 = IntPtr.Zero;
            IShellFolder parentShellFolder =
                (item.ParentItem != null) ? item.ParentItem.ShellFolder : item.ShellFolder;

            #endregion

            #region Show / Invoke

            try
            {
                if (ContextMenuHelper.GetIContextMenu(parentShellFolder, pidls, out iContextMenuPtr, out iContextMenu))
                {
                    contextMenu = ShellAPI.CreatePopupMenu();
                    iContextMenu.QueryContextMenu(
                        contextMenu,
                        0,
                        ShellAPI.CMD_FIRST,
                        ShellAPI.CMD_LAST,
                        ShellAPI.CMF.EXPLORE |
                        ShellAPI.CMF.CANRENAME |
                        ((Control.ModifierKeys & Keys.Shift) != 0 ? ShellAPI.CMF.EXTENDEDVERBS : 0));

                    Marshal.QueryInterface(iContextMenuPtr, ref ShellAPI.IID_IContextMenu2, out iContextMenuPtr2);
                    Marshal.QueryInterface(iContextMenuPtr, ref ShellAPI.IID_IContextMenu3, out iContextMenuPtr3);

                    try
                    {
                        iContextMenu2 =
                            (IContextMenu2)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr2, typeof(IContextMenu2));

                        iContextMenu3 =
                            (IContextMenu3)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr3, typeof(IContextMenu3));
                    }
                    catch (Exception) { }

                    uint selected = ShellAPI.TrackPopupMenuEx(
                                        contextMenu,
                                        ShellAPI.TPM.RETURNCMD,
                                        ptInvoke.X,
                                        ptInvoke.Y,
                                        this.Handle,
                                        IntPtr.Zero);

                    br.OnContextMenuMouseHover(new ContextMenuMouseHoverEventArgs(string.Empty));

                    if (selected >= ShellAPI.CMD_FIRST)
                    {
                        string command = ContextMenuHelper.GetCommandString(iContextMenu, selected - ShellAPI.CMD_FIRST, true);

                        if (command == "Explore" && br.FolderView.SelectedNode != null)
                        {
                            if (!br.FolderView.SelectedNode.IsExpanded)
                                br.FolderView.SelectedNode.Expand();

                            br.FolderView.SelectedNode = br.FolderView.SelectedNode.Nodes[hitTest.Item.Text];
                        }
                        else if (command == "rename")
                        {
                            hitTest.Item.BeginEdit();
                        }
                        else
                        {
                            ContextMenuHelper.InvokeCommand(
                                iContextMenu,
                                selected - ShellAPI.CMD_FIRST,
                                ShellItem.GetRealPath(br.SelectedItem),
                                ptInvoke);
                        }
                    }
                }
            }
            #endregion
            catch (Exception) { }
            #region Finally
            finally
            {
                if (iContextMenu != null)
                {
                    Marshal.ReleaseComObject(iContextMenu);
                    iContextMenu = null;
                }

                if (iContextMenu2 != null)
                {
                    Marshal.ReleaseComObject(iContextMenu2);
                    iContextMenu2 = null;
                }

                if (iContextMenu3 != null)
                {
                    Marshal.ReleaseComObject(iContextMenu3);
                    iContextMenu3 = null;
                }

                if (contextMenu != null)
                    ShellAPI.DestroyMenu(contextMenu);

                if (iContextMenuPtr != IntPtr.Zero)
                    Marshal.Release(iContextMenuPtr);

                if (iContextMenuPtr2 != IntPtr.Zero)
                    Marshal.Release(iContextMenuPtr2);

                if (iContextMenuPtr3 != IntPtr.Zero)
                    Marshal.Release(iContextMenuPtr3);
            }
            #endregion
        }

        private void CreateFolderMenu(Point ptInvoke, MouseEventArgs e)
        {
            #region Fields

            ShellItem currentItem = br.SelectedItem;

            foreach (ListViewItem item in br.FileView.SelectedItems)
                item.Selected = false;

            IntPtr contextMenu = IntPtr.Zero, viewSubMenu = IntPtr.Zero;
            IntPtr newContextMenuPtr = IntPtr.Zero, newContextMenuPtr2 = IntPtr.Zero, newContextMenuPtr3 = IntPtr.Zero;
            newSubmenuPtr = IntPtr.Zero;

            #endregion

            try
            {
                #region Make Menu

                contextMenu = ShellAPI.CreatePopupMenu();
                viewSubMenu = ShellAPI.CreatePopupMenu();

                #region View Submenu

                ShellAPI.MENUITEMINFO itemInfo = new ShellAPI.MENUITEMINFO("View");
                itemInfo.cbSize = ShellAPI.cbMenuItemInfo;
                itemInfo.fMask = ShellAPI.MIIM.SUBMENU | ShellAPI.MIIM.STRING;
                itemInfo.hSubMenu = viewSubMenu;
                ShellAPI.InsertMenuItem(contextMenu, 0, true, ref itemInfo);

                ShellAPI.MFT rCheck = ShellAPI.MFT.RADIOCHECK | ShellAPI.MFT.CHECKED;
                ShellAPI.AppendMenu(viewSubMenu, 
                    (br.CurrentViewPlugin == null && br.FileView.View == View.Tile ? rCheck : 0), (uint)CMD_CUSTOM.Tiles, "Tiles");
                ShellAPI.AppendMenu(viewSubMenu,
                    (br.CurrentViewPlugin == null && br.FileView.View == View.LargeIcon ? rCheck : 0), (uint)CMD_CUSTOM.Icons, "Icons");
                ShellAPI.AppendMenu(viewSubMenu,
                    (br.CurrentViewPlugin == null && br.FileView.View == View.List ? rCheck : 0), (uint)CMD_CUSTOM.List, "List");
                ShellAPI.AppendMenu(viewSubMenu,
                    (br.CurrentViewPlugin == null && br.FileView.View == View.Details ? rCheck : 0), (uint)CMD_CUSTOM.Details, "Details");

                for (int i = 0; i < pluginWrapper.ViewPlugins.Count; i++)
                {
                    ShellAPI.AppendMenu(viewSubMenu,
                        (IViewPlugin.Equals(br.CurrentViewPlugin, pluginWrapper.ViewPlugins[i]) ? rCheck : 0), 
                            (uint)CMD_CUSTOM.SpecialView + (uint)i, ((IViewPlugin)pluginWrapper.ViewPlugins[i]).ViewName);
                }

                #endregion

                #region Paste Menu

                DragDropEffects effects = ShellHelper.CanDropClipboard(currentItem);
                bool canPaste = (effects & DragDropEffects.Copy) != 0 || (effects & DragDropEffects.Move) != 0;
                bool canPasteShortCut = (effects & DragDropEffects.Link) != 0;

                ShellAPI.AppendMenu(contextMenu, ShellAPI.MFT.SEPARATOR, 0, string.Empty);
                ShellAPI.AppendMenu(contextMenu, canPaste ? 0 : ShellAPI.MFT.GRAYED, (int)CMD_CUSTOM.Paste, "Paste");
                ShellAPI.AppendMenu(contextMenu, canPasteShortCut ? 0 : ShellAPI.MFT.GRAYED, (int)CMD_CUSTOM.Paste_ShortCut, "Paste Shortcut");

                #endregion

                #region New Submenu

                if (br.SelectedItem.IsFileSystem &&
                    ContextMenuHelper.GetNewContextMenu(br.SelectedItem, out newContextMenuPtr, out newContextMenu))
                {
                    ShellAPI.AppendMenu(contextMenu, ShellAPI.MFT.SEPARATOR, 0, string.Empty);
                    newContextMenu.QueryContextMenu(
                        contextMenu,
                        5,
                        ShellAPI.CMD_FIRST,
                        ShellAPI.CMD_LAST,
                        ShellAPI.CMF.NORMAL);

                    newSubmenuPtr = ShellAPI.GetSubMenu(contextMenu, 5);

                    Marshal.QueryInterface(newContextMenuPtr, ref ShellAPI.IID_IContextMenu2, out newContextMenuPtr2);
                    Marshal.QueryInterface(newContextMenuPtr, ref ShellAPI.IID_IContextMenu3, out newContextMenuPtr3);

                    try
                    {
                        newContextMenu2 =
                            (IContextMenu2)Marshal.GetTypedObjectForIUnknown(newContextMenuPtr2, typeof(IContextMenu2));

                        newContextMenu3 =
                            (IContextMenu3)Marshal.GetTypedObjectForIUnknown(newContextMenuPtr3, typeof(IContextMenu3));
                    }
                    catch (Exception) { }
                }

                #endregion

                #region Properties Menu

                if (!br.SelectedItem.Equals(br.ShellBrowser.DesktopItem))
                {
                    ShellAPI.AppendMenu(contextMenu, ShellAPI.MFT.SEPARATOR, 0, string.Empty);
                    ShellAPI.AppendMenu(contextMenu, 0, (int)CMD_CUSTOM.Properties, "Properties");
                }

                #endregion

                #endregion

                CMD_CUSTOM selected = (CMD_CUSTOM)ShellAPI.TrackPopupMenuEx(
                                    contextMenu,
                                    ShellAPI.TPM.RETURNCMD,
                                    ptInvoke.X,
                                    ptInvoke.Y,
                                    this.Handle,
                                    IntPtr.Zero);

                #region Invoke

                if ((int)selected >= ShellAPI.CMD_FIRST)
                {
                    switch (selected)
                    {
                        #region Normal Views

                        case CMD_CUSTOM.Tiles:
                            br.FileView.View = View.Tile;
                            br.ResetSpecialView();
                            provider.ReleaseStorage();
                            provider.ReleaseStream();
                            break;
                        case CMD_CUSTOM.Icons:
                            br.FileView.View = View.LargeIcon;
                            br.ResetSpecialView();
                            provider.ReleaseStorage();
                            provider.ReleaseStream();
                            break;
                        case CMD_CUSTOM.List:
                            br.FileView.View = View.List;
                            br.ResetSpecialView();
                            provider.ReleaseStorage();
                            provider.ReleaseStream();
                            break;
                        case CMD_CUSTOM.Details:
                            br.FileView.SuspendHeaderContextMenu = true;
                            br.FileView.View = View.Details;
                            br.ResetSpecialView();
                            provider.ReleaseStorage();
                            provider.ReleaseStream();
                            break;

                        #endregion

                        #region Folder Items

                        case CMD_CUSTOM.Properties:
                            ContextMenuHelper.InvokeCommand(
                                br.SelectedItem.ParentItem,
                                new IntPtr[] { br.SelectedItem.PIDLRel.Ptr },
                                "properties",
                                ptInvoke);
                            break;

                        case CMD_CUSTOM.Paste:
                            ContextMenuHelper.InvokeCommand(
                                br.SelectedItem.ParentItem,
                                new IntPtr[] { br.SelectedItem.PIDLRel.Ptr },
                                "paste",
                                ptInvoke);
                            break;

                        case CMD_CUSTOM.Paste_ShortCut:
                            ContextMenuHelper.InvokeCommand(
                                br.SelectedItem.ParentItem,
                                new IntPtr[] { br.SelectedItem.PIDLRel.Ptr },
                                "pastelink",
                                ptInvoke);
                            break;

                        #endregion

                        default:
                            #region New
                            if ((uint)selected <= ShellAPI.CMD_LAST)
                            {
                                lock (br.ShellBrowser)
                                {
                                    br.NewItemCreated = true;
                                }

                                ContextMenuHelper.InvokeCommand(
                                    newContextMenu,
                                    (uint)selected - ShellAPI.CMD_FIRST,
                                    ShellItem.GetRealPath(br.SelectedItem),
                                    ptInvoke);
                            }
                            #endregion
                            #region Special Views
                            else
                            {
                                int index = (int)selected - (int)CMD_CUSTOM.SpecialView;

                                if (br.FileView.Alignment != ListViewAlignment.Left)
                                    br.FileView.Alignment = ListViewAlignment.Left;
                                
                                br.FileView.View = View.LargeIcon;
                                br.CurrentViewPlugin = pluginWrapper.ViewPlugins[index] as IViewPlugin;
                                br.SpecialViewPanelVisible = true;
                                br.SpecialViewPanel.Controls.Add(br.CurrentViewPlugin.ViewControl);
                                br.CurrentViewPlugin.ViewControl.Dock = DockStyle.Fill;
                            }
                            #endregion
                            break;
                    }
                }

                #endregion
            }
            catch (Exception) { }
            #region Finally
            finally
            {
                if (newContextMenu != null)
                {
                    Marshal.ReleaseComObject(newContextMenu);
                    newContextMenu = null;
                }

                if (newContextMenu2 != null)
                {
                    Marshal.ReleaseComObject(newContextMenu2);
                    newContextMenu2 = null;
                }

                if (newContextMenu3 != null)
                {
                    Marshal.ReleaseComObject(newContextMenu3);
                    newContextMenu3 = null;
                }

                if (contextMenu != null)
                    ShellAPI.DestroyMenu(contextMenu);

                if (viewSubMenu != null)
                    ShellAPI.DestroyMenu(viewSubMenu);

                if (newContextMenuPtr != IntPtr.Zero)
                    Marshal.Release(newContextMenuPtr);

                if (newContextMenuPtr2 != IntPtr.Zero)
                    Marshal.Release(newContextMenuPtr2);

                if (newContextMenuPtr3 != IntPtr.Zero)
                    Marshal.Release(newContextMenuPtr3);
                
                newSubmenuPtr = IntPtr.Zero;
            }
            #endregion
        }

        #endregion

        #region IViewPlugin Methods

        private delegate void FileSelected(IFileInfoProvider provider, ShellItem item);
        private delegate void FolderSelected(IDirInfoProvider provider, ShellItem item);

        void viewPluginTimer_Tick(object sender, EventArgs e)
        {
            if (!viewPluginWorker.IsBusy)
            {
                viewPluginWorker.RunWorkerAsync();
                viewPluginTimer.Stop();
            }
        }

        void br_SelectedFolderChanged(object sender, SelectedFolderChangedEventArgs e)
        {
            if (br.CurrentViewPlugin != null)
                br.CurrentViewPlugin.Reset();
        }

        void FileView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            if (e.IsSelected && br.SelectionChange && br.CurrentViewPlugin != null)
            {
                viewPluginTimer.Stop();
                provider.ProviderItem = e.Item.Tag as ShellItem;
                viewPluginTimer.Start();
            }
        }

        void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            provider.ReleaseStorage();
            provider.ReleaseStream();

            try
            {
                if (provider.ProviderItem.CanRead)
                {
                    IAsyncResult asyncResult = null;

                    if (provider.ProviderItem.IsFolder)
                    {
                        FolderSelected selectedDelegate = new FolderSelected(br.CurrentViewPlugin.FolderSelected);
                        asyncResult = br.CurrentViewPlugin.ViewControl.BeginInvoke(selectedDelegate, provider, provider.ProviderItem);
                    }
                    else
                    {
                        FileSelected selectedDelegate = new FileSelected(br.CurrentViewPlugin.FileSelected);
                        asyncResult = br.CurrentViewPlugin.ViewControl.BeginInvoke(selectedDelegate, provider, provider.ProviderItem);
                    }

                    asyncResult.AsyncWaitHandle.WaitOne();
                }
                else
                    br.CurrentViewPlugin.Reset();
            }
            catch (Exception) { }
        }

        #endregion

        #region ToolTip

        void SetToolTip(ListViewItem listItem)
        {
            IntPtr queryInfoPtr;
            IQueryInfo queryInfo;

            if (ShellHelper.GetIQueryInfo((ShellItem)listItem.Tag, out queryInfoPtr, out queryInfo))
            {
                string info;
                queryInfo.GetInfoTip(ShellAPI.QITIPF.DEFAULT, out info);

                //if (string.IsNullOrEmpty(info))
                    //queryInfo.GetInfoTip(ShellAPI.QITIPF.USESLOWTIP, out info);

                Marshal.ReleaseComObject(queryInfo);
                Marshal.Release(queryInfoPtr);

                listItem.ToolTipText = info;
            }
        }

        void FileView_ItemMouseHover(object sender, ListViewItemMouseHoverEventArgs e)
        {
            br.BeginInvoke(toolTipDelegate, e.Item);
        }

        #endregion
    }

    #region ContextMenuHelper

    /// <summary>
    /// This class provides static methods which are being used to retrieve IContextMenu's for specific items
    /// and to invoke certain commands.
    /// </summary>
    internal static class ContextMenuHelper
    {
        #region GetCommandString

        public static string GetCommandString(IContextMenu iContextMenu, uint idcmd, bool executeString)
        {
            string command = GetCommandStringW(iContextMenu, idcmd, executeString);

            if (string.IsNullOrEmpty(command))
                command = GetCommandStringA(iContextMenu, idcmd, executeString);

            return command;
        }

        /// <summary>
        /// Retrieves the command string for a specific item from an iContextMenu (Ansi)
        /// </summary>
        /// <param name="iContextMenu">the IContextMenu to receive the string from</param>
        /// <param name="idcmd">the id of the specific item</param>
        /// <param name="executeString">indicating whether it should return an execute string or not</param>
        /// <returns>if executeString is true it will return the executeString for the item, 
        /// otherwise it will return the help info string</returns>
        public static string GetCommandStringA(IContextMenu iContextMenu, uint idcmd, bool executeString)
        {
            string info = string.Empty;
            byte[] bytes = new byte[256];
            int index;

            iContextMenu.GetCommandString(
                idcmd,
                (executeString ? ShellAPI.GCS.VERBA : ShellAPI.GCS.HELPTEXTA),
                0,
                bytes,
                ShellAPI.MAX_PATH);

            index = 0;
            while (index < bytes.Length && bytes[index] != 0)
            { index++; }

            if (index < bytes.Length)
                info = Encoding.Default.GetString(bytes, 0, index);

            return info;
        }

        /// <summary>
        /// Retrieves the command string for a specific item from an iContextMenu (Unicode)
        /// </summary>
        /// <param name="iContextMenu">the IContextMenu to receive the string from</param>
        /// <param name="idcmd">the id of the specific item</param>
        /// <param name="executeString">indicating whether it should return an execute string or not</param>
        /// <returns>if executeString is true it will return the executeString for the item, 
        /// otherwise it will return the help info string</returns>
        public static string GetCommandStringW(IContextMenu iContextMenu, uint idcmd, bool executeString)
        {
            string info = string.Empty;
            byte[] bytes = new byte[256];
            int index;

            iContextMenu.GetCommandString(
                idcmd,
                (executeString ? ShellAPI.GCS.VERBW : ShellAPI.GCS.HELPTEXTW),
                0,
                bytes,
                ShellAPI.MAX_PATH);

            index = 0;
            while (index < bytes.Length - 1 && (bytes[index] != 0 || bytes[index + 1] != 0))
            { index += 2; }

            if (index < bytes.Length - 1)
                info = Encoding.Unicode.GetString(bytes, 0, index + 1);

            return info;
        }

        #endregion

        #region Invoke Commands

        /// <summary>
        /// Invokes a specific command from an IContextMenu
        /// </summary>
        /// <param name="iContextMenu">the IContextMenu containing the item</param>
        /// <param name="cmd">the index of the command to invoke</param>
        /// <param name="parentDir">the parent directory from where to invoke</param>
        /// <param name="ptInvoke">the point (in screen co�rdinates) from which to invoke</param>
        public static void InvokeCommand(IContextMenu iContextMenu, uint cmd, string parentDir, Point ptInvoke)
        {
            ShellAPI.CMINVOKECOMMANDINFOEX invoke = new ShellAPI.CMINVOKECOMMANDINFOEX();
            invoke.cbSize = ShellAPI.cbInvokeCommand;
            invoke.lpVerb = (IntPtr)cmd;
            invoke.lpDirectory = parentDir;
            invoke.lpVerbW = (IntPtr)cmd;
            invoke.lpDirectoryW = parentDir;
            invoke.fMask = ShellAPI.CMIC.UNICODE | ShellAPI.CMIC.PTINVOKE |
                ((Control.ModifierKeys & Keys.Control) != 0 ? ShellAPI.CMIC.CONTROL_DOWN : 0) |
                ((Control.ModifierKeys & Keys.Shift) != 0 ? ShellAPI.CMIC.SHIFT_DOWN : 0);
            invoke.ptInvoke = new ShellAPI.POINT(ptInvoke.X, ptInvoke.Y);
            invoke.nShow = ShellAPI.SW.SHOWNORMAL;

            iContextMenu.InvokeCommand(ref invoke);
        }

        /// <summary>
        /// Invokes a specific command from an IContextMenu
        /// </summary>
        /// <param name="iContextMenu">the IContextMenu containing the item</param>
        /// <param name="cmdA">the Ansi execute string to invoke</param>
        /// <param name="cmdW">the Unicode execute string to invoke</param>
        /// <param name="parentDir">the parent directory from where to invoke</param>
        /// <param name="ptInvoke">the point (in screen co�rdinates) from which to invoke</param>
        public static void InvokeCommand(IContextMenu iContextMenu, string cmd, string parentDir, Point ptInvoke)
        {
            ShellAPI.CMINVOKECOMMANDINFOEX invoke = new ShellAPI.CMINVOKECOMMANDINFOEX();
            invoke.cbSize = ShellAPI.cbInvokeCommand;
            invoke.lpVerb = Marshal.StringToHGlobalAnsi(cmd);
            invoke.lpDirectory = parentDir;
            invoke.lpVerbW = Marshal.StringToHGlobalUni(cmd);
            invoke.lpDirectoryW = parentDir;
            invoke.fMask = ShellAPI.CMIC.UNICODE | ShellAPI.CMIC.PTINVOKE |
                ((Control.ModifierKeys & Keys.Control) != 0 ? ShellAPI.CMIC.CONTROL_DOWN : 0) |
                ((Control.ModifierKeys & Keys.Shift) != 0 ? ShellAPI.CMIC.SHIFT_DOWN : 0);
            invoke.ptInvoke = new ShellAPI.POINT(ptInvoke.X, ptInvoke.Y);
            invoke.nShow = ShellAPI.SW.SHOWNORMAL;

            iContextMenu.InvokeCommand(ref invoke);
        }

        /// <summary>
        /// Invokes a specific command for a set of pidls
        /// </summary>
        /// <param name="parent">the parent ShellItem which contains the pidls</param>
        /// <param name="pidls">the pidls from the items for which to invoke</param>
        /// <param name="cmd">the execute string from the command to invoke</param>
        /// <param name="ptInvoke">the point (in screen co�rdinates) from which to invoke</param>
        public static void InvokeCommand(ShellItem parent, IntPtr[] pidls, string cmd, Point ptInvoke)
        {
            IntPtr icontextMenuPtr;
            IContextMenu iContextMenu;

            if (GetIContextMenu(parent.ShellFolder, pidls, out icontextMenuPtr, out iContextMenu))
            {
                try
                {
                    InvokeCommand(
                        iContextMenu,
                        cmd,
                        ShellItem.GetRealPath(parent),
                        ptInvoke);
                }
                catch (Exception) { }
                finally
                {
                    if (iContextMenu != null)
                        Marshal.ReleaseComObject(iContextMenu);

                    if (icontextMenuPtr != IntPtr.Zero)
                        Marshal.Release(icontextMenuPtr);
                }
            }
        }

        #endregion

        /// <summary>
        /// Retrieves the IContextMenu for specific items
        /// </summary>
        /// <param name="parent">the parent IShellFolder which contains the items</param>
        /// <param name="pidls">the pidls of the items for which to retrieve the IContextMenu</param>
        /// <param name="icontextMenuPtr">the pointer to the IContextMenu</param>
        /// <param name="iContextMenu">the IContextMenu for the items</param>
        /// <returns>true if the IContextMenu has been retrieved succesfully, false otherwise</returns>
        public static bool GetIContextMenu(
            IShellFolder parent,
            IntPtr[] pidls,
            out IntPtr iContextMenuPtr,
            out IContextMenu iContextMenu)
        {
            if (parent.GetUIObjectOf(
                        IntPtr.Zero,
                        (uint)pidls.Length,
                        pidls,
                        ref ShellAPI.IID_IContextMenu,
                        IntPtr.Zero,
                        out iContextMenuPtr) == ShellAPI.S_OK)
            {
                iContextMenu =
                    (IContextMenu)Marshal.GetTypedObjectForIUnknown(
                        iContextMenuPtr, typeof(IContextMenu));

                return true;
            }
            else
            {
                iContextMenuPtr = IntPtr.Zero;
                iContextMenu = null;

                return false;
            }
        }

        public static bool GetNewContextMenu(ShellItem item, out IntPtr iContextMenuPtr, out IContextMenu iContextMenu)
        {
            if (ShellAPI.CoCreateInstance(
                    ref ShellAPI.CLSID_NewMenu,
                    IntPtr.Zero,
                    ShellAPI.CLSCTX.INPROC_SERVER,
                    ref ShellAPI.IID_IContextMenu,
                    out iContextMenuPtr) == ShellAPI.S_OK)
            {
                iContextMenu = Marshal.GetTypedObjectForIUnknown(iContextMenuPtr, typeof(IContextMenu)) as IContextMenu;
                
                IntPtr iShellExtInitPtr;
                if (Marshal.QueryInterface(
                    iContextMenuPtr,
                    ref ShellAPI.IID_IShellExtInit,
                    out iShellExtInitPtr) == ShellAPI.S_OK)
                {
                    IShellExtInit iShellExtInit = Marshal.GetTypedObjectForIUnknown(
                        iShellExtInitPtr, typeof(IShellExtInit)) as IShellExtInit;

                    PIDL pidlFull = item.PIDLFull;
                    iShellExtInit.Initialize(pidlFull.Ptr, IntPtr.Zero, 0);

                    Marshal.ReleaseComObject(iShellExtInit);
                    Marshal.Release(iShellExtInitPtr);
                    pidlFull.Free();
                    
                    return true;
                }
                else
                {
                    if (iContextMenu != null)
                    {
                        Marshal.ReleaseComObject(iContextMenu);
                        iContextMenu = null;
                    }

                    if (iContextMenuPtr != IntPtr.Zero)
                    {
                        Marshal.Release(iContextMenuPtr);
                        iContextMenuPtr = IntPtr.Zero;
                    }

                    return false;
                }
            }
            else
            {
                iContextMenuPtr = IntPtr.Zero;
                iContextMenu = null;
                return false;
            }
        }

        /// <summary>
        /// When keys are pressed, this method will check for known key combinations. For example copy and past with
        /// Ctrl + C and Ctrl + V.
        /// </summary>
        public static void ProcessKeyCommands(Browser br, object sender, KeyEventArgs e)
        {
            if (e.Control && !e.Shift && !e.Alt)
            {
                switch (e.KeyCode)
                {
                    case Keys.C:
                    case Keys.Insert:
                    case Keys.V:
                    case Keys.X:
                        #region Copy/Paste/Cut
                        {
                            Cursor.Current = Cursors.WaitCursor;
                            IntPtr[] pidls;
                            ShellItem parent;
                            if (sender.Equals(br.FileView) && e.KeyCode != Keys.V)
                            {
                                pidls = new IntPtr[br.FileView.SelectedItems.Count];
                                for (int i = 0; i < pidls.Length; i++)
                                {
                                    pidls[i] = ((ShellItem)br.FileView.SelectedItems[i].Tag).PIDLRel.Ptr;
                                }
                                parent = br.SelectedItem;
                            }
                            else
                            {
                                pidls = new IntPtr[1];
                                pidls[0] = br.SelectedItem.PIDLRel.Ptr;
                                parent = (br.SelectedItem.ParentItem != null ? br.SelectedItem.ParentItem : br.SelectedItem);
                            }

                            if (pidls.Length > 0)
                            {
                                string cmd;
                                if (e.KeyCode == Keys.C || e.KeyCode == Keys.Insert)
                                    cmd = "copy";
                                else if (e.KeyCode == Keys.V)
                                    cmd = "paste";
                                else
                                    cmd = "cut";

                                ContextMenuHelper.InvokeCommand(parent, pidls, cmd, new Point(0, 0));
                                Cursor.Current = Cursors.Default;
                            }
                            e.Handled = true;
                            e.SuppressKeyPress = true;
                        }
                        #endregion
                        break;

                    case Keys.A:
                        #region Select All
                        {
                            foreach (ListViewItem item in br.FileView.Items)
                                item.Selected = true;

                            br.FileView.Focus();
                        }
                        e.Handled = true;
                        e.SuppressKeyPress = true;
                        #endregion
                        break;

                    case Keys.N:
                        #region Create New Folder
                        if (!br.CreateNewFolder())
                            System.Media.SystemSounds.Beep.Play();
                        
                        e.Handled = true;
                        e.SuppressKeyPress = true;
                        #endregion
                        break;

                    case Keys.Z:
                        break;

                    case Keys.Y:
                        break;
                }
            }
            else
            {
                switch (e.KeyCode)
                {
                    case Keys.Insert:
                        #region Paste
                        if (e.Shift && !e.Control && !e.Alt)
                        {
                            IntPtr[] pidls = new IntPtr[1];
                            pidls[0] = br.SelectedItem.PIDLRel.Ptr;
                            ShellItem parent = (br.SelectedItem.ParentItem != null ? br.SelectedItem.ParentItem : br.SelectedItem);
                            ContextMenuHelper.InvokeCommand(parent, pidls, "paste", new Point(0, 0));
                        }
                        e.Handled = true;
                        e.SuppressKeyPress = true;
                        #endregion
                        break;

                    case Keys.Delete:
                        #region Delete
                        if (!e.Control && !e.Alt)
                        {
                            IntPtr[] pidls;
                            ShellItem parent;
                            if (sender.Equals(br.FileView))
                            {
                                pidls = new IntPtr[br.FileView.SelectedItems.Count];
                                for (int i = 0; i < pidls.Length; i++)
                                {
                                    pidls[i] = ((ShellItem)br.FileView.SelectedItems[i].Tag).PIDLRel.Ptr;
                                }
                                parent = br.SelectedItem;
                            }
                            else
                            {
                                pidls = new IntPtr[1];
                                pidls[0] = br.SelectedItem.PIDLRel.Ptr;
                                parent = (br.SelectedItem.ParentItem != null ? br.SelectedItem.ParentItem : br.SelectedItem);
                            }

                            if (pidls.Length > 0)
                                ContextMenuHelper.InvokeCommand(parent, pidls, "delete", new Point(0, 0));
                        }
                        e.Handled = true;
                        e.SuppressKeyPress = true;
                        #endregion
                        break;

                    case Keys.F4:
                        #region AddressBox
                        {
                            br.NavAddressBox.Focus();
                            br.NavAddressBox.DroppedDown = true;
                        }
                        e.Handled = true;
                        e.SuppressKeyPress = true;
                        #endregion
                        break;

                    case Keys.F2:
                        #region Rename
                        if (sender.Equals(br.FolderView))
                        {
                            if (br.FolderView.SelectedNode != null)
                            {
                                br.FolderView.LabelEdit = true;
                                br.FolderView.SelectedNode.BeginEdit();
                            }
                        }
                        else if (sender.Equals(br.FileView))
                        {
                            if (br.FileView.SelectedOrder.Count > 0)
                            {
                                ArrayList temp = new ArrayList();
                                foreach (object obj in br.FileView.SelectedOrder)
                                    temp.Add(obj);

                                ListViewItem item = br.FileView.SelectedOrder[0] as ListViewItem;
                                item.BeginEdit();

                                for (int i = temp.Count - 1; i >= 0; i--)
                                {
                                    item = temp[i] as ListViewItem;
                                    item.Selected = false;
                                    item.Selected = true;
                                }
                            }
                        }
                        #endregion
                        break;

                    case Keys.Back:
                        #region Up
                        {
                            if (br.FolderView.SelectedNode != null && br.FolderView.SelectedNode.Parent != null)
                                br.FolderView.SelectedNode = br.FolderView.SelectedNode.Parent;
                        }
                        e.Handled = true;
                        e.SuppressKeyPress = true;
                        #endregion
                        break;
                }
            }
        }
    }

    #endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Netherlands Netherlands
I'm a student in Amsterdam (The Netherlands). I study Artificial Intelligence at the University of Amsterdam and I'm very fond of programming.

I discovered .Net programming a few years ago and immediately liked the Visual Studio environment. Since then I experimented a lot with .Net.

Comments and Discussions