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));