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.