using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Win32API;
namespace JGFSControls
{
/// <summary>
/// Shows the Explorer Context Menu
/// </summary>
public class JContextMenu
{
public const int MAX_SHELL_ID = 0x7FFF;
public const int MIN_SHELL_ID = 1;
private JContextMenu()
{
//
// No instances, all static
//
}
/// <summary>
/// Takes an ArrayList of IntPtr and returns an IntPtr[]
/// </summary>
/// <param name="alPIDLS">ArrayList of PIDLs</param>
/// <returns>IntPtr[]</returns>
private static IntPtr[] ArrayListToIPArray(System.Collections.ArrayList alPIDLS)
{
int count = 0;
IntPtr[] resultArray = null;
if (alPIDLS != null && alPIDLS.Count > 0)
{
resultArray = new IntPtr[alPIDLS.Count];
count = 0;
foreach (IntPtr current in alPIDLS)
{
resultArray[count] = (IntPtr)alPIDLS[count];
count++;
}
}
return resultArray;
}
/// <summary>
/// Creates an IntPtr[] from a List of file/folder paths
/// </summary>
/// <param name="hwnd">Parent window handle</param>
/// <param name="listPaths">List of file/folder paths</param>
/// <param name="ParentFolder">IShellFolder to be returned</param>
/// <returns>An IntPtr[] of ItemIDLists for strFiles</returns>
private static IntPtr[] CreatePIDLList(IntPtr hwnd, List<string> listPaths, ref IShellFolder parentSF)
{
System.Collections.ArrayList alPIDLS = new System.Collections.ArrayList();
IntPtr ipChild = new IntPtr();
foreach (string current in listPaths)
{
ipChild = GetChildIDList(hwnd, current, ref parentSF);
alPIDLS.Add(ipChild);
}
return ArrayListToIPArray(alPIDLS);
}
/// <summary>
/// Gets an IntPtr to an ItemIDList for a file/folder and sets ParentFolder
/// </summary>
/// <param name="hwnd">Parent window handle</param>
/// <param name="objectPath">File/Folder path</param>
/// <param name="parentShellFolder">IShellFolder to be set</param>
/// <returns>An IntPtr to the objectPath ItemIDList</returns>
public static IntPtr GetChildIDList(IntPtr hwnd, string objectPath, ref IShellFolder parentShellFolder)
{
string parentName = "", childName = "";
// Don't use Path.GetDirectoryName(objectPath) as the objectPath could be anything
SplitObjectPath(objectPath, out parentName, out childName);
uint pchEaten = 0, dwAttributes = 0;
IntPtr ipParent, ipChild;
IntPtr[] ipArray = new IntPtr[0];
IShellFolder desktopSF = null;
Guid IID_IShellFolder = new Guid(JInterfaces.IShellFolder);
SHGetDesktopFolder(ref desktopSF);
// ParseDisplayName - parent
// Translates a file object's or folder's display name into an item identifier list
pchEaten = 1;
dwAttributes = 0;
ipParent = new IntPtr();
desktopSF.ParseDisplayName(hwnd, IntPtr.Zero, parentName, ref pchEaten, ref ipParent, ref dwAttributes);
// BindToObject
// Retrieves an IShellFolder object for a subfolder
parentShellFolder = null;
desktopSF.BindToObject(ipParent, IntPtr.Zero, ref IID_IShellFolder, ref parentShellFolder);
// ParseDisplayName - child
// Translates a file object's or folder's display name into an item identifier list
pchEaten = 1;
dwAttributes = 0;
ipChild = new IntPtr();
parentShellFolder.ParseDisplayName(hwnd, IntPtr.Zero, childName, ref pchEaten, ref ipChild, ref dwAttributes);
// Release the desktopSF
Marshal.ReleaseComObject(desktopSF);
return ipChild;
}
private static int MAKEINTRESOURCE(int res)
{
return 0x0000FFFF & res;
}
/// <summary>
/// Shows Explorer Context Menu for an object whose PIDL is known
/// </summary>
/// <param name="hwnd">Parent window handle</param>
/// <param name="ipPIDL">IntPtr to the object's ItemIDList</param>
/// <param name="pt">Position where context menu should be shown</param>
/// <param name="icm3">IContextMenu3 to be set for owner draw items on context menu</param>
/// <returns>true</returns>
public static bool ShowContextMenu(IntPtr hwnd, IntPtr ipPIDL, Point pt, ref IContextMenu3 icm3)
{
uint rgfReserved = 0;
IntPtr ipParent = new IntPtr();
IntPtr[] ipPIDLArray = new IntPtr[1];
IShellFolder desktopSF = null;
IShellFolder parentSF = null;
IUnknown iu;
Guid IID_IShellFolder = new Guid(JInterfaces.IShellFolder);
Guid IID_IContextMenu = new Guid(JInterfaces.IContextMenu);
SHGetDesktopFolder(ref desktopSF);
// BindToObject
// Retrieves an IShellFolder object for a subfolder
// If ipPIDL is the DeskTop then parentSF will be null
parentSF = null;
desktopSF.BindToObject(ipParent, IntPtr.Zero, ref IID_IShellFolder, ref parentSF);
ipPIDLArray[0] = ipPIDL;
iu = null;
object obTemp = new object();
if (parentSF != null)
parentSF.GetUIObjectOf(hwnd, 1, ipPIDLArray, ref IID_IContextMenu, ref rgfReserved, ref obTemp);
else
desktopSF.GetUIObjectOf(hwnd, 1, ipPIDLArray, ref IID_IContextMenu, ref rgfReserved, ref obTemp);
iu = (IUnknown)obTemp;
Marshal.ReleaseComObject(desktopSF);
return ShowMenu(hwnd, pt, iu, ref icm3);
}
/// <summary>
/// Shows Context Menu for one file/folder
/// </summary>
/// <param name="hwnd">Parent window handle</param>
/// <param name="filePath">Full path of object for which context menu is to be displayed</param>
/// <param name="pt">Position where context menu should be shown</param>
/// <param name="icm3">IContextMenu3 to be set for owner draw items on context menu</param>
/// <returns>true</returns>
public static bool ShowContextMenu(IntPtr hwnd, string filePath, Point pt, ref IContextMenu3 icm3)
{
uint rgfReserved = 0;
IntPtr ipChild = new IntPtr();
IShellFolder parentSF = null;
IUnknown iu;
Guid IID_IContextMenu = new Guid(JInterfaces.IContextMenu);
ipChild = GetChildIDList(hwnd, filePath, ref parentSF);
IntPtr[] ipChildArray = new IntPtr[1];
ipChildArray[0] = ipChild;
iu = null;
object obTemp = new object();
parentSF.GetUIObjectOf(hwnd, 1, ipChildArray, ref IID_IContextMenu, ref rgfReserved, ref obTemp);
iu = (IUnknown)obTemp;
Marshal.ReleaseComObject(parentSF);
return ShowMenu(hwnd, pt, iu, ref icm3);
}
/// <summary>
/// Shows Context Menu for String Array of files/folders
/// </summary>
/// <param name="hwnd">Parent window handle</param>
/// <param name="filePath">List of full paths of objects for which context menu is to be displayed</param>
/// <param name="pt">Position where context menu should be shown</param>
/// <param name="icm3">IContextMenu3 to be set for owner draw items on context menu</param>
/// <returns>true</returns>
public static bool ShowContextMenu(IntPtr hwnd, List<string> fileArray, Point pt, ref IContextMenu3 icm3)
{
if (fileArray == null || fileArray.Count < 1)
return false;
uint rgfReserved = 0;
IShellFolder parentFolder = null;
IUnknown iu = null;
Guid IID_IContextMenu = new Guid(JInterfaces.IContextMenu);
IntPtr[] ipList = CreatePIDLList(hwnd, fileArray, ref parentFolder);
if (ipList.GetUpperBound(0) < 0)
return false;
object obTemp = new object();
parentFolder.GetUIObjectOf(hwnd, (uint)ipList.GetUpperBound(0) + 1, ipList, ref IID_IContextMenu, ref rgfReserved, ref obTemp);
iu = (IUnknown)obTemp;
Marshal.ReleaseComObject(parentFolder);
return ShowMenu(hwnd, pt, iu, ref icm3);
}
/// <summary>
/// Shows the context menu after all the background work is complete
/// </summary>
/// <param name="hwnd">Parent window handle</param>
/// /// <param name="pt">Position where context menu should be shown</param>
/// <param name="iu">IUnknown created during background work</param>
/// <param name="icm3">IContextMenu3 to be set for owner draw items on context menu</param>
/// <returns>true</returns>
public static bool ShowMenu(IntPtr hwnd, Point pt, IUnknown iu, ref IContextMenu3 icm3)
{
// Casting to an IcontextMenu2 does this: pContextMenu->QueryInterface(IID_IContextMenu2, (VOID**)&pContextMenu2);
// Advice provided by Mattias Sj�gren [MVP] http://www.msjogren.net/dotnet/
// We need this to deal with 'Send To' and 'Open With' which have owner draw sub menus
CMINVOKECOMMANDINFO cmInvoke = new CMINVOKECOMMANDINFO();
icm3 = (IContextMenu3)iu;
RECT mRect;
//string menuText = new string('\0', 255);
//int result;
if (icm3 != null)
{
IntPtr hMenu = CreatePopupMenu();
icm3.QueryContextMenu(hMenu, 0, MIN_SHELL_ID, MAX_SHELL_ID, (ushort)(CMF.CMF_EXPLORE | CMF.CMF_CANRENAME));
int cmdID = TrackPopupMenu(hMenu, ((int)TPM.TPM_RETURNCMD | (int)TPM.TPM_LEFTALIGN), pt.X, pt.Y, 0, hwnd, out mRect);
if (cmdID != 0)
{
// This will identify the command selected
//itemInfo.fMask = (uint)MIIM.MIIM_STRING;
//itemInfo.fType = (uint)MFT.MFT_STRING;
//itemInfo.dwTypeData = menuText;
//itemInfo.cch = 255;
//itemInfo.cbSize = (uint)Marshal.SizeOf(itemInfo);
//result = JDll.GetMenuItemInfoA(hMenu, (uint)(cmdID - 0), false, ref itemInfo);
cmInvoke.cbSize = (uint)Marshal.SizeOf(cmInvoke);
cmInvoke.hwnd = hwnd;
cmInvoke.lpVerb = MAKEINTRESOURCE(cmdID - 1);
cmInvoke.lpParameters = "";
cmInvoke.lpDirectory = "";
cmInvoke.nShow = (int)SW.SW_SHOWNORMAL;
icm3.InvokeCommand(ref cmInvoke);
}
}
return true;
}
/// <summary>
/// Splits a file/folder fully qualified path into its parent directory and the object name
/// </summary>
/// <param name="strFQPath">Fully qualified path of object</param>
/// <param name="strParent">Parent folder</param>
/// <param name="strChild">Object name</param>
/// <returns>true on success, false otherwise</returns>
public static bool SplitObjectPath(string pathIN, out string parentPart, out string childPart)
{
parentPart = "";
childPart = "";
try
{
FileInfo fInfo = new FileInfo(pathIN);
if (fInfo.Exists)
{
childPart = fInfo.Name;
parentPart = fInfo.DirectoryName;
}
else
{
DirectoryInfo dInfo = new DirectoryInfo(pathIN);
if (dInfo.Exists)
{
try
{
childPart = dInfo.Name;
parentPart = dInfo.Parent.FullName;
}
catch
{
parentPart = "";
}
}
else
{
parentPart = "";
childPart = pathIN;
}
}
}
catch
{
return false;
}
return true;
}
// CreatePopupMenu
[DllImport("user32.dll", EntryPoint = "CreatePopupMenu", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr CreatePopupMenu();
// TrackPopupMenu
[DllImport("user32.dll", EntryPoint = "TrackPopupMenu", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern int TrackPopupMenu(IntPtr hMenu, int wFlags, int x, int y, int nReserved, IntPtr hwnd, out RECT lprc);
// SHGetDesktopFolder
[DllImport("shell32.dll", EntryPoint = "SHGetDesktopFolder", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern void SHGetDesktopFolder(ref IShellFolder sf);
}
}