Skip to main content
Email Password   helpLost your password?
Sample Image - maximum width is 600 pixels

Introduction

I seem to spend a fair bit of time managing files on my PC and don't find Windows Explorer the easiest tool to use. The best file manager I have ever used came with a DOS program called Brooklyn Bridge, which sold me completely on the concept of dual panes. The files I want to copy/move are on the left, the place I want to move them to is on the right. Mark the files, press the right keys (no menus in those days) and job done. Unfortunately it is not Y2K compliant and I haven't been able to find an alternative that I can get on with.

I also like to keep notes about files, so I can remind myself what they are, and I also want the ability to search those notes for a file I “know” that I have somewhere. Although this is possible in Explorer, I prefer those notes to be in my database so I can back it up and re-use it after a re-install. Having tried several file managers, none seemed to do what I wanted so I took the plunge and wrote my own.

This project is the result, it is not the first attempt, it's probably not the one hundredth attempt, but it's the one that works and doesn't (seem to) leak memory.

NET and Files/Folders

There are several classes in .NET relating to file/folder handling, and the DriveInfo class has been added in version 2. However, there is still no .NET way to find the correct icon for any of these, or to find the proper mask for the icon – so that shares and system folders show up properly. There is also no .NET way to show the Explorer Context Menu. What I have done is to use the .NET classes as far as possible and fallen back on the API where I couldn't find a .NET solution.

Application Structure

At the top level, there is a Windows Form - JFileManager. This has the main menu and a Splitter Control. Within the Splitter Control sit two instances of a User Control called cFiler, the one on the left is the source and the one on the right the destination.

In turn, the cFiler control contains further controls - a cFilerTS, a  cFilerTV and a cFilerLV. Each of these controls is self contained so the cFiler brings them all together and communicates between them. 

Speed of Application

I was anxious to ensure the program ran at a reasonable speed.

The first obvious bottleneck was getting the drive information which is used three times in each of the cFiler controls, making six times for the two controls. What I did was to set up all the information that was needed in a class called cJDriveInfoCollection which contains a HashTable in turn containing the CJDriveInfo. This is set up the first time it is called – and can be refreshed when needed. The class is static and I found it easier to use a HashTable than a Collections class.

The second bottleneck was setting the cSysTreeView to a new path. Since information is only added to a node when it is expanded, keeping a HashTable linking paths to nodes didn't seem practical. In the end, I decided to keep a HashTable of drives, so that the start node for any path could easily be found, and then work my way down the cSysTreeView finding the next node, expanding it (to get the folders added) and then continuing down. I ran some tests using the deepest nested folder I had but despite as much optimization as I was capable of, it takes just over two seconds to get to this node from the root node. I have left the test in (although the menu option is disabled) - if you want to try some tweaks to speed this up, I would be delighted to hear them!

When you close the TreeView pane, it is no longer kept up to date which speeds things up when you use it in that mode. It is updated when the TreeView pane is opened again.

Acknowledgements

Programming is a hobby for me. I have had a lot of help from various people over the years, generously and freely given, for which I am grateful.

I have acknowledged specific contributions in the source code.

Using the Code

I have tried to document the code as completely as possible, as it stands this is a complete application but there may be useful bits that can be pulled out and used in other projects.

Class Example

This is the cJDriveInfo class:

using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace JFileManager
{
	/// <summary>
	/// Provides additional information about drives
	/// </summary>
	public class cJDriveInfo
	{
	public string DisplayName = "";
	public string Path = "";
	public ObjectType ObjectType = ObjectType.DRIVE;
	public DriveType DriveType = DriveType.Unknown;
	public string TypeName = "";
	public int IconIndex = 0;
	public int SelectedIconIndex = 0;
	public long TotalSize = 0L;
	public long AvailableFreeSpace = 0L;
	public Image Image = null;
	public bool IsReady = false;
	public int Mask;
	public int StateMask;
	public int State;
	public bool HasChildren = false;
	public string VolumeLabel = "";
	public int Index;

	public cJDriveInfo(string strPath)
	: this(new DriveInfo(strPath))
	{
	}

	public cJDriveInfo(DriveInfo drivInfo)
	{
	this.Path = drivInfo.RootDirectory.ToString();
	this.DriveType = drivInfo.DriveType;
	this.IsReady = drivInfo.IsReady;

	this.GetSHFileInfo();

	// We won't get this info if drive is not ready
	try
	{
	this.TotalSize = drivInfo.TotalSize;
	this.AvailableFreeSpace = drivInfo.AvailableFreeSpace;
	// Display Name
	this.VolumeLabel = drivInfo.VolumeLabel;
	string strDrive = drivInfo.RootDirectory.ToString().Replace("\\", "");
	this.DisplayName = this.VolumeLabel + " (" + strDrive + ")";
	}
	catch
	{
	this.TotalSize = 0L;
	this.AvailableFreeSpace = 0L;
	}

	// Image Masks
	cCommon.GetLVImageMask(this.Path, out this.Mask, out this.StateMask, 
                               out this.State);

	// Sub-folders
	this.HasChildren = (cCommon.DirectorySubFolderCount(this.Path) > 0);
	}

	private void GetSHFileInfo()
	{
	SHFILEINFO shfi = new SHFILEINFO();

	UInt32 uFlags = (UInt32)(SHGFI.SHGFI_ICON | SHGFI.SHGFI_SMALLICON | 
                          SHGFI.SHGFI_TYPENAME | SHGFI.SHGFI_ADDOVERLAYS |
                          SHGFI.SHGFI_SYSICONINDEX | SHGFI.SHGFI_DISPLAYNAME);
	IntPtr ipTemp = cCommon.SHGetFileInfo(this.Path, 0, out shfi, 
                               Marshal.SizeOf(shfi), uFlags);

	// Check result
	if (ipTemp != IntPtr.Zero)
	{
	this.DisplayName = shfi.szDisplayName;
	this.TypeName = shfi.szTypeName;
	this.IconIndex = shfi.iIcon;
	this.Image = cCommon.ImageFromIcon(shfi.hIcon);
	}

	if (shfi.hIcon != IntPtr.Zero)
	cCommon.DestroyIcon(shfi.hIcon);

	// Selected Icon
	uFlags = (uint)(SHGFI.SHGFI_ICON | SHGFI.SHGFI_SMALLICON | 
                          SHGFI.SHGFI_ADDOVERLAYS | SHGFI.SHGFI_SYSICONINDEX |
                          SHGFI.SHGFI_OPENICON);
	ipTemp = cCommon.SHGetFileInfo(this.Path, 0, out shfi, 
                               Marshal.SizeOf(shfi), uFlags);

	if (ipTemp != IntPtr.Zero)
	{
	this.SelectedIconIndex = shfi.iIcon;
	}

	if (shfi.hIcon != IntPtr.Zero)
	cCommon.DestroyIcon(shfi.hIcon);
	}
	}
}

As you can see, its properties are obtained partly from the inbuilt DriveInfo class and partly from the API.

Memory Leaks

Using the API means moving out of the shelter of .NET’s memory management. I learnt (not quickly I have to confess) to read the help that comes with Visual Studio, especially when using the API!
Early versions of this program had massive memory leaks and so I read the help file for every API call I used and found the following relating to SHGetFileInfo:

If SHGetFileInfo returns an icon handle in the hIcon member of the SHFILEINFO structure pointed to by psfi, you are responsible for freeing it with DestroyIcon when you no longer need it.

Whoops!

My fault, I really should read the help more closely. I hope that I have picked up all the memory issues now.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralBeen There Pin
thund3rstruck
10:58 5 Dec '08  
QuestionLooks like good work, But could not find EXE (or application) to run Pin
Harshdeep Mehta (4700787)
6:58 4 Dec '08  
GeneralIt's a cool project, but Pin
Alexey Prosyankin
2:31 4 Dec '08  
GeneralRe: It's a cool project, but Pin
Jeff Gaines
2:52 4 Dec '08  
GeneralRe: It's a cool project, but Pin
Alexey Prosyankin
3:36 4 Dec '08  
GeneralRe: It's a cool project, but Pin
Alexey Prosyankin
4:37 4 Dec '08  
GeneralRe: It's a cool project, but Pin
Jeff Gaines
23:29 4 Dec '08  
GeneralRe: It's a cool project, but Pin
Alexey Prosyankin
1:17 5 Dec '08  
GeneralWindows Explorer Context Menu Pin
Doncp
17:25 27 Nov '08  
GeneralRe: Windows Explorer Context Menu Pin
Doncp
6:54 28 Nov '08  
GeneralRe: Windows Explorer Context Menu Pin
Jeff Gaines
1:09 29 Nov '08  
GeneralSuggestion [modified] Pin
faroo28
0:23 20 Aug '06  
GeneralRe: Suggestion Pin
Jeff Gaines
7:34 20 Aug '06  
GeneralGreat Works !! But a bit slow ... Pin
buster223
9:17 2 Aug '06  
GeneralRe: Great Works !! But a bit slow ... Pin
Jeff Gaines
10:10 2 Aug '06  
GeneralRe: Great Works !! But a bit slow ... Pin
faroo28
7:11 13 Jun '08  
GeneralPowerDesk Pin
Ted Ferenc
23:10 29 Jul '06  
GeneralDirectory Opus Pin
Leo Davidson
2:41 31 Jul '06  
GeneralRe: Directory Opus Pin
Ted Ferenc
4:38 31 Jul '06  
GeneralRe: Directory Opus Pin
simon.proctor
4:53 31 Jul '06  
GeneralRe: Directory Opus Pin
MAP Tiger
0:25 1 Aug '06  
GeneralRe: PowerDesk Pin
jeff@jgaines.co.uk
23:45 31 Jul '06  
GeneralRe: PowerDesk Pin
umeca74
5:44 24 Aug '06  
GeneralSuper! Pin
Doncp
12:04 29 Jul '06  
GeneralRe: Super! Pin
jeff@jgaines.co.uk
13:45 29 Jul '06  


Last Updated 3 Dec 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009