效果如图所示
文件:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Security.Permissions;
namespace Start10
{
/// <summary>
/// "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
/// </summary>
/// <example>
/// ShellContextMenu scm = new ShellContextMenu();
/// FileInfo[] files = new FileInfo[1];
/// files[0] = new FileInfo(@"c:\windows\notepad.exe");
/// scm.ShowContextMenu(this.Handle, files, Cursor.Position);
/// </example>
public class ShellContextMenu : NativeWindow
{
#region Constructor
/// <summary>Default constructor</summary>
public ShellContextMenu()
{
this.CreateHandle(new CreateParams());
}
#endregion
#region Destructor
/// <summary>Ensure all resources get released</summary>
~ShellContextMenu()
{
ReleaseAll();
}
#endregion
#region GetContextMenuInterfaces()
/// <summary>Gets the interfaces to the context menu</summary>
/// <param name="oParentFolder">Parent folder</param>
/// <param name="arrPIDLs">PIDLs</param>
/// <returns>true if it got the interfaces, otherwise false</returns>
private bool GetContextMenuInterfaces(IShellFolder oParentFolder, IntPtr[] arrPIDLs, out IntPtr ctxMenuPtr)
{
int nResult = oParentFolder.GetUIObjectOf(
IntPtr.Zero,
(uint)arrPIDLs.Length,
arrPIDLs,
ref IID_IContextMenu,
IntPtr.Zero,
out ctxMenuPtr);
if (S_OK == nResult)
{
_oContextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(ctxMenuPtr, typeof(IContextMenu));
return true;
}
else
{
ctxMenuPtr = IntPtr.Zero;
_oContextMenu = null;
return false;
}
}
#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 (_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)
//{
// string info = string.Empty;
// if (ShellHelper.LoWord(m.WParam) == (int)CMD_CUSTOM.ExpandCollapse)
// info = "Expands or collapses the current selected item";
// else
// {
// info = "";
// }
//}
#endregion
#region IContextMenu2
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) == S_OK)
return;
}
#endregion
#region IContextMenu3
if (_oContextMenu3 != null &&
m.Msg == (int)WM.MENUCHAR)
{
if (_oContextMenu3.HandleMenuMsg2(
(uint)m.Msg, m.WParam, m.LParam, IntPtr.Zero) == S_OK)
return;
}
#endregion
base.WndProc(ref m);
}
#endregion
#region InvokeCommand
private void InvokeCommand(IContextMenu oContextMenu, uint nCmd, string strFolder, Point pointInvoke)
{
CMINVOKECOMMANDINFOEX invoke = new CMINVOKECOMMANDINFOEX();
invoke.cbSize = cbInvokeCommand;
invoke.lpVerb = (IntPtr)(nCmd - CMD_FIRST);
invoke.lpDirectory = strFolder;
invoke.lpVerbW = (IntPtr)(nCmd - CMD_FIRST);
invoke.lpDirectoryW = strFolder;
invoke.fMask = CMIC.UNICODE | CMIC.PTINVOKE |
((Control.ModifierKeys & Keys.Control) != 0 ? CMIC.CONTROL_DOWN : 0) |
((Control.ModifierKeys & Keys.Shift) != 0 ? CMIC.SHIFT_DOWN : 0);
invoke.ptInvoke = new POINT(pointInvoke.X, pointInvoke.Y);
invoke.nShow = SW.SHOWNORMAL;
oContextMenu.InvokeCommand(ref invoke);
}
#endregion
#region ReleaseAll()
/// <summary>
/// Release all allocated interfaces, PIDLs
/// </summary>
private void ReleaseAll()
{
if (null != _oContextMenu)
{
Marshal.ReleaseComObject(_oContextMenu);
_oContextMenu = null;
}
if (null != _oContextMenu2)
{
Marshal.ReleaseComObject(_oContextMenu2);
_oContextMenu2 = null;
}
if (null != _oContextMenu3)
{
Marshal.ReleaseComObject(_oContextMenu3);
_oContextMenu3 = null;
}
if (null != _oDesktopFolder)
{
Marshal.ReleaseComObject(_oDesktopFolder);
_oDesktopFolder = null;
}
if (null != _oParentFolder)
{
Marshal.ReleaseComObject(_oParentFolder);
_oParentFolder = null;
}
if (null != _arrPIDLs)
{
FreePIDLs(_arrPIDLs);
_arrPIDLs = null;
}
}
#endregion
#region GetDesktopFolder()
/// <summary>
/// Gets the desktop folder
/// </summary>
/// <returns>IShellFolder for desktop folder</returns>
private IShellFolder GetDesktopFolder()
{
IntPtr pUnkownDesktopFolder = IntPtr.Zero;
if (null == _oDesktopFolder)
{
// Get desktop IShellFolder
int nResult = SHGetDesktopFolder(out pUnkownDesktopFolder);
if (S_OK != nResult)
{
throw new ShellContextMenuException("Failed to get the desktop shell folder");
}
_oDesktopFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnkownDesktopFolder, typeof(IShellFolder));
}
return _oDesktopFolder;
}
#endregion
#region GetParentFolder()
/// <summary>
/// Gets the parent folder
/// </summary>
/// <param name="folderName">Folder path</param>
/// <returns>IShellFolder for the folder (relative from the desktop)</returns>
private IShellFolder GetParentFolder(string folderName)
{
if (null == _oParentFolder)
{
IShellFolder oDesktopFolder = GetDesktopFolder();
if (null == oDesktopFolder)
{
return null;
}
// Get the PIDL for the folder file is in
IntPtr pPIDL = IntPtr.Zero;
uint pchEaten = 0;
SFGAO pdwAttributes = 0;
int nResult = oDesktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, folderName, ref pchEaten, out pPIDL, ref pdwAttributes);
if (S_OK != nResult)
{
return null;
}
IntPtr pStrRet = Marshal.AllocCoTaskMem(MAX_PATH * 2 + 4);
Marshal.WriteInt32(pStrRet, 0, 0);
nResult = _oDesktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet);
StringBuilder strFolder = new StringBuilder(MAX_PATH);
StrRetToBuf(pStrRet, pPIDL, strFolder, MAX_PATH);
Marshal.FreeCoTaskMem(pStrRet);
pStrRet = IntPtr.Zero;
_strParentFolder = strFolder.ToString();
// Get the IShellFolder for folder
IntPtr pUnknownParentFolder = IntPtr.Zero;
nResult = oDesktopFolder.BindToObject(pPIDL, IntPtr.Zero, ref IID_IShellFolder, out pUnknownParentFolder);
// Free the PIDL first
Marshal.FreeCoTaskMem(pPIDL);
if (S_OK != nResult)
{
return null;
}
_oParentFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownParentFolder, typeof(IShellFolder));
}
return _oParentFolder;
}
#endregion
#region GetPIDLs()
/// <summary>
/// Get the PIDLs
/// </summary>
/// <param name="arrFI">Array of FileInfo</param>
/// <returns>Array of PIDLs</returns>
protected IntPtr[] GetPIDLs(FileInfo[] arrFI)
{
if (null == arrFI || 0 == arrFI.Length)
{
return null;
}
IShellFolder oParentFolder = GetParentFolder(arrFI[0].DirectoryName);
if (null == oParentFolder)
{
return null;
}
IntPtr[] arrPIDLs = new Int