Sergey Shishkin

on agile software development

Archive for January 2007

How to get the ‘size on disk’?

“A cluster is the smallest logical amount of disk space that can be allocated to hold a file” — Wikipedia. It means that if you have a 100 bytes text file it will waste at least one cluster (usually 4096 bytes), but always a whole number of clusters. On the other hand, if you have applied the file system compression to the 100KB text file it might likely use only a half of its actual size. So, if you see the file’s properties in the Windows Explorer there are two sizes: Size and Size on disk.

Managed .NET API provides only the size of the file’s content (FileInfo.Length property). Although the size the file occupy on the disk is more useful in some cases. But how one can get it?

There are two Win32 functions: GetCompressedFileSize and GetDiskFreeSpace. The former will return the right size only if the file is compressed. Elsewise you need to use the latter to get the cluster size (it returns the number of sectors per cluster and the number of bytes per sector) and manually calculate the result. Here is the code:

public static long GetSizeOnDisk (string fileName)

{

  FileInfo file = newFileInfo(fileName);

  if ((file.Attributes & FileAttributes.Compressed) > 0)

  {

    return GetCompressedFileSize(file.FullName);

  }

  else

  {

    long clusterSize = GetDiskClusterSize(

      file.Directory.Root.FullName);

 

    long sizeOnDisk = file.Length;

    long overload = sizeOnDisk % clusterSize;

    if (overload != 0)

    {

      sizeOnDisk += (clusterSize – overload);

    }

    return sizeOnDisk;

  }

}

 

private static long GetCompressedFileSize (string fileName)

{

  uint high;

  uint low;

 

  low = GetCompressedFileSize(fileName, out high);

 

  int error = Marshal.GetLastWin32Error();

  if (high == 0 && low == 0xFFFFFFFF && error != 0)

  {

    thrownewWin32Exception(error);

  }

 

  return (long)(((ulong)high << 32) + low);

}

 

private static int GetDiskClusterSize (string driveName)

{

  uint sectorsPerCluster;

  uint bytesPerSector;

  uint numberOfFreeClusters;

  uint totalNumberOfClusters;

 

  if (GetDiskFreeSpace(driveName,

    out sectorsPerCluster,

    out bytesPerSector,

    out numberOfFreeClusters,

    out totalNumberOfClusters))

  {

    return (int)sectorsPerCluster * (int)bytesPerSector;

  }

  else

  {

    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

    return -1;

  }

}

 

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]

private static extern uint GetCompressedFileSize (

  string lpFileName, outuint lpFileSizeHigh);

 

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]

private static extern bool GetDiskFreeSpace (

  string lpRootPathName,

  outuint lpSectorsPerCluster,

  outuint lpBytesPerSector,

  outuint lpNumberOfFreeClusters,

  outuint lpTotalNumberOfClusters);

Advertisements

Written by Sergey Shishkin

25.01.2007 at 18:14

Posted in Uncategorized

VSTS Unit Testing: DeploymentItemAttribute

What do you expect to be deployed to the test run folder alongside with the required assemblies when you have such a test class?

[TestClass]
public class MyTest
{
	public const string test1 = "test1.txt";
	[TestMethod()]
	[DeploymentItem("test2.txt")]
	[DeploymentItem(test1)]
	public void Test(){}
}

Suppose you have both “test1.txt” and “test2.txt” files sitting together in the test project’s folder. The answer is: only “test2.txt” will be deployed. Regardless of the fact that constant and literal arguments are identical after compilation, VSTS will only analyze your source code and will do it wrong way!

Remember also, that DeploymentItemAttribute is valid only for methods, not classes. So, if you have 20 test methods and 10 deployment items to copy you have to put 200 DeploymentItemAttributes with literal arguments!

It’s brilliant! I just have nothing more to add!

Written by Sergey Shishkin

15.01.2007 at 18:35

Posted in Uncategorized