Click here to Skip to main content
15,881,852 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.3K   2.2K   119  
A dual pane file manager for Windows XP.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using JGLibrary;

namespace JGFSControls
{
	internal class JDropFiles
	{
		private JDropFiles()
		{
			//
			// No instances, all static - this is standalone as it is a bit specialised
			//
		}

		/// <summary>
		/// Copies array of file names to Clipboard in FileDrop format, returns true on success, false on failure
		/// </summary>
		/// <param name="strFiles">String array of fully qualified file names to be copied to Clipboard. </param>
		public static DataObject CopyFilesToClipboardForDragDrop(List<string> listFilePaths, POINT pt)
		{
			//	Create and Copy File List to Clipboard in several formats

			if (listFilePaths == null || listFilePaths.Count < 1)
				return null;

			// Obtain a DROPFILES for the give list of files
			MemoryStream msDrop = FileListToDropFiles(listFilePaths, pt);

			//	Get a DataObject for Shortcuts
			MemoryStream msLinks = FileListToShellIDListArray(listFilePaths);

			//	Get a File List
			string szFileList = CreateFileList(listFilePaths);

			//	Now wrap them all in one DataObject
			DataObject doTemp = new DataObject();
			doTemp.SetData(DataFormats.FileDrop, true, msDrop);
			doTemp.SetData("Shell IDList Array", true, msLinks);
			doTemp.SetData(DataFormats.StringFormat, true, szFileList);

			// Copy the data object to the clipboard
			Clipboard.SetDataObject(doTemp, false);

			return doTemp;
		}

		private static string CreateFileList(List<string> listSourceObjects)
		{
			string szResult = "";
			for (int count = 0; count < listSourceObjects.Count; count++)
				szResult += listSourceObjects[count] + '\0';

			szResult += '\0';

			return szResult;
		}

		private static object[] FileListToIDListArray(List<string> listObjects)
		{
			IntPtr pidl;
			int cbPidl;

			// Allocate an array of objects (byte arrays) to store the 
			// pidl of each item in the file list
			object[] apidl = new object[listObjects.Count];
			for (int count = 0; count < listObjects.Count; count++)
			{
				// Obtain a pidl for the file
				pidl = ILCreateFromPath(listObjects[count]);

				cbPidl = (int)ILGetSize(pidl);

				// Convert the pidl to a byte array
				apidl[count] = (object)new byte[cbPidl];
				Marshal.Copy(pidl, (byte[])apidl[count], 0, cbPidl);

				// Free the pidl
				ILFree(pidl);
			}
			return apidl;
		}

		private static MemoryStream FileListToShellIDListArray(List<string> listObjects)
		{
			int cb = 0;
			int offset = 0;
			int count;
			uint pidlDesktop = 0;

			object[] apidl = null;
			MemoryStream stream = null;
			BinaryWriter streamWriter = null;

			// Convert the array of paths to an array of pidls
			apidl = FileListToIDListArray(listObjects);

			// Determine the amount of memory required for the CIDA
			// The 2 in the statement below is for the offset to the 
			// folder pidl and the count field in the CIDA structure
			cb = offset = Marshal.SizeOf(typeof(uint)) * (apidl.Length + 2);
			for (count = 0; count < apidl.Length; count++)
			{
				cb += ((byte[])apidl[count]).Length; ;
			}

			// Create a memory stream that we will write the CIDA into
			stream = new MemoryStream();

			// Wrap the memory stream with a BinaryWriter object
			streamWriter = new BinaryWriter(stream);

			// Write the cidl member of the CIDA structure
			streamWriter.Write(apidl.Length);

			// Write the array of offsets for each pidl. Calculate each offset as we go
			streamWriter.Write(offset);
			offset += Marshal.SizeOf(pidlDesktop);

			for (count = 0; count < apidl.Length; count++)
			{
				streamWriter.Write(offset);
				offset += ((byte[])apidl[count]).Length;
			}

			// Write the parent folder pidl
			streamWriter.Write(pidlDesktop);

			// Write the item pidls
			for (count = 0; count < apidl.Length; count++)
			{
				streamWriter.Write((byte[])apidl[count]);
			}

			// Return the memory stream that contains the CIDA
			return stream;
		}

		private static MemoryStream FileListToDropFiles(List<string> listFilePaths, POINT pt)
		{
			DataObject doTemp = new DataObject();
			int numFiles = listFilePaths.Count + 1;
			int count;

			//	Create DROPFILES structure
			DROPFILES df = new DROPFILES();
			df.pFiles = Marshal.SizeOf(df);
			df.pt = pt;
			df.fNC = false;
			df.fWide = 0;

			//	Calculate memory required
			int intMemRequired = Marshal.SizeOf(typeof(DROPFILES));
			IntPtr ipGlobal = Marshal.AllocHGlobal(intMemRequired);

			//	Copy the DROPFILES structure in to memory
			Marshal.StructureToPtr(df, ipGlobal, true);

			//	For Debugging - Read it back
			//DROPFILES dfTemp = new DROPFILES();
			//dfTemp = (DROPFILES)Marshal.PtrToStructure(ipGlobal, typeof(DROPFILES));

			// Create a MemoryStream that we will write the data into
			MemoryStream msTemp = new MemoryStream();
			BinaryWriter streamWriter = new BinaryWriter(msTemp);

			//	Write the DROPFILES structure to the MemoryStream - convert to a byte[]
			byte[] bTemp = new byte[Marshal.SizeOf(typeof(DROPFILES))];
			Marshal.Copy(ipGlobal, bTemp, 0, 20);
			streamWriter.Write(bTemp, 0, 20);

			// Now Write the FileList - don't copy over listFilePaths, it copies the preceeding '@' and breaks it!
			string strFileList = CreateFileList(listFilePaths);
			for (count = 0; count < strFileList.Length; count++)
				streamWriter.Write(strFileList[count]);

			//	Terminating double zero
			streamWriter.Write((byte)0);
			streamWriter.Write((byte)0);

			//			//Debugging - write it to a file to test
			//			string strTest = @"D:\Temp\TestData.bin";
			//			FileStream fs = new FileStream(strTest, FileMode.Create);
			//			// Create the writer for data.
			//			BinaryReader r = new BinaryReader(msTemp);
			//			msTemp.Position = 0;
			//			
			//			BinaryWriter w = new BinaryWriter(fs);
			//			// Write data to Test.data.
			//			try
			//			{
			//				for(int i = 0; i < msTemp.Length; i++)
			//				{
			//					w.Write(r.ReadByte());
			//				}
			//			}
			//			finally
			//			{
			//				w.Close();
			//			}

			//	Free Global Memory
			Marshal.FreeHGlobal(ipGlobal);

			return msTemp;
		}

		//	ILCreateFromPath
		[DllImport("shell32.dll", CharSet = CharSet.Auto)]
		public static extern IntPtr ILCreateFromPath(string pwszPath);

		//	ILFree
		[DllImport("shell32.dll", CharSet = CharSet.None)]
		public static extern void ILFree(IntPtr pidl);

		//	ILGetSize
		[DllImport("shell32.dll", CharSet = CharSet.None)]
		public static extern uint ILGetSize(IntPtr pidl);

		[StructLayout(LayoutKind.Sequential)]
		public struct DROPFILES
		{
			public int pFiles;
			public POINT pt;
			public bool fNC;
			public int fWide;
		}

	}		//	End cDropFiles

}

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