Click here to Skip to main content
15,895,746 members
Articles / Programming Languages / C#

Dual Pane File Manager

Rate me:
Please Sign up or sign in to vote.
4.64/5 (28 votes)
21 Dec 2010CPOL8 min read 140.8K   2.3K   119  
A dual pane file manager for Windows XP.
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);


	}

}

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired
United Kingdom United Kingdom
I have been a keen hobbyist programmer since getting my first computer - a Vic 20 (you had to be able to write programs then since few programs were available and all were expensive).
Retired and now living in Pewsey, Wiltshire, where I spend (far too much of) my time writing computer programs to keep my mind active.

Comments and Discussions