Wednesday, 18 November 2015

Adding a new item to context menu of Windows Explorer in C#.Net



Requirement: Whenever user right clicks on any folder in windows explorer, display a new option and if user selects that option, launch your application. The below steps will add a new option on right click of any folder:

You would need to add 2 sub keys in registry at the below location:

HKEY_CLASSES_ROOT\Folder\shell

Key 1: Folder\\shell\\MyProduct

Set the value of above key and the same will be shown in the context menu. You can also add a icon to the context menu item by adding a value to the key 1 and name it "Icon" as shown below:

Icon  "Path to your icon file"

Key 2: Folder\\shell\\MyProduct\\command

Set the value of above key and it will launch the application:

Eg. C:\Program Files (x86)\myProduct.exe "%1"

Here "%1" will give the path of the folder which is right clicked.



Programmatically this can be done as below:

private static void updateRegistryToAddContextMenu()
        {
            string MenuName = "Folder\\shell\\MyProduct";
            string Command = "Folder\\shell\MyProduct\\command";

            RegistryKey regmenu = null;
            RegistryKey regcmd = null;
            try
            {
                regmenu = Registry.ClassesRoot.CreateSubKey(MenuName);
                if (regmenu != null)
                    regmenu.SetValue("","New context menu option");
                regcmd = Registry.ClassesRoot.CreateSubKey(Command);
                if (regcmd != null)
                {
                    string exePath = Application.ExecutablePath;
                    regcmd.SetValue("", exePath + " " + "\"" + "%1" + "\"");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);

            }
            finally
            {
                if (regmenu != null)
                    regmenu.Close();
                if (regcmd != null)
                    regcmd.Close();
            }
        }

Thursday, 1 October 2015

Enumerate folder with “No Name” (blank space folder, AKA non-breaking space) in C#.Net:

A folder with no name can be created by following the below steps:
  1. Right-click on the folder and select Rename.
  2. Now press the Alt key and from the Numeric keypad, press 0160.
  3. Now press Enter or click anywhere on the desktop.


If you want to retrieve all the files and folders from any directory (in C#), you would do something like this:

string path = “Path to the directory”;
DirectoryInfo di = new DirectoryInfo(path);
foreach (DirectoryInfo dirInfo in di.GetDirectories())
{
       FileInfo[] fInfos = dirInfo.GetFiles();
       // You can easily iterate through the files here
}

But if you have any folder with a blank space as name, the above method would give you wrong result. You would need to make few changes as shown below:

string path = “Path to the directory” + “\\”;
DirectoryInfo di = new DirectoryInfo(path);
foreach (DirectoryInfo dirInfo in di.GetDirectories())
{
     if (dirInfo.Name == "\u00A0")
     {
          string fullPath = dirInfo.FullName + "\\";
          dirInfo = new DirectoryInfo(fullPath);
     } 

     FileInfo[] fInfos = dirInfo.GetFiles();
     // You can easily iterate through the files
}
  

Reduce flickering from controls in C#.Net

Flickering effect can be reduced by double buffering the controls. The below code will help you in reducing the flickering effect from any type of control.

public static class ControlExtensions
    {
        public static void SetDoubleBuffered(System.Windows.Forms.Control c)
        {
            if (System.Windows.Forms.SystemInformation.TerminalServerSession)
                return;

            System.Reflection.PropertyInfo aProp =
                  typeof(System.Windows.Forms.Control).GetProperty(
                        "DoubleBuffered",
                        System.Reflection.BindingFlags.NonPublic |
                        System.Reflection.BindingFlags.Instance);

            aProp.SetValue(c, true, null);
        }
    }

Whether it is a panel, or a Tab control, you can call the above defined function:

ControlExtensions.SetDoubleBuffered(MenuPanel);
ControlExtensions.SetDoubleBuffered(settingsTabControl);

Friday, 25 September 2015

Copy, Move and Delete Files and Folders in C#:


Below are the commonly used methods (using “System.IO” namespace):

For deleting:
 File:          File.Delete(“Path to the file”);
 Folder:     Directory.Delete(“Path to the folder”);

The above methods will delete the file \ folder permanently. User cannot restore them back.

For Copying:
File:          File. Copy(“source path” , “destination path”);
 Folder:     You will have to recursively traverse through the source directory and create directory and copy files.

For Moving:
File:          File.Move(“source path” , “destination path”);
 Folder:     Directory.Move(“ source path” , “destination path”);

The above methods wouldn’t display the error dialogs, in case of any error OR the progress dialog to show the progress.

"Microsoft.VisualBasic.FileIO"namespace provides a class “FileSystem” that shows all the dialog such as Error dialog, Progress dialog etc. and it’s very simple to use. 



For deleting:
 File:          FileSystem.DeleteFile(“Path to the file”, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);

 Folder:     FileSystem.DeleteDirectory(“Path to the file”, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);

Here  3rd Argument:  RecycleOption also provide 2 options:
  • Delete to Recycle Bin.
  • Delete permanently.


For Copying:
File:          FileSystem.CopyFile(“ source path” , “destination path”, UIOption.OnlyErrorDialogs);
Folder:     FileSystem.CopyDirectory(sourceFile, destFile, UIOption.OnlyErrorDialogs);

For Moving:
File:          FileSystem.MoveFile(sourcePath, targetPath, UIOption.OnlyErrorDialogs);
Folder:     FileSystem.MoveDirectory(sourcePath, targetPath, UIOption.OnlyErrorDialogs);


Argument UIOption passed above provide 2 options:
  • To show only error dialogs
  •  To show all dialogs.

Wednesday, 29 July 2015

Get the size of files having long path in C#.Net 3.5

Get the size of files having long path in  C#.Net 3.5


Suppose you want to get the size of all the files present in a directory. You would do something like this:

Method 1:

DirectoryInfo di = new DirectoryInfo(path); // path: It’s the path to the directory
FileInfo[] fInfos = di.GetFiles();
long fileSize = 0;
foreach (FileInfo fi in fInfos)
{     
fileSize = fi.Length;                   
}

But if you have files having full path (including directory name) more than 260 characters, “fi.Length” will throw FileNotFoundException. Even if the file exists, you will get this error, as shown in the screenshot below:


To fix this issue, include the following namespace:
using System.Reflection;
And use the below code:
public const int MAX_PATH = 260;
public const int MAX_ALTERNATE = 14;

[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
};

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WIN32_FIND_DATA
{
public FileAttributes dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)]
public string cAlternate;
}

[DllImport("kernel32", CharSet = CharSet.Unicode)]public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

Define the below function that will return the size of files having long path:

private long getFileSize(string fileName, out WIN32_FIND_DATA findData)
        {
            long size = 0;
            IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
            IntPtr findHandle;

            findHandle = FindFirstFile(@"\\?\" + fileName , out findData);
            if (findHandle != INVALID_HANDLE_VALUE)
            {
                if ((findData.dwFileAttributes & FileAttributes.Directory) == 0)
                {
                    size = (long)findData.nFileSizeLow + (long)findData.nFileSizeHigh * 4294967296;
                }
            }
            return size;
        }

Update the Method 1 with the below code:



DirectoryInfo di = new DirectoryInfo(path); // path: It’s the path to the directory
FileInfo[] fInfos = di.GetFiles();
long fileSize = 0;
if (fi.Exists)
{
fileSize = fi.Length;
}
else
{
// This will handle long paths
FieldInfo fld = typeof(FileSystemInfo).GetField( "FullPath",  BindingFlags.Instance |
BindingFlags.NonPublic);
string fullPath = fld.GetValue(fi).ToString();
WIN32_FIND_DATA findData;
fileSize = getFileSize(fullPath, out findData);
}



The function getFileSize outputs “findData” of type WIN32_FIND_DATA. It can be used to get the attribute, created date, last access date and other properties of file having long path as below:

string attribute = findData.dwFileAttributes.ToString();

DateTime CreationTime = DateTime.FromFileTimeUtc((((long)findData.ftCreationTime.dwHighDateTime) << 32) | ((uint)findData.ftCreationTime.dwLowDateTime));

DateTime LastAccessTime = DateTime.FromFileTimeUtc((((long)findData.ftLastAccessTime.dwHighDateTime) << 32) | ((uint)findData.ftLastAccessTime.dwLowDateTime));

DateTime LastWriteTime = DateTime.FromFileTimeUtc((((long)findData.ftLastWriteTime.dwHighDateTime) << 32) | ((uint)findData.ftLastWriteTime.dwLowDateTime)); 

Wednesday, 24 June 2015

Dealing with PathTooLongException while scanning files with long paths in C#

Suppose you have a list of object of class ‘FileInfo’. This class provide properties and instance methods for the creation, copying, deletion, moving, and opening of files, and aids in the creation of FileStream objects.
List<FileInfo> AllFilesInfo;
Let say you have a DataTable 'dt' with columns ‘Name’ and ‘Path’. For all the files in AllFilesInfo object, you want to fill in the name and directory name in dataTable.
foreach (FileInfo fileInfo in AllFilesInfo)
{
       DataRow dr = dt.NewRow();
       dr[0] = fileInfo.Name;
       dr[1] = fileInfo.DirectoryName;
}
The above code works well if you don’t have any file with long path. But as soon as, your code finds any such file, you will encounter the below exception:
PathTooLongException  was caught: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
When you debug and add a watch for fileInfo object, you will find both Directory and DirectoryName are throwing this exception. Now if you expand the ‘Non-public members’ and then the ‘base’ object of ‘fileInfo’, you will see a property named ‘FullPath’ giving the fullpath, which is what you need.


‘FullPath’ is a protected property of class ‘FileSystemInfo’, which is a base class for both ‘FileInfo’ and ‘DirectoryInfo’. You can access the path as follows:
DataRow dr = dt.NewRow();
dr[0] = fileInfo.Name;
try
{
    dr[1] = fileInfo.DirectoryName;
}
catch (PathTooLongException ex)
{
    FieldInfo fld = typeof(FileSystemInfo).GetField(
                                                    "FullPath",
                                                     BindingFlags.Instance |
                                                     BindingFlags.NonPublic);
    string fullPath = fld.GetValue(fileInfo).ToString();
    fullPath = fullPath.Remove(fullPath.IndexOf(fileInfo.Name)); // Removing file name from the fullpath
    dr[1] = fullPath;
}
By using the above code, you can access the fullPath of files with long names.

Tuesday, 16 June 2015

Determining the owner of a file\folder in C#.Net and handling with general exceptions

Determining the owner of a file\folder in C#.Net

In order to determine the owner of a file/folder, you would need to use the classes from the following namespaces:
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

Put the following lines in a function and it will give you the owner name:
FileSecurity fileSecurity = File.GetAccessControl(path);
IdentityReference sid = fileSecurity.GetOwner(typeof(SecurityIdentifier));
NTAccount ntAccount = sid.Translate(typeof(NTAccount)) as NTAccount;
string owner = ntAccount.Value;

Here ‘path’ is the location of the file/folder. 

General Exceptions:

1.  UnauthorizedAccessException: If you don’t have access to the file, you will get this exception.

2.  IdentityNotMappedException: “Some or all identity references could not be translated”


Whenever any user is created on the machine, a unique security ID is assigned. This SID can be converted to Domain Name\Username format by translating the SID to NTAccount type, but if that user is removed from the machine then only SID exists. Now if you try to convert the SID to NTAccount type, the above exception would occur. In that case, you may just want to display the SID as follows: 
IdentityReference sid = null;
string owner = null;
try
{
FileSecurity fileSecurity = File.GetAccessControl(path);
sid = fileSecurity.GetOwner(typeof(SecurityIdentifier));
NTAccount ntAccount = sid.Translate(typeof(NTAccount)) as NTAccount;
owner = ntAccount.Value;
}
catch (IdentityNotMappedException ex)
{
if (sid != null)
owner = sid.ToString();
}

Optimizing the code if you want to display the owner name of large number of files :

If you have too many files, getting the SIDs and translating it to DomainName\username(s) would be very time consuming. You can optimize the code by finding the SIDs for all the files and once you have all the SIDs, translate them to DomainName\username.

So, for example: If you have 10000 files, determine the SID for all the 10000 files. Let say you get 7 SIDs. Now translate only those 7 SIDs to DomainName\username format. It will definitely speed up the process.